如何在 AppKit 上的自定义 SwiftUI 表单中右对齐项目标签?

2024-04-04

我有以下可可形式:

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)
}

然而结果不是我需要的:

没有文档,请帮忙。


我不相信对齐指南在当前的实施中会起作用。和他们玩了一下后,他们似乎根据容器的给定大小来调整孩子的大小and then根据指南调整每个孩子的位置。这会导致您看到的奇怪行为。

下面我按照复杂程度展示了 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
        }
    }
}

Here's a screenshot of the results with label2 or label3. The two labels are aligned

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 AppKit 上的自定义 SwiftUI 表单中右对齐项目标签? 的相关文章

随机推荐