【问题标题】:How to get a ComboBox with swiftUI如何使用 swiftUI 获取 ComboBox
【发布时间】:2021-06-13 09:02:32
【问题描述】:

有没有办法在 SwiftUI 中创建 NSComboBox? 当我使用Picker时,在MacOS下会创建一个NSPopUpButton

我试过了:

var body: some View {
    Picker("Test:", selection: $selected) {
        ForEach(1..<10) { i in
            Text(String(i))
        }
    }
    .padding()
}

但这会让我得到这个:

但我需要一个组合框,我可以在其中输入文本以及从列表中选择。

这在 SwiftUI 中是否可能没有NSComboBox 集成为NSViewRepresentable

我已经检查了以下问题:SwiftUI Custom Picker / ComboBox 但它是关于自定义选择器而不是关于 NSComboBox。

【问题讨论】:

标签: macos swiftui combobox


【解决方案1】:

VDKComboBox

使用NSViewRepresentable 并没有我预期的那么糟糕。这是 SwiftUI 的 NSComboBox 的完整实现:​​

import Foundation
import SwiftUI


struct VDKComboBox: NSViewRepresentable
{
    // The items that will show up in the pop-up menu:
    var items: [String]
    
    // The property on our parent view that gets synced to the current stringValue of the NSComboBox, whether the user typed it in or selected it from the list:
    @Binding var text: String
    

    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
    
    
    func makeNSView(context: Context) -> NSComboBox
    {
        let comboBox = NSComboBox()
        comboBox.usesDataSource = false
        comboBox.completes = false
        comboBox.delegate = context.coordinator
        comboBox.intercellSpacing = NSSize(width: 0.0, height: 10.0)            // Matches the look and feel of Big Sur onwards.
        return comboBox
    }
    

    func updateNSView(_ nsView: NSComboBox, context: Context)
    {
        nsView.removeAllItems()
        nsView.addItems(withObjectValues: items)
        
        // ComboBox doesn't automatically select the item matching its text; we must do that manually. But we need the delegate to ignore that selection-change or we'll get a "state modified during view update; will cause undefined behavior" warning.
        context.coordinator.ignoreSelectionChanges = true
        nsView.stringValue = text
        nsView.selectItem(withObjectValue: text)
        context.coordinator.ignoreSelectionChanges = false
    }
}



// MARK: - Coordinator


extension VDKComboBox
{
    class Coordinator: NSObject, NSComboBoxDelegate
    {
        var parent: VDKComboBox
        var ignoreSelectionChanges: Bool = false
        
        init(_ parent: VDKComboBox) {
            self.parent = parent
        }
        

        func comboBoxSelectionDidChange(_ notification: Notification)
        {
            if !ignoreSelectionChanges,
               let box: NSComboBox = notification.object as? NSComboBox,
               let newStringValue: String = box.objectValueOfSelectedItem as? String
            {
                parent.text = newStringValue
            }
        }
        
        
        func controlTextDidEndEditing(_ obj: Notification)
        {
            if let textField = obj.object as? NSTextField
            {
                parent.text = textField.stringValue
            }
        }
    }
}

使用它:

struct MyView: View
{   
    @State var comboBoxText: String = ""

    var body: some View
    {
         VDKComboBox(items: ["one", "two", "three"], text: $comboBoxText)
             .padding()
    }
}

当然,您将动态填充项目列表;硬编码的字符串数组只是一个简单的例子。

替代方案

我确实意识到你明确排除了NSViewRepresentable,但我尝试了很多其他方法来模仿NSComboBox,包括:

  • TextField 顶部的 SwiftUI Button.trailing 对齐
  • 动态交换TextFieldPicker
  • 自定义Popover 链接到TextField

所有这些都以某种方式看起来“陌生”。而且由于NSViewRepresentable 并没有那么糟糕,这就是我所决定的。将来它可能会对其他人有所帮助,因此即使您想要一个不依赖于 NSViewRepresentable 的实现,我也将我的实现放在这里。

【讨论】:

  • 感谢您的回答,但我希望没有 NSViewRepresentable 也可以,因为该应用程序也应该在 iOS 设备上运行,我想避免所有 #ifdef ;-)
  • 啊。 NSComboBox 是一个 Mac 成语;我认为没有该控件的官方 iOS 版本。有一些第三方尝试,但大多数看起来都很笨拙。尽管如此,#ifdef 应该仅限于组合框文件,因此痛苦相对较小。每次使用自定义组合框时都不需要#ifdef。
猜你喜欢
  • 2021-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多