我有以下可可形式:
struct Canvas: PreviewProvider {
static var previews: some View {
VStack {
HStack(alignment: .firstTextBaseline) {
Text("Endpoint:")
TextField("https://localhost:8080/api", text: .constant(""))
}
Divider()
HStack(alignment: .firstTextBaseline) {
Text("Path:")
TextField("/todos", text: .constant(""))
}
Spacer()
}
.padding()
.previewLayout(.fixed(width: 280, height: 200))
}
}
此面板看起来不错,但我想右对齐“端点:”和“路径:”标签:
所以我应用了自定义的水平对齐方式:
struct Canvas: PreviewProvider {
static var previews: some View {
VStack(alignment: .label) {
HStack(alignment: .firstTextBaseline) {
Text("Endpoint:").alignmentGuide(.label) { $0[.trailing] }
TextField("https://localhost:8080/api", text: .constant(""))
}
Divider()
HStack(alignment: .firstTextBaseline) {
Text("Path:").alignmentGuide(.label) { $0[.trailing] }
TextField("/todos", text: .constant(""))
}
Spacer()
}
.padding()
.previewLayout(.fixed(width: 280, height: 200))
}
}
extension HorizontalAlignment {
private enum Label: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[.leading]
}
}
static let label: HorizontalAlignment = .init(Label.self)
}
结果不是我需要的:
没有文档,请帮助。
然后再>>根据指南对齐每个孩子的大小。这导致您看到的怪异行为。下面,我展示了3种不同的技术,这些技术将使您能够以复杂的顺序获得所需的结果。每个人都有其特定示例之外的应用程序。
对于较长的表格,最后一个(label3()
)将是最可靠的。
struct ContentView: View {
@State var sizes: [String:CGSize] = [:]
var body: some View {
VStack {
HStack(alignment: .firstTextBaseline) {
self.label3("Endpoint:")
TextField("https://localhost:8080/api", text: .constant(""))
}
Divider()
HStack(alignment: .firstTextBaseline) {
self.label3("Path:")
TextField("/todos", text: .constant(""))
}
}
.padding()
.onPreferenceChange(SizePreferenceKey.self) { preferences in
self.sizes = preferences
}
}
func label1(_ text: String) -> some View {
Text(text) // Use a minimum size based on your best guess. Look around and you'll see that many macOS apps actually lay forms out like this because it's simple to implement.
.frame(minWidth: 100, alignment: .trailing)
}
func label2(_ text: String, sizer: String = "Endpoint:") -> some View {
ZStack(alignment: .trailing) { // Use dummy content for sizing based on the largest expected item. This can be great when laying out icons and you know ahead of time which will be the biggest.
Text(sizer).opacity(0.0)
Text(text)
}
}
func label3(_ text: String) -> some View {
Text(text) // Use preferences and save the size of each label
.background(
GeometryReader { proxy in
Color.clear
.preference(key: SizePreferenceKey.self, value: [text : proxy.size])
}
)
.frame(minWidth: self.sizes.values.map { $0.width }.max() ?? 0.0, alignment: .trailing)
}
}
struct SizePreferenceKey: PreferenceKey {
typealias Value = [String:CGSize]
static var defaultValue: Value = [:]
static func reduce(value: inout Value, nextValue: () -> Value) {
let next = nextValue()
for (k, v) in next {
value[k] = v
}
}
}
这里是label2
或label3
的结果的屏幕截图。