AppKit 上的 SwiftUI - 在弹出窗口中将焦点设置为 TextField

Posted

技术标签:

【中文标题】AppKit 上的 SwiftUI - 在弹出窗口中将焦点设置为 TextField【英文标题】:SwiftUI on AppKit - Set Focus to a TextField in a popover 【发布时间】:2021-04-18 19:56:06 【问题描述】:

在下面的 AppKit 示例中,我希望第一个文本字段在弹出框出现时具有(键盘)焦点。 我该怎么做

将初始焦点设置为视图并 如何控制焦点顺序 如何以编程方式设置焦点,例如作为按钮操作
struct ContentView: View 
  @State var showPopup = false
  @State var text = ""
  
  var body: some View 
    VStack
      Text("Hello, world!")
      .padding()
      Button("show Popup")  showPopup = true
    
    .frame(width: 300, height: 300)
    .padding()
    .popover( isPresented: $showPopup,
              arrowEdge: .trailing
    ) 
      VStack
        Text("Popup")
        TextField("enter Text",text:$text )
      
      .padding()
    
  

【问题讨论】:

【参考方案1】:

I needed to do this in one of my projects myself。 首先,我将 NSViewController 子类化,其中视图在视图出现时成为第一响应者(聚焦)。

class FocusedTextFieldViewController: NSViewController, NSTextFieldDelegate 
    @Binding var text: String
    
    init(text: Binding<String>) 
        _text = text
        super.init(nibName: nil, bundle: nil)
    
    
    required init?(coder: NSCoder) 
        fatalError()
    

    var textField: NSTextField!
    
    override func loadView() 
        // Set up the text field.
        textField = NSTextField()
        
        // Set the text field's delegate
        textField.delegate = self
        
        // Set an initial text value.
        textField.stringValue = text
        
        // Set the view to the new text field.
        view = textField
    
    
    override func viewDidAppear() 
        // Make the view the first responder.
        view.window?.makeFirstResponder(view)
    
    
    func controlTextDidChange(_ obj: Notification) 
        // Update the text.
        text = textField.stringValue
    

然后,我使用 NSViewControllerRepresentable 在 SwiftUI 中显示 NSViewController。

struct FocusedTextField: NSViewControllerRepresentable 
    @Binding var text: String
    
    func makeNSViewController(context: Context) -> some FocusedTextFieldViewController 
        return FocusedTextFieldViewController(text: $text)
    
    
    func updateNSViewController(_ nsViewController: NSViewControllerType, context: Context) 
        let textField = nsViewController.textField
        textField?.stringValue = text
    

现在,您可以在 SwiftUI 中使用“FocusedTextField”了。

struct ContentView: View 
    @State private var popoverIsPresented = false
    @State private var text = ""
    var body: some View 
        Button("Show Popover") 
            // Present the popover.
            popoverIsPresented.toggle()
        
        .popover(isPresented: $popoverIsPresented, content: 
            FocusedTextField(text: $text)
        )
    

如果你想取消文本字段的焦点,你可以将它添加到 loadView 函数到 FocusedTextFieldViewController 并使用通知中心再次取消文本字段的焦点。

override func loadView() 
    // Set up the text field.
    textField = NSTextField()
    
    // Set the text field's delegate
    textField.delegate = self
    
    // Set an initial text value.
    textField.stringValue = text
    
    // Set the view to the new text field.
    view = textField
    
    // Set up a notification for unfocusing the text field.
    NotificationCenter.default.addObserver(forName: Notification.Name("UnfocusTextField"), object: nil, queue: nil, using:  _ in
        // Unfocus the text field.
        self.view.window?.resignFirstResponder()
    )

然后,您可以在想要取消聚焦文本字段时执行此操作。

NotificationCenter.default.post(name: Notification.Name("UnfocusTextField"), object: nil)

但如果文本字段当前处于活动状态,这可能不起作用。

【讨论】:

如果我有一个带有几个 FocusedTextFields 的视图。焦点的顺序是如何定义的?如何以编程方式为另一个 FocusedTextField 提供焦点? FocusedTextFields 呈现为 5 行的“TextEditor”。我可以将其限制为一行吗(textField.maximumNumberOfLines = 1,仅防止字段中的换行符) 如果要聚焦另一个文本字段,需要调用NSViewController中的view.window?.makeFirstResponder(view)方法。您可以像我一样使用通知让第一响应者辞职(这实际上不起作用),然后使视图成为第一响应者。我真的不知道“一行”是指文本字段的大小还是文本字段的行数。如果要限制行数,可以使用textField.usesSingleLineMode = true。如果要设置大小,可以使用 SwiftUI 的帧修饰符:.frame(height: yourheight)。 25 将是默认高度。

以上是关于AppKit 上的 SwiftUI - 在弹出窗口中将焦点设置为 TextField的主要内容,如果未能解决你的问题,请参考以下文章

让我们在 MacOS AppKit 上的 SwiftUI 中使用所有可用宽度的按钮

如何在 AppKit 上的自定义 SwiftUI 表单中右对齐项目标签?

Mac 上的 SwiftUI - 如何将按钮指定为主按钮?

在弹出窗口中从 iframe 向后台脚本发送消息

SwiftUI 按钮,在 AppKit 中扩展并接受键盘快捷键

在弹出窗口上预览图像,无法正确显示