当新的 NSTextView 成为 FirstResponder (MacOS) 时如何移动光标?

Posted

技术标签:

【中文标题】当新的 NSTextView 成为 FirstResponder (MacOS) 时如何移动光标?【英文标题】:How to move cursor when new NSTextView becomes FirstResponder (MacOS)? 【发布时间】:2020-06-07 18:40:50 【问题描述】:

每次用户按下回车键时,都会创建一个新的 NSTextView。这工作正常,新的 NSTextView 成为第一响应者,这也是我的目标。但是,尽管它是第一响应者,但光标并没有移动到新的 NSTextView 中。

下面是我的代码:

 func makeNSView(context: NSViewRepresentableContext<TextView>) -> TextView.NSViewType 
        let textView = NSTextView()
        textView.textContainer?.lineFragmentPadding = 10
        textView.textContainerInset = .zero
        textView.font = NSFont.systemFont(ofSize: 12)
        textView.becomeFirstResponder()
        //NSApp.activate(ignoringOtherApps: true) ----> doesn't make difference but is supposed to help switch cursor
        print("\(textView) is first responder") //proof that the first responder is shifting, but the cursor does not move with it for some reason
        return textView
    

我已尝试按照其他答案的建议使用此行:

NSApp.activate(ignoringOtherApps: true)

但是,它没有任何区别。我也尝试在选定范围内插入文本,但它不会在显示的任何 NSTextView 中插入文本,无论它们是否是第一响应者。

有哪些方法可以将光标移动到不同的 NSTextView(最好是第一响应者)?

如果需要更多信息,请告诉我。

【问题讨论】:

当您要求它成为第一响应者时,您的视图不是视图层次结构的一部分。将文本视图添加到窗口后成为第一响应者 【参考方案1】:

通过这样做,我能够获得所需的行为:

  func makeNSView(context: NSViewRepresentableContext<TextView>) -> TextView.NSViewType 
        let textView = NSTextView()
        textView.textContainer?.lineFragmentPadding = 10
        textView.textContainerInset = .zero
        textView.font = NSFont.systemFont(ofSize: 12)
        DispatchQueue.main.async textView.window?.makeFirstResponder(textView)
            textView.setSelectedRange(NSMakeRange(0, 0)) 

        print("\(textView) is first responder") //proof that the first responder is shifting, but the cursor does not for some reason
        return textView
    

答案最初来自这里:How to make first responder in SwiftUI macOS

感谢大家的帮助!

【讨论】:

【参考方案2】:

正如 Warren Burton 在评论中提到的那样,您尚未将新视图添加到窗口的视图层次结构中,而您正试图使其成为第一响应者。那是行不通的。

此外,调用becomeFirstResponder() 确实不会使接收者成为第一响应者。 (这与 UIKit 不同。)事实上,你永远不应该在 macOS 上调用becomeFirstResponder()(除非在覆盖中转发到超类)。 documentation 明确指出这一点:

使用 NSWindow makeFirstResponder(_:) 方法,而不是这个方法,使对象成为第一响应者。 切勿直接调用此方法。

(添加了重点。)becomeFirstResponder() 被 Cocoa 调用以通知视图(或其他响应者)它已成为第一响应者。它不会导致更改,它会通知更改。

因此,一旦视图被添加到窗口的视图层次结构中,调用textView.window.makeFirstResponder(textView)

【讨论】:

您好,感谢您的回答!我添加了 textView.window?.makeFirstResponder(textView) 来代替 textView.becomeFirstResponder()。但是,结果是一样的;光标不动。如何将视图显式添加到窗口的视图层次结构中?我可能错过了那一步。 通常,您将其添加为已经是窗口视图层次结构一部分的视图的子视图。如果新的文本视图出现在屏幕上,您必须在某处执行此操作。 嗯,上面的代码实际上是在 NSTextView 的包装器中,所以我可以在 SwiftUI 中使用它。所以我猜测视图被添加到幕后的窗口中。你知道 SwiftUI 中有什么函数会显式地向窗口的层次结构添加一个新视图吗?

以上是关于当新的 NSTextView 成为 FirstResponder (MacOS) 时如何移动光标?的主要内容,如果未能解决你的问题,请参考以下文章

当新的 USB 连接/分离时,wifi 的 adb 连接被终止

当新的 API 出现时,构建一个向后兼容的 OS X 应用程序?

当新的应用实例启动时,如何将数据传递给已经运行的应用实例

swift—UITextField与 NSTextView 的键盘事

当新页面被推入颤动时如何隐藏本机Tabbar?

NSTextView 提取原始文本内容