【问题标题】:Customize the SwiftUI Form label layout for MacOS为 MacOS 自定义 SwiftUI 表单标签布局
【发布时间】: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 上,如下所示

标签: macos forms swiftui


【解决方案1】:

您希望 .readSize 从您的 Button 标签中获取, 并摆脱 .frame,然后它就可以工作了:)

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)
            
            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)
                    }
                }
                .readSize { textSize in
                    commonSize = textSize
                }
                
            }
            //        .frame(width: commonSize.width, height: commonSize.height)
            .alignmentGuide(.leading, computeValue: { d in (d.width - commonSize.width) })
            .background(.black)
        }
        .padding()
    }
}

extension View {
  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)     {}
}

【讨论】:

猜你喜欢
  • 2022-06-13
  • 1970-01-01
  • 1970-01-01
  • 2017-04-14
  • 2022-11-28
  • 1970-01-01
  • 2019-08-07
  • 1970-01-01
  • 2020-02-23
相关资源
最近更新 更多