如何使TextField成为第一响应者?

Epa*_*aga 31 swift swiftui

这是我的代码...

struct ContentView : View {
    @State var showingTextField = false
    @State var text = ""
    var body: some View {
        return VStack {
            if showingTextField {
                TextField($text)
            }
            Button(action: {
                self.showingTextField.toggle()
            }) {
                Text ("Show")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我想要的是当文本字段变为可见时,使文本字段成为第一响应者(即,获得焦点并弹出键盘)。

Jos*_*fer 81

使用SwiftUI-Introspect,您可以执行以下操作:

TextField("", text: $value)
.introspectTextField { textField in
    textField.becomeFirstResponder()
}
Run Code Online (Sandbox Code Playgroud)

  • 如果 TextField 上有样式修饰符,则通常必须在样式后面添加 introspectTextField 修饰符,因为顺序很重要。 (3认同)
  • 我无法让它在 Introspect 0.0.6 或 0.1.0 上工作。:( (3认同)
  • 这个 Cocoapod 也对我有用。我怀疑这不是被接受的答案的唯一原因是这个问题早在 SwiftUI-Introspect 创建之前(2019 年 11 月下旬)就在 6 月(WWDC19)提出并得到了回答。 (2认同)

Mat*_*ini 29

目前似乎还不太可能,但是您可以自己实现类似的功能。

您可以创建一个自定义文本字段并添加一个值,使其成为第一响应者。

struct CustomTextField: UIViewRepresentable {

    class Coordinator: NSObject, UITextFieldDelegate {

        @Binding var text: String
        var didBecomeFirstResponder = false

        init(text: Binding<String>) {
            _text = text
        }

        func textFieldDidChangeSelection(_ textField: UITextField) {
            text = textField.text ?? ""
        }

    }

    @Binding var text: String
    var isFirstResponder: Bool = false

    func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.delegate = context.coordinator
        return textField
    }

    func makeCoordinator() -> CustomTextField.Coordinator {
        return Coordinator(text: $text)
    }

    func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) {
        uiView.text = text
        if isFirstResponder && !context.coordinator.didBecomeFirstResponder  {
            uiView.becomeFirstResponder()
            context.coordinator.didBecomeFirstResponder = true
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:didBecomeFirstResponder需要确保文本字段仅成为第一响应者,而不是每次刷新时都由SwiftUI

您将像这样使用它...

struct ContentView : View {

    @State var text: String = ""

    var body: some View {
        CustomTextField(text: $text, isFirstResponder: true)
            .frame(width: 300, height: 50)
            .background(Color.red)
    }

}
Run Code Online (Sandbox Code Playgroud)

附注:我添加了一个,frame因为它的行为不像股票TextField,这意味着幕后还有更多的事情要做。

更多关于Coordinators在这个优秀的WWDC 19讲: 整合SwiftUI

在Xcode 11.1上测试

  • 就像魅力一样,谢谢! (3认同)
  • @Greg 是的,仍然需要。 (3认同)

Moj*_*ini 20

iOS 15

There is a new wrapper called @FocusState that controls the state of the keyboard and the focused keyboard ('aka' firstResponder).

Becaome First Responder ( Focused )

If you use a focused modifier on the text fields, you can make them become focused:

在此处输入图片说明

Resign first responder ( Dismiss keyboard )

or dismiss the keyboard by setting the variable to nil:

在此处输入图片说明



iOS 13 and above: Old but working!

Simple wrapper struct - Works like a native:

请注意,根据评论中的要求添加文本绑定支持

struct LegacyTextField: UIViewRepresentable {
    @Binding public var isFirstResponder: Bool
    @Binding public var text: String

    public var configuration = { (view: UITextField) in }

    public init(text: Binding<String>, isFirstResponder: Binding<Bool>, configuration: @escaping (UITextField) -> () = { _ in }) {
        self.configuration = configuration
        self._text = text
        self._isFirstResponder = isFirstResponder
    }

    public func makeUIView(context: Context) -> UITextField {
        let view = UITextField()
        view.addTarget(context.coordinator, action: #selector(Coordinator.textViewDidChange), for: .editingChanged)
        view.delegate = context.coordinator
        return view
    }

    public func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
        configuration(uiView)
        switch isFirstResponder {
        case true: uiView.becomeFirstResponder()
        case false: uiView.resignFirstResponder()
        }
    }

    public func makeCoordinator() -> Coordinator {
        Coordinator($text, isFirstResponder: $isFirstResponder)
    }

    public class Coordinator: NSObject, UITextFieldDelegate {
        var text: Binding<String>
        var isFirstResponder: Binding<Bool>

        init(_ text: Binding<String>, isFirstResponder: Binding<Bool>) {
            self.text = text
            self.isFirstResponder = isFirstResponder
        }

        @objc public func textViewDidChange(_ textField: UITextField) {
            self.text.wrappedValue = textField.text ?? ""
        }

        public func textFieldDidBeginEditing(_ textField: UITextField) {
            self.isFirstResponder.wrappedValue = true
        }

        public func textFieldDidEndEditing(_ textField: UITextField) {
            self.isFirstResponder.wrappedValue = false
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

struct ContentView: View {
    @State var text = ""
    @State var isFirstResponder = false

    var body: some View {
        LegacyTextField(text: $text, isFirstResponder: $isFirstResponder)
    }
}
Run Code Online (Sandbox Code Playgroud)

奖励:完全可定制

LegacyTextField(text: $text, isFirstResponder: $isFirstResponder) {
    $0.textColor = .red
    $0.tintColor = .blue
}
Run Code Online (Sandbox Code Playgroud)

这种方法是完全适应的。例如,你可以看到如何在SwiftUI添加活动指示灯用同样的方法在这里

  • 这缺少 TextField 通常具有的 $text 绑定(和标签)?(无论如何,我无法让它工作) (4认同)

Cas*_*gen 12

响应链

我为跨平台第一响应者处理制作了这个小包,而无需在 iOS 13+ 上的SwiftUI中对视图进行子类化或制作自定义 ViewRepresentables

https://github.com/Amzd/ResponderChain

如何将其应用于您的问题

SceneDelegate.swift

...
// Set the ResponderChain as environmentObject
let rootView = ContentView().environmentObject(ResponderChain(forWindow: window))
...
Run Code Online (Sandbox Code Playgroud)

内容视图.swift

struct ContentView: View {

    @EnvironmentObject var chain: ResponderChain
    @State var showingTextField = false
    @State var text = ""

    var body: some View {
        return VStack {
            if showingTextField {
                TextField($text).responderTag("field1").onAppear {
                    DispatchQueue.main.async {
                        chain.firstResponder = "field1"
                    }
                }
            }
            Button(action: { self.showingTextField.toggle() }) {
                Text ("Show")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您应该提到该解决方案包含对其他两个包的依赖项。 (3认同)
  • 迄今为止我见过的最优雅的解决方案。对 SwiftUI 的大力支持。我自己用过,强烈推荐! (2认同)
  • 我理解这一点,但了解您的解决方案不是独立的并且存在其他代码依赖项可能对其他人有帮助。这并不意味着该解决方案是好还是坏。了解依赖关系只是有用的,仅此而已。 (2认同)
  • @CasperZandbergen 它非常好,但没有回答我的初始化问题。在最新的 SwiftUI 版本中演示它的一个小示例应用程序对于像我这样的新 SwiftUI 开发人员来说将大有帮助。我完全不熟悉 SceneDelegate 和 ApplicationDelegate 是什么,因为它们不再存在于苹果最新的示例代码中。我意识到 SwiftUI 本身的文档记录非常丰富,但我认为这使得库更有必要广泛记录其用法和假设。 (2认同)

mah*_*han 11

iOS 15.0+

macOS 12.0+,

Mac 催化剂 15.0+,

tvOS 15.0+,

watchOS 8.0+

focused(_:)如果您只有一个 TextField,请使用。

专注(_:)

通过将其焦点状态绑定到给定的布尔状态值来修改此视图。

struct NameForm: View {
    
    @FocusState private var isFocused: Bool
    
    @State private var name = ""
    
    var body: some View {
        TextField("Name", text: $name)
            .focused($isFocused)
        
        Button("Submit") {
            if name.isEmpty {
                isFocued = true
            }
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

focused(_:equals:)如果您有多个 TextField,请使用。

专注(_:等于:)

通过将其焦点状态绑定到给定的状态值来修改此视图。

struct LoginForm: View {
    enum Field: Hashable {
        case usernameField
        case passwordField
    }

    @State private var username = ""
    @State private var password = ""
    @FocusState private var focusedField: Field?

    var body: some View {
        Form {
            TextField("Username", text: $username)
                .focused($focusedField, equals: .usernameField)

            SecureField("Password", text: $password)
                .focused($focusedField, equals: .passwordField)

            Button("Sign In") {
                if username.isEmpty {
                    focusedField = .usernameField
                } else if password.isEmpty {
                    focusedField = .passwordField
                } else {
                    handleLogin(username, password)
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

SwiftUI 文档

  • 如果这只是向后兼容就好了。至少到iOS 14! (8认同)
  • 专注于表单对我来说不起作用 (2认同)
  • iOS 15 或 Xcode 13 中存在错误。@Dasoga (2认同)
  • 也许我只是在这里很密集,但这似乎是如何找出重点关注哪个领域的答案,而不是设置重点在某个领域。我正在努力弄清楚如何给予字段默认焦点。 (2认同)

Das*_*oga 10

就我而言,我想立即聚焦一个文本字段,我使用了.onappear函数

struct MyView: View {
    
    @FocusState private var isTitleTextFieldFocused: Bool

    @State private var title = ""
    
    var body: some View {
        VStack {
            TextField("Title", text: $title)
                .focused($isTitleTextFieldFocused)
            
        }
        .padding()
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                self.isTitleTextFieldFocused = true
            }
            
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @King 需要 iOS 15 (3认同)
  • 除了将延迟更改为“.now() + 0.02”之类的内容以便键盘立即显示之外,这个答案是最好的答案,至少对于 iOS 15 来说是这样。快速简单,无需添加大量代码或依赖项。 (2认同)

Rui*_*ing 8

对于那些最终在这里遇到但使用@Matteo Pacini的答案崩溃的人,请注意beta 4中的这一更改:无法分配给属性:'$ text'对于此块是不变的

init(text: Binding<String>) {
    $text = text
}
Run Code Online (Sandbox Code Playgroud)

应该使用:

init(text: Binding<String>) {
    _text = text
}
Run Code Online (Sandbox Code Playgroud)

而且,如果您想让文本字段成为的第一响应者sheet,请注意,becomeFirstResponder只有在显示该文本字段后才能调用。换句话说,将@Matteo Pacini的文本字段直接放在sheet内容中会导致崩溃。

要解决此问题,请添加其他检查uiView.window != nil以检查文本字段的可见性。在焦点集中在视图层次结构中之后:

struct AutoFocusTextField: UIViewRepresentable {
    @Binding var text: String

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: UIViewRepresentableContext<AutoFocusTextField>) -> UITextField {
        let textField = UITextField()
        textField.delegate = context.coordinator
        return textField
    }

    func updateUIView(_ uiView: UITextField, context:
        UIViewRepresentableContext<AutoFocusTextField>) {
        uiView.text = text
        if uiView.window != nil, !uiView.isFirstResponder {
            uiView.becomeFirstResponder()
        }
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: AutoFocusTextField

        init(_ autoFocusTextField: AutoFocusTextField) {
            self.parent = autoFocusTextField
        }

        func textFieldDidChangeSelection(_ textField: UITextField) {
            parent.text = textField.text ?? ""
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*ler 7

正如其他人所指出的(例如@kipelovets 评论已接受的答案,例如@Eonil 的答案),我也没有找到任何适用于 macOS 的公认解决方案。但是,我有一些运气,使用NSViewControllerRepresentable使NSSearchField显示为 SwiftUI 视图中的第一响应者:

import Cocoa
import SwiftUI

class FirstResponderNSSearchFieldController: NSViewController {

  @Binding var text: String

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

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

  override func loadView() {
    let searchField = NSSearchField()
    searchField.delegate = self
    self.view = searchField
  }

  override func viewDidAppear() {
    self.view.window?.makeFirstResponder(self.view)
  }
}

extension FirstResponderNSSearchFieldController: NSSearchFieldDelegate {

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

struct FirstResponderNSSearchFieldRepresentable: NSViewControllerRepresentable {

  @Binding var text: String

  func makeNSViewController(
    context: NSViewControllerRepresentableContext<FirstResponderNSSearchFieldRepresentable>
  ) -> FirstResponderNSSearchFieldController {
    return FirstResponderNSSearchFieldController(text: $text)
  }

  func updateNSViewController(
    _ nsViewController: FirstResponderNSSearchFieldController,
    context: NSViewControllerRepresentableContext<FirstResponderNSSearchFieldRepresentable>
  ) {
  }
}
Run Code Online (Sandbox Code Playgroud)

示例 SwiftUI 视图:

struct ContentView: View {

  @State private var text: String = ""

  var body: some View {
    FirstResponderNSSearchFieldRepresentable(text: $text)
  }
}

Run Code Online (Sandbox Code Playgroud)


小智 5

为了填补这个缺失的功能,你可以使用 Swift Package Manager 安装 SwiftUIX:

  1. 在 Xcode 中,打开您的项目并导航到 File ? 斯威夫特包?添加包依赖...
  2. 粘贴存储库 URL ( https://github.com/SwiftUIX/SwiftUIX ) 并单击下一步。
  3. 对于规则,选择分支(分支设置为 master)。
  4. 单击完成。
  5. 打开项目设置,将 SwiftUI.framework 添加到 Linked Frameworks and Libraries,将 Status 设置为 Optional。

更多信息:https : //github.com/SwiftUIX/SwiftUIX

import SwiftUI
import SwiftUIX

struct ContentView : View {

    @State var showingTextField = false
    @State var text = ""

    var body: some View {
        return VStack {
            if showingTextField {
                CocoaTextField("Placeholder text", text: $text)
                    .isFirstResponder(true)
                    .frame(width: 300, height: 48, alignment: .center)
            }
            Button(action: { self.showingTextField.toggle() }) {
                Text ("Show")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Aas*_*ish 5

斯威夫特用户界面

struct ContentView: View {
enum Field {
    case firstTextfield
    case secondTextfield
    case lastTextfield
}

@State private var firstTextfield = ""
@State private var secondTextfield = ""
@State private var lastTextfield = ""
@FocusState private var focusedField: Field?

var body: some View {
    VStack {
        TextField("Enter anything on first textfield", text: $firstTextfield)
            .focused($focusedField, equals: .firstTextfield)
            .submitLabel(.next)

        TextField("Enter anything on second textfield", text: $secondTextfield)
            .focused($focusedField, equals: .secondTextfield)
            .submitLabel(.next)

        TextField("Enter anything on last textfield", text: $lastTextfield)
            .focused($focusedField, equals: .lastTextfield)
            .submitLabel(.join)
    }
    .onSubmit {
        switch focusedField {
        case .firstTextfield:
            focusedField = .secondTextfield
        case .secondTextfield:
            focusedField = .lastTextfield
        default:
            focusedField = nil
        }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

描述:添加一个带有文本字段案例的枚举,以及一​​个包含在@FocusState该枚举类型中的属性。添加focused(_:equals:)修饰符以具有绑定值,等于枚举情况。现在,您可以将 更改focusedField为您希望光标所在的任何文本字段,或者通过将 nil 分配给focusedField 来退出第一响应者。


归档时间:

查看次数:

3078 次

最近记录:

5 年,10 月 前