SwiftUI Picker在OSX上看起来非常糟糕,尤其是在处理较长的项目列表时
Swiftui Picker on OSX with a long item list
并且由于确实找到了任何方法来限制Picker在Osx上显示的项目数,因此我决定将NSComboBox连接到SwiftUI
在使用Observable Comboselection类实例的@Published索引以编程方式修改选择索引之前,一切看起来都很好:
combo.selectItem(at: selected.index)
combo.selectItem(at: selected.index)
combo.objectValue = combo.objectValueOfSelectedItem
print("populating index change \(selected.index) to Combo : (String(describing: combo.objectValue))")
正确执行,并且打印的日志显示正确的信息
但是NSComboBox文本字段未使用准确的对象值刷新
这里有人解释吗? ;代码有什么问题吗?
这里是所有代码:
import SwiftUI
class ComboSelection : ObservableObject {
@Published var index : Int
init( index: Int ) {
self.index = index
}
func newSelection( newIndex : Int ) {
index = newIndex
}
}
//
// SwiftUI NSComboBox component interface
//
struct SwiftUIComboBox : NSViewRepresentable {
typealias NSViewType = NSComboBox
var content : [String]
var nbLines : Int
var selected : ComboSelection
final class Coordinator : NSObject ,
NSComboBoxDelegate {
var control : SwiftUIComboBox
var selected : ComboSelection
init( _ control: SwiftUIComboBox , selected : ComboSelection ) {
self.selected = selected
self.control = control
}
func comboBoxSelectionDidChange(_ notification: Notification) {
print ("entering coordinator selection did change")
let combo = notification.object as! NSComboBox
selected.newSelection( newIndex: combo.indexOfSelectedItem )
}
}
func makeCoordinator() -> SwiftUIComboBox.Coordinator {
return Coordinator(self, selected:selected)
}
func makeNSView(context: NSViewRepresentableContext<SwiftUIComboBox>) -> NSComboBox {
let returned = NSComboBox()
returned.numberOfVisibleItems = nbLines
returned.hasVerticalScroller = true
returned.usesDataSource = false
returned.delegate = context.coordinator // Important : not forget to define delegate
for key in content{
returned.addItem(withObjectValue: key)
}
return returned
}
func updateNSView(_ combo: NSComboBox, context: NSViewRepresentableContext<SwiftUIComboBox>) {
combo.selectItem(at: selected.index)
combo.objectValue = combo.objectValueOfSelectedItem
print("populating index change \(selected.index) to Combo : \(String(describing: combo.objectValue))")
}
}
答案 0 :(得分:0)
请查看更新并简化的代码,并添加一些有效的演示。问题的主要原因是SwiftUI视图层次结构没有更新,因此为了进行这样的更新,我使用了Binding,该绑定将更改转移到UIViewRepresentable并返回。希望这种方法会有所帮助。
这是演示
下面是一个模块的完整演示代码(只需设置
window.contentView = NSHostingView(rootView:TestComboBox())
在应用程序委托中
struct SwiftUIComboBox : NSViewRepresentable {
typealias NSViewType = NSComboBox
var content : [String]
var nbLines : Int
@Binding var selected : Int
final class Coordinator : NSObject, NSComboBoxDelegate {
var selected : Binding<Int>
init(selected : Binding<Int>) {
self.selected = selected
}
func comboBoxSelectionDidChange(_ notification: Notification) {
print ("entering coordinator selection did change")
if let combo = notification.object as? NSComboBox, selected.wrappedValue != combo.indexOfSelectedItem {
selected.wrappedValue = combo.indexOfSelectedItem
}
}
}
func makeCoordinator() -> SwiftUIComboBox.Coordinator {
return Coordinator(selected: $selected)
}
func makeNSView(context: NSViewRepresentableContext<SwiftUIComboBox>) -> NSComboBox {
let returned = NSComboBox()
returned.numberOfVisibleItems = nbLines
returned.hasVerticalScroller = true
returned.usesDataSource = false
returned.delegate = context.coordinator // Important : not forget to define delegate
for key in content {
returned.addItem(withObjectValue: key)
}
return returned
}
func updateNSView(_ combo: NSComboBox, context: NSViewRepresentableContext<SwiftUIComboBox>) {
if selected != combo.indexOfSelectedItem {
DispatchQueue.main.async {
combo.selectItem(at: self.selected)
print("populating index change \(self.selected) to Combo : \(String(describing: combo.objectValue))")
}
}
}
}
struct TestComboBox: View {
@State var selection = 0
let content = ["Alpha", "Beta", "Gamma", "Delta", "Epselon", "Zetta", "Eta"]
var body: some View {
VStack {
Button(action: {
if self.selection + 1 < self.content.count {
self.selection += 1
} else {
self.selection = 0
}
}) {
Text("Select next")
}
Divider()
SwiftUIComboBox(content: content, nbLines: 3, selected: $selection)
Divider()
Text("Current selection: \(selection), value: \(content[selection])")
}
.frame(width: 300, height: 300)
}
}