SwiftUI 布局:如何自定义 AlignmentGuides
SwiftUI 为我们提供了视图不同边缘的对齐指南(.leading
、trailing
、top
等)以及.center
和两个基线选项来帮助文本对齐。然而,当您处理在不同视图之间分割的视图时,这些方法都不能很好地工作——如果您必须使在用户界面完全不同的两个视图部分对齐。
为了解决这个问题,SwiftUI允许我们创建自定义的对齐辅助线,并在整个UI的视图中使用这些辅助线。在这些视图之前或之后发生什么并不重要,它们仍然会排成一条线。
例如,下面的布局在左侧显示我的Twitter帐户名和我的个人资料图片,右侧显示“Full name:”加上“Wei Xian”的大号字体:
struct ContentView: View {
var body: some View {
HStack {
VStack {
Text("@twostraws")
Image("wei-xian")
.resizable()
.frame(width: 64, height: 64)
}
VStack {
Text("Full name:")
Text("WEI XIAN")
.font(.largeTitle)
}
}
}
}
如果你想让“@twostraws”和“Wei Xian”垂直对齐,你现在就很难了。水平堆栈内部包含两个垂直堆栈,因此没有内置的方法来获得所需的对齐方式——像HStack(alignment: .top)
这样的方便的方式。
要解决这个问题,我们需要定义一个自定义布局指南。这应该是VerticalAlignment
或HorizontalAlignment
的扩展,并且是符合AlignmentID
协议的自定义类型。
当我说“自定义类型”时,您可能会想到一个结构体,但实际上,将其作为枚举来实现是一个好主意,我将很快解释。
AlignmentID
协议只有一个要求,即一致性类型必须提供一个静态defaultValue(in:)
方法,该方法接受ViewDimensions
对象并返回一个CGFloat
,指定如果视图没有alignmentGuide()
修饰符,该视图应该如何对齐。您将获得视图的现有ViewDimensions
对象,因此您可以选择其中一个作为默认值,也可以使用硬编码值。
让我们写出代码,这样您就可以看到它的样子:
extension VerticalAlignment {
struct MidAccountAndName: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
d[.top]
}
}
static let midAccountAndName = VerticalAlignment(MidAccountAndName.self)
}
您可以看到我在默认情况下使用了.top
视图维度,并且还创建了一个名为midAccountAndName
的静态常量,以使自定义对齐更易于使用。
现在,我提到使用枚举比使用结构体更可取,原因如下:
我们刚刚创建了一个名为MidAccountAndName
的新结构体,这意味着我们可以(如果需要的话)创建该结构体的实例,即使这样做没有任何意义,因为它没有任何功能。如果将struct MidAccountAndName
替换为enum MidAccountAndName
,那么就不能再创建它的一个实例了——它的存在只是为了容纳一些功能。
无论您选择的是枚举还是结构体,其用法都保持不变:将其设置为堆栈的对齐方式,然后使用alignmentGuide()
在要对齐的任何视图上激活它。这只是一个指南:它帮助您沿一条直线对齐视图,但没有说明如何对齐视图。这意味着您仍然需要为alignmentGuide()
提供闭包,该闭包可以根据需要定位视图。
例如,我们可以将Twitter代码更新为use.midAccountAndName
,然后告诉帐户和名称使用其中心位置作为指南。明确地说,这意味着“对齐这两个视图,使它们的中心都位于.midAccountAndName
指南上”。
下面是代码:
HStack(alignment: .midAccountAndName) {
VStack {
Text("@twostraws")
.alignmentGuide(.midAccountAndName) { d in d[VerticalAlignment.center] }
Image("paul-hudson")
.resizable()
.frame(width: 64, height: 64)
}
VStack {
Text("Full name:")
Text("PAUL HUDSON")
.alignmentGuide(.midAccountAndName) { d in d[VerticalAlignment.center] }
.font(.largeTitle)
}
}
示例
这将确保它们是垂直对齐的,不管前面或后面是什么。我建议您尝试在我们的示例前后添加更多的文本视图–SwiftUI将重新定位所有内容,以确保我们对齐的两个视图保持不变。