【问题标题】:SwiftUI on AppKit - Set Focus to a TextField in a popoverAppKit 上的 SwiftUI - 在弹出窗口中将焦点设置为 TextField
【发布时间】: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()
    }
  }
}

【问题讨论】:

    标签: macos swiftui focus


    【解决方案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 将是默认高度。
    猜你喜欢
    • 2012-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 2020-05-05
    • 1970-01-01
    • 2010-12-25
    • 2011-09-26
    相关资源
    最近更新 更多