NSViewControllerRepresentable嵌入SwiftUI列表时不响应事件

问题描述

我有以下代码段:

列表元素:

struct PaletteListRowUIView: View {
    @State var colorPalette: ColorPalette
    var isSelected: Bool
    var onSelect: () -> Void
    
    var body: some View {        
        return vstack(alignment: .leading) {
            HStack {
                Group {
                    if(isSelected) {
                        Ellipse().fill(Color.white)
                    } else {
                        Ellipse().stroke(Color.white)
                    }
                }.contentShape(Ellipse())
                .aspectRatio(1,contentMode: .fit).frame(width: 10,height: 10)
                .gesture(TapGesture().onEnded(onSelect))

                RenameableText(text: $colorPalette.name,onFinished: {/* saves to core data */})
            }
        }
    }
}

列表本身:

struct PaletteListUIView: View {
    @EnvironmentObject var colorPaletteManager: ColorPaletteManager
    
    var body: some View {
        List {
            ForEach(Array(self.colorPaletteManager.colorPalettes.enumerated()),id: \.element.id) { idx,palette in
                PaletteListRowUIView(
                    colorPalette: palette,isSelected: self.colorPaletteManager.isSelected(index: idx),onSelect: { self.colorPaletteManager.selectPalette(at: idx) })
            }.frame(height: 50)

        }.frame(width: 256)
    }
}

还有一个包装好的NSTextField:

class RenameableTextController: NSViewController {

    @Binding var text: String
    let onFinished: () -> Void
    var originalValue: String?
    var isInEditMode: Bool = false
    private var monitors: [Any?] = []
    private var tapper: NSClickGestureRecognizer?

    init(text: Binding<String>,onFinished: @escaping () -> Void) {
        self._text = text
        self.onFinished = onFinished
        super.init(nibName: nil,bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func loadView() {
        let textField = NSTextField()
        textField.cell = CustomTextFieldCell()
        textField.stringValue = text
        textField.isEditable = false
        textField.isBordered = false
        textField.bezelStyle = .roundedBezel
        textField.backgroundColor = .clear
        self.view = textField
    }
    
    override func viewWilldisappear() {
        self.makeTextFieldInEditable()
    }
    
    override func mouseUp(with event: NSEvent) {
        print("double")
        if(event.clickCount == 2) {
            self.makeTextFieldEditable()
        }
    }
    
    func makeTextFieldEditable() {
        self.isInEditMode = true
        let textField = self.view as! NSTextField
        self.originalValue = textField.stringValue
        textField.delegate = self
        textField.isEditable = true
        textField.isBordered = true

        self.view.window?.makeFirstResponder(textField)
        textField.currentEditor()?.selectedRange = .init(location: textField.stringValue.count,length: 0)
        monitorForEvents()
    }
    
    func makeTextFieldInEditable() {
        let textField = self.view as! NSTextField
        textField.resignFirstResponder()
        if(isInEditMode) {   // This line is necessary because otherwise a depencency cycle is created in SwiftUI
            self.view.window?.makeFirstResponder(nil)
        }
        textField.isEditable = false
        textField.isBordered = false
        removeMonitors()
        self.isInEditMode = false
    }
    
    override func cancelOperation(_ sender: Any?) {
        let textField = self.view as! NSTextField
        textField.stringValue = originalValue!
        makeTextFieldInEditable()
    }
}

extension RenameableTextController: NSTextFieldDelegate {

    func controlTextDidChange(_ obj: Notification) {
        if let textField = obj.object as? NSTextField {
            self.text = textField.stringValue
        }
    }

    func controlTextDidEndEditing(_ obj: Notification) {
        makeTextFieldInEditable()
        onFinished()
    }
}

extension RenameableTextController {
    
    func monitorForEvents() {
        let localMonitor = NSEvent.addLocalMonitorForEvents(matching: [.leftMouseUp]) { event in
            self.makeTextFieldInEditable()
            return event
        }
        monitors.append(localMonitor)
    }
    
    func removeMonitors() {
        monitors.forEach { monitor in
            if let monitor = monitor {
                NSEvent.removeMonitor(monitor)
            }
        }
        monitors = []
    }
}

struct RenameableText: NSViewControllerRepresentable {

    @Binding var text: String
    let onFinished: () -> Void

    func makeNSViewController(
        context: NSViewControllerRepresentableContext<RenameableText>
    ) -> RenameableTextController {print("called")
        return RenameableTextController(text: $text,onFinished: onFinished)
    }

    func updateNSViewController(
        _ nsViewController: RenameableTextController,context: NSViewControllerRepresentableContext<RenameableText>
    ) {
    }
}

我使用RenameableText以便通过双击在列表中重命名该条目。 但是仅当您将List替换为vstack时,此功能才能正常工作。一旦我使用List SwiftUI View,NSViewControllerRepresentable就不再收到mouseUp事件。有趣的是,包含Group的{​​{1}}视图仍在响应TapGesture()。

有人经历过类似的行为并且知道如何解决这个问题,或者这是应该报告的错误吗?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)