问题
I have the following code snippets:
The list element:
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 */})
}
}
}
}
The list itself:
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)
}
}
And a wrapped 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>
) {
}
}
I use the RenameableText
in order to allow renaming an entry in the list by double clicking it. But this works only like intended if you replace the List
with a VStack
. As soon as I use the List
SwiftUI View the NSViewControllerRepresentable
does not receive the mouseUp
event any more. The interesting part is, that Group
View containing the Ellipse
is still responding to the TapGesture().
Has anyone experienced similar behaviour and knows how to solve this or is this a bug that should be reported?
来源:https://stackoverflow.com/questions/63982519/nsviewcontrollerrepresentable-not-responding-to-events-when-embedded-into-swiftu