SwiftUI 里的 View 树——第三部分:嵌套视图偏好与锚点使用
2023-10-05 08:57:49
SwiftUI-Lab:探究 View 树 part-3 嵌套视图
嵌套视图偏好和锚点入门
在 SwiftUI 中,嵌套视图是一种常见的视图结构,它允许你将多个视图组合在一起形成一个更复杂的视图。嵌套视图可以通过父子关系或通过使用 SwiftUI 的 VStack
、HStack
和 ZStack
等布局视图来实现。
当涉及到嵌套视图时,偏好和锚点是非常有用的工具。偏好允许你从子视图传递信息到父视图,而锚点允许你将子视图定位到父视图内的特定位置。这使得你可以在构建复杂的 UI 布局时具有更大的灵活性。
示例:构建一个带边框的文本字段
为了更好地理解嵌套视图偏好和锚点的用法,让我们构建一个带边框的文本字段。这个文本字段将由一个 TextField
和一个 RoundedRectangle
组成,RoundedRectangle
将作为文本字段的边框。
首先,创建一个新的 SwiftUI 项目并打开 ContentView.swift
文件。
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
// 创建一个文本字段
TextField("Enter your name", text: /*@START_MENU_TOKEN@*/"Placeholder"/*@END_MENU_TOKEN@*/)
.padding()
.background(Color.white)
.cornerRadius(5)
// 创建一个圆角矩形边框
RoundedRectangle(cornerRadius: 5)
.stroke(Color.black, lineWidth: 2)
}
.padding()
}
}
@main
struct SwiftUI_LabApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
运行这段代码,你将看到一个带有圆角边框的文本字段。然而,边框的位置并不是我们想要的,它应该紧贴文本字段。
使用偏好和锚点调整边框位置
为了将边框紧贴文本字段,我们将使用 frame(maxWidth:, maxHeight:)
修饰符和 anchorPreference
函数。
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
// 创建一个文本字段
TextField("Enter your name", text: /*@START_MENU_TOKEN@*/"Placeholder"/*@END_MENU_TOKEN@*/)
.padding()
.background(Color.white)
.cornerRadius(5)
// 使用 frame 修饰符和 anchorPreference 函数来调整边框位置
RoundedRectangle(cornerRadius: 5)
.stroke(Color.black, lineWidth: 2)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.anchorPreference(key: FramePreferenceKey.self, value: .bounds, transform: { anchor in
return anchor
})
}
.padding()
}
}
struct FramePreferenceKey: PreferenceKey {
static var defaultValue: Anchor<CGRect>?
static func reduce(value: inout Anchor<CGRect>?, nextValue: Anchor<CGRect>?) {
value = nextValue
}
}
@main
struct SwiftUI_LabApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
在上面的代码中,我们首先使用 frame(maxWidth:, maxHeight:)
修饰符来设置边框的大小。然后,我们使用 anchorPreference
函数来创建一个键为 FramePreferenceKey
的偏好。这个偏好将把文本字段的边界作为值传递给父视图。
在父视图中,我们使用 onPreferenceChange
修饰符来监听 FramePreferenceKey
的值的变化。当这个值发生变化时,我们将把边框的位置更新为文本字段的边界。
import SwiftUI
struct ContentView: View {
@State private var frame: Anchor<CGRect>?
var body: some View {
VStack {
// 创建一个文本字段
TextField("Enter your name", text: /*@START_MENU_TOKEN@*/"Placeholder"/*@END_MENU_TOKEN@*/)
.padding()
.background(Color.white)
.cornerRadius(5)
// 使用 frame 修饰符和 anchorPreference 函数来调整边框位置
RoundedRectangle(cornerRadius: 5)
.stroke(Color.black, lineWidth: 2)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.anchorPreference(key: FramePreferenceKey.self, value: .bounds, transform: { anchor in
return anchor
})
// 使用 onPreferenceChange 修饰符来监听 FramePreferenceKey 的值的变化
.onPreferenceChange(FramePreferenceKey.self) { frame in
self.frame = frame
}
}
.padding()
// 使用 frame 来更新边框的位置
.overlay(
GeometryReader { geometry in
RoundedRectangle(cornerRadius: 5)
.stroke(Color.black, lineWidth: 2)
.frame(width: geometry.size.width, height: geometry.size.height)
.position(x: frame?.x ?? 0, y: frame?.y ?? 0)
}
)
}
}
struct FramePreferenceKey: PreferenceKey {
static var defaultValue: Anchor<CGRect>?
static func reduce(value: inout Anchor<CGRect>?, nextValue: Anchor<CGRect>?) {
value = nextValue
}
}
@main
struct SwiftUI_LabApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
现在,边框将紧贴文本字段,无论文本字段的大小如何变化。
额外的锚点用法
除了我们在上面示例中使用的 position
锚点之外,SwiftUI 还提供了许多其他的锚点。这些锚点可以用于将子视图定位到父视图内的不同位置。
以下是几个常用的锚点:
.topLeading
: 将子视图定位到父视图的左上角.top
: 将子视图定位到父视图的顶部中央.topTrailing
: 将子视图定位到父视图的右上角.leading
: 将子视图定位到父视图的左中央.center
: 将子视图定位到父视图的中央.trailing
: 将子视图定位到父视图的右中央.bottomLeading
: 将子视图定位到父视图的左下角.bottom
: 将子视图定位到父视图的底部中央.bottomTrailing
: 将子视图定位到父视图的右下角
这些锚点可以单独使用,也可以组合使用。例如,你可以使用 .topLeading
锚点将子视图定位到父视图的左上角,然后使用 .trailing
锚点将子视图的右边缘与父视图的右边缘对齐。
总结
在这篇文章中,我们学习了如何在 SwiftUI 中使用嵌套视图偏好和锚点来构建复杂的 UI 布局。我们还了解了一些额外的锚点用法。通过这些知识,你可以构建出更灵活、更美观的 SwiftUI 界面。
我希望这篇文章对你有帮助。如果你有任何问题或建议,请随时留言。