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 对齐
- 动态交换
TextField 和Picker
- 自定义
Popover 链接到TextField
所有这些都以某种方式看起来“陌生”。而且由于NSViewRepresentable 并没有那么糟糕,这就是我所决定的。将来它可能会对其他人有所帮助,因此即使您想要一个不依赖于 NSViewRepresentable 的实现,我也将我的实现放在这里。