使用 SegmentedPickerStyle 为 SwiftUI Picker 设置段宽度相等

Set segment equal width for SwiftUI Picker with SegmentedPickerStyle

使用 SegmentedPickerStyle 样式 Picker 可以使控件看起来像 UISegmentedControl。但我想知道如何在选择器中调整段宽度。例如,图像中的选择器具有不同的文本宽度。

有没有办法让 SwiftUI 中的段宽度相同?

    Picker(selection: $store.utility.saliencyType, label: EmptyView()) {
        ForEach(Store.Utility.SaliencyType.allCases, id: \.self) { saliencyType in
            Text(saliencyType.text)
                .tag(saliencyType)
        }
    }.pickerStyle(SegmentedPickerStyle())

这是默认的 macOS NSSegmetedControl 行为

@property NSSegmentDistribution segmentDistribution API_AVAILABLE(macos(10.13)); 
// Defaults to NSSegmentDistributionFill on 10.13, older systems will continue to behave similarly to NSSegmentDistributionFit

更新: 这是解决方法,基于在 运行 时间视图层次结构中找到 NSSegmentedControl

免责声明:实际上它是安全的,即。 运行 时间内没有崩溃,但可以在将来停止工作以恢复默认行为。

因此,我们的想法是通过可表示形式将 NSView 注入到上方的视图层次结构 (!!) Picker,如

Picker(selection: $store.utility.saliencyType, label: EmptyView()) {
        ForEach(Store.Utility.SaliencyType.allCases, id: \.self) { saliencyType in
            Text(saliencyType.text)
                .tag(saliencyType)
        }
    }
    .overlay(NSPickerConfigurator {                // << here !!
        [=11=].segmentDistribution = .fillEqually // change style !!
    })
    .pickerStyle(SegmentedPickerStyle())

和配置器本身

struct NSPickerConfigurator: NSViewRepresentable {
    var configure: (NSSegmentedControl) -> Void

    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            if let holder = view.superview?.superview {
                let subviews = holder.subviews
                if let nsSegmented = subviews.first?.subviews.first as? NSSegmentedControl {
                    self.configure(nsSegmented)
                }
            }
        }
        return view
    }

    func updateNSView(_ nsView: NSView, context: Context) {
    }
}

啊,深入到 AppKit 方法。

确实很聪明。 然而,这对我不起作用,Monteray 12.3

使用 Xcode 的可视化调试器进一步调试,我可以在视图层次结构中看到 NSPickerConfigurator class,但没有 NSSegmetedControl。

看起来苹果正在从层次结构中清除 NSViews。

是时候考虑纯粹的 swiftui 了。

...For examle, the picker in the image has a different width for text.

如果您来到这里寻求 iOS SwiftUI SegmentedPickerStyle 解决方案...我发现 iOS SwiftUI .pickerStyle(SegmentedPickerStyle()) 将符合全局 UISegmentedControl.appearance() 设置,所以我使用以下方法成功分配每个段的宽度:

UISegmentedControl.appearance().apportionsSegmentWidthsByContent = true

例如,如果您想在您的应用中支持动态类型字体,这将特别有用,否则可能会导致名称较长的段爆裂并被截断。 [旁白:我还使用这个技巧来更改 SwiftUI 分段选择器的字体大小!参见