【发布时间】:2022-02-04 03:27:17
【问题描述】:
我正在运行一个 macOS 应用程序,其中包含一个带有一些元素的表单。我喜欢使用带有 labels 的元素(例如:TextField 和 Picker)时的默认布局,但是在使用自定义控件复制该布局时遇到了麻烦。具体来说,我想要一个包含文本/标签字段(位于其他标签下)和一个按钮(即使是上面的其他字段)的 HStack。相反,这两个元素都在第二部分之下,而不是两者。
我创建了一个虚拟项目来显示我的问题。这就像表单是一个两列的表,标签被放在第一列,其他任何东西都放在第二列。我确实看到了这个问题,Swiftui Form Label alignment on macOS,但我希望通过下面的示例可能会有更好的解决方案。我尝试将所有内容都放在 VStack 中,但随后标签左对齐,文本框从任何地方开始。我也不想硬编码标签的宽度。
import SwiftUI
struct ContentView: View {
@State var myName:String = "Kyra"
@State var selectedPickerItem: String?
var pickerItems = ["item 1",
"item 2",
"item 3",
"item 4",
"item 5",
"item 6"]
var body: some View {
Form {
TextField("My Name:", text: $myName, prompt: Text("What's your name?"))
.foregroundColor(.white)
.background(.black)
Picker(selection: $selectedPickerItem, label: Text("Pick Something:")) {
Text("No Chosen Item").tag(nil as String?)
ForEach(pickerItems, id: \.self) { item in
Text(item).tag(item as String?)
}
}
.foregroundColor(.white)
.background(.black)
HStack {
Label("Label:", image: "default")
.labelStyle(.titleOnly)
.foregroundColor(.white)
Button(action: {
print("Do something")
}) {
HStack {
Text("Button HERE")
Image(systemName: "chevron.right")
.foregroundColor(.secondary)
.font(.caption)
}
}
}
.frame(minWidth: 0, maxWidth: .infinity)
.background(.black)
}
.padding()
}
}
我基本上希望表格中的第三部分看起来像前两个部分。我添加了黑色背景,所以它更明显。我希望 label: 甚至位于 My Name: 和 Pick Something: 而按钮与文本字段本身和选择器相同以上。
感谢任何可以帮助我提出优雅解决方案的人
更新 1:接受@ChrisR 的评论并尝试使用该样式。我发现 ButtonStyle 没有像 ToggleStyle 示例那样的 width。然后我找到了这个问题Get width of a view using in SwiftUI,并使用Paul B 的答案中的commonSize 来设置对齐方式。它正确设置了我的堆栈的宽度,但无法正确设置对齐方式。
代码:
import SwiftUI
struct ContentView: View {
@State var myName:String = "Kyra"
@State var selectedPickerItem: String?
var pickerItems = ["item 1",
"item 2",
"item 3",
"item 4",
"item 5",
"item 6"]
@State private var commonSize = CGSize()
var body: some View {
Form {
TextField("My Name:", text: $myName, prompt: Text("What's your name?"))
.foregroundColor(.white)
.background(.black)
.readSize { textSize in
commonSize = textSize
}
Picker(selection: $selectedPickerItem, label: Text("Pick Something:")) {
Text("No Chosen Item").tag(nil as String?)
ForEach(pickerItems, id: \.self) { item in
Text(item).tag(item as String?)
}
}
.foregroundColor(.white)
.background(.black)
HStack {
Label("Label:", image: "default")
.labelStyle(.titleOnly)
.foregroundColor(.white)
Button(action: {
print("Do something")
}) {
HStack {
Text("Button HERE")
Image(systemName: "chevron.right")
.foregroundColor(.secondary)
.font(.caption)
}
}
}
.frame(width: commonSize.width, height: commonSize.height)
.alignmentGuide(.leading, computeValue: { d in (d.width - commonSize.width) })
.background(.black)
}
.padding()
}
}
func readSize(onChange: @escaping (CGSize) -> Void) -> some View {
background(
GeometryReader { geometryProxy in
Color.clear
.preference(key: SizePreferenceKey.self, value: geometryProxy.size)
}
)
.onPreferenceChange(SizePreferenceKey.self, perform: onChange)
}
}
private struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {}
}
更新 2:我尝试了 ChrisP 的回答“您希望 .readSize 从您的 Button 标签中删除,并摆脱 .frame”并最终在右列中左对齐:
如果我不设置 commonSize 或使用 0 而不是它会将两个元素移动到第一列:
如果我通过从 HStack 中删除标签来拆分元素,我可以在第一列获得一个,在第二列获得一个,但是它们位于两条不同的行上。
【问题讨论】:
-
这会有所帮助:swiftui-lab.com/custom-styling --- 章节“特别注意事项 #3:表单 (macOS)”
-
看起来棒极了;但是,当我使用 ButtonStyle 时,
self.width不起作用,因为它“没有成员宽度”,所以我不确定我应该如何尝试使用按钮和文本来复制它? -
你几乎成功了。只是 .getSize 在错误的元素,看看我的答案 :)
-
如此接近。大声笑我更新了我的问题。感谢您的帮助
-
它确实有效!将 .readSize 放在 BUTTON 上,而不是 HStack 上,如下所示