OSX SwiftUI 集成 NSComboBox 不刷新当前选择
OSX SwiftUI integrating NSComboBox Not refreshing current sélected
SwiftUI Picker 在 OSX 上看起来很糟糕,尤其是在处理长项目列表时
Swiftui Picker on OSX with a long item list
并且由于确实找到了任何解决方案来限制 Osx 上 Picker 显示的项目数量,我决定将 NSComboBox 连接到 SwiftUI
在使用 Observable Comboselection class 实例的 @Published 索引以编程方式修改选择索引之前,一切看起来都很好(参见下面的代码):
- 然后正确调用了 NSViewRepresentable 实例的 updateNSView 函数(打印在日志中可见的消息)
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))")
}
}
请参阅更新和简化的代码,添加了一些工作演示。问题的主要原因是缺少 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)
}
}
SwiftUI Picker 在 OSX 上看起来很糟糕,尤其是在处理长项目列表时
Swiftui Picker on OSX with a long item list
并且由于确实找到了任何解决方案来限制 Osx 上 Picker 显示的项目数量,我决定将 NSComboBox 连接到 SwiftUI
在使用 Observable Comboselection class 实例的 @Published 索引以编程方式修改选择索引之前,一切看起来都很好(参见下面的代码):
- 然后正确调用了 NSViewRepresentable 实例的 updateNSView 函数(打印在日志中可见的消息)
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))")
}
}
请参阅更新和简化的代码,添加了一些工作演示。问题的主要原因是缺少 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)
}
}