⚡️ Advanced SwiftUI Layout System
2023/11/29布局法则:父 view 提供建议的 size
然后子 view 根据建议以及自身特性进行布局。
Frame
minWidth | idealWidth | maxWidth |
---|---|---|
minHeight | idealHeight | maxHeight |
width | height | alignment |
idealWidth
需要搭配 .fixedSize(horizontal: true, vertical: true)
使用。
修改
frame
相当于修改了父 view 建议的size
。
GeometryReader
用于获取父 view 建议的 size
。
struct ContentView: View {
var body: some View {
VStack {
GeometryReader { geo in
Text("width: \(geo.size.width), height: \(geo.sieze.height)")
}
}
}
}
geometryProxy
定义(iOS16):
public struct GeometryProxy {
public var size: CGSize { get } // 父 view 建议的 size。
public subscript<T>(anchor: Anchor<T>) -> T { get } // 获取 .leading, .top 等类似的数据
public var safeAreaInsets: EdgeInsets { get } // 获取安全区域的值
public func frame(in coordinateSpace: CoordinateSpace) -> CGRect // 传入 CoordinateSpace 类型参数[.local, .global, .named("")]
}
强大的 geo.frame(in)
,能够获取到某个 view 相对某个坐标空间的 bounds。
struct ContentView: View {
var body: some View {
VStack {
Spacer()
GeometryReader { geo in
Text("CoordinateSpace: \(geo.frame(in: .named("OutVStack")).minY)")
Text("Global: \(geo.frame(in: .global).minY)")
}
Spacer()
}
.frame(height: 200 )
.border(Color.green)
.coordinateSpace(name: "OutVStack")
}
}
Alignment Guide
Container Alignment
Preference
用于父 view 获取子 view 信息的场景,核心思想有2个:
- *设置 PreferenceKey 和自定义的 PreferenceData ,把子 view 的信息绑定到 PreferenceData上,子 view 通过
.preference(key:, value:)
修饰符进行绑定。* - 父 view 根据 PreferenceKey 获取到所有子 view 的 PreferenceData ,通过
.onPreferenceChange
获取到信息。
🔨 示例代码
// 固定写法 struct NumberPreferenceValue: Equatable { let viewIdx: Int let rect: CGRect } struct NumberPreferenceKey: PreferenceKey { typealias Value = [NumberPreferenceValue] static var defaultValue: Value = [] static func reduce(value: inout Value, nextValue: () -> Value) { value.append(contentsOf: nextValue()) } } // 子 view 上进行绑定 struct NumberView: View { let idx: Int var body: some View { GeometryReader { geo in Text("\(self.idx)") .background(GeometryReader { geo2 in // 获取全局坐标 Color.clear.preference( key: NumberPreferenceKey.self, value: [NumberPreferenceValue(viewIdx: self.idx, rect: geo2.frame(in: .global))] ) }) } } } // 父 view 获取所有子 view 信息 struct ContentView: View { var body: some View { VStack { NumberView(idx: 0) NumberView(idx: 1) } .onPreferenceChange(NumberPreferenceKey.self) { prefs in // 这里获取到所有信息 for pref in prefs { print("viewIdx: \(pref.viewIdx), rect: \(pref.rect)") } } } }
Stacks
HStack / VStack / ZStack
在无其他约束条件下,其特性表现为:尽量满足子 views 的布局要求,并且自身最终的布局 size 取决于子 views 的 size。ZStack
中后面的子 view 覆盖在前面的 view。
Spacer
搭配 Stacks 使用,特性是在某个方向上占据更多空间。参数 minLength
为最小占据空间。
LayoutPriority
布局优先级