SwiftUI: 如何让 TextField 成为第一响应者?

这是我的 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")
}
}
}
}

我想要的是,当文本字段成为 看得见,使文本字段成为第一个响应者(即接收焦点和有键盘弹出)。

83212 次浏览

Swift UI 3

从 Xcode 13开始,可以使用 focused修饰符使视图成为第一响应者。


快速用户界面1/2

目前看来这是不可能的,但是你可以自己实现类似的东西。

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

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
}
}
}

注意: 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)
}


}

另外,我添加了一个 frame,因为它的行为不像股票 TextField,这意味着有更多的东西正在幕后进行。

更多关于 Coordinators的资料,请参阅 WWDC 第19期精彩演讲: 集成 SwiftUI

在 Xcode 11.4上测试

对于那些最终来到这里,但使用@Matteo Pacini 的回答面临崩溃的人,请注意 beta 4: 无法分配到属性: $text & # 39; 是不可变的中关于这个区块的这个变化:

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

应使用:

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

如果您想使文本字段成为 sheet中的第一响应者,请注意在文本字段显示之前不能调用 becomeFirstResponder。换句话说,把@Matteo Pacini 的文本字段直接放在 sheet内容中会导致崩溃。

要解决这个问题,需要为 textfield 的可见性添加一个额外的检查 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 ?? ""
}
}
}

IOS15

有一个名为 @FocusState的新包装器,它控制键盘和聚焦键盘的状态(“ aka”firstResponder)。

成为第一反应者(聚焦)

如果你在文本字段上使用 focused修饰符,你可以使它们变得聚焦:

enter image description here

辞职第一响应者(解散键盘)

或者通过将变量设置为 nil来关闭键盘:

enter image description here



IOS13及以上版本: 老旧但正常!

简单的包装器 struct ——像本地语言一样工作:

请注意,按照注释中的要求添加了 文本绑定支持

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.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
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
}
}
}

用法:

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


var body: some View {
LegacyTextField(text: $text, isFirstResponder: $isFirstResponder)
}
}

奖励: 完全可定制

LegacyTextField(text: $text, isFirstResponder: $isFirstResponder) {
$0.textColor = .red
$0.tintColor = .blue
}

此方法具有完全的适应性。例如,您可以使用相同的方法 给你查看 < strong > 如何在 SwiftUI 中添加活动指示符

使用 SwiftUI-内省,你可以:

TextField("", text: $value)
.introspectTextField { textField in
textField.becomeFirstResponder()
}

选择的答案导致了 AppKit 的一些无限循环问题。我不知道 UIKit 的情况。

为了避免这个问题,我建议直接共享 NSTextField实例。

import AppKit
import SwiftUI


struct Sample1: NSViewRepresentable {
var textField: NSTextField
func makeNSView(context:NSViewRepresentableContext<Sample1>) -> NSView { textField }
func updateNSView(_ x:NSView, context:NSViewRepresentableContext<Sample1>) {}
}

你可以这样用。

let win = NSWindow()
let txt = NSTextField()
win.setIsVisible(true)
win.setContentSize(NSSize(width: 256, height: 256))
win.center()
win.contentView = NSHostingView(rootView: Sample1(textField: txt))
win.makeFirstResponder(txt)


let app = NSApplication.shared
app.setActivationPolicy(.regular)
app.run()

这打破了纯价值语义,但是依赖 AppKit 意味着您部分地放弃了纯价值语义,并且会产生一些脏东西。这是一个魔术洞,我们需要现在处理缺乏第一反应控制在 SwiftUI。

由于我们直接访问 NSTextField,设置第一响应器是普通的 AppKit 方式,因此没有明显的麻烦来源。

您可以下载工作源代码 给你

正如其他人所指出的(例如 @ kipelovets 对公认答案的评论,例如 @ Eonil 的回答) ,我也没有找到任何可以在 macOS 上工作的解决方案。然而,我有一些运气,使用 NSViewController 可代表性得到一个 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>
) {
}
}

SwiftUI 视图示例:

struct ContentView: View {


@State private var text: String = ""


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


由于响应器链不能通过 SwiftUI 使用,因此我们必须使用 UIViewRezeable 来使用它。

一定要看看下面的链接,因为我已经做了一个工作方法,可以类似于我们使用 UIKit 的方式工作。

Https://stackoverflow.com/a/61121199/6445871

它是我的实现的变体,基于@Mojtaba Hosseini 和@Matteo Pacini 解决方案。 我对 SwiftUI 还是一个新手,所以我不能保证代码的绝对正确性,但是它可以工作。

我希望对某些人有所帮助。

ResponderView: 这是一个通用响应视图,可以与任何 UIKit 视图一起使用。

struct ResponderView<View: UIView>: UIViewRepresentable {
@Binding var isFirstResponder: Bool
var configuration = { (view: View) in }


func makeUIView(context: UIViewRepresentableContext<Self>) -> View { View() }


func makeCoordinator() -> Coordinator {
Coordinator($isFirstResponder)
}


func updateUIView(_ uiView: View, context: UIViewRepresentableContext<Self>) {
context.coordinator.view = uiView
_ = isFirstResponder ? uiView.becomeFirstResponder() : uiView.resignFirstResponder()
configuration(uiView)
}
}


// MARK: - Coordinator
extension ResponderView {
final class Coordinator {
@Binding private var isFirstResponder: Bool
private var anyCancellable: AnyCancellable?
fileprivate weak var view: UIView?


init(_ isFirstResponder: Binding<Bool>) {
_isFirstResponder = isFirstResponder
self.anyCancellable = Publishers.keyboardHeight.sink(receiveValue: { [weak self] keyboardHeight in
guard let view = self?.view else { return }
DispatchQueue.main.async { self?.isFirstResponder = view.isFirstResponder }
})
}
}
}


// MARK: - keyboardHeight
extension Publishers {
static var keyboardHeight: AnyPublisher<CGFloat, Never> {
let willShow = NotificationCenter.default.publisher(for: UIApplication.keyboardWillShowNotification)
.map { ($0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height ?? 0 }


let willHide = NotificationCenter.default.publisher(for: UIApplication.keyboardWillHideNotification)
.map { _ in CGFloat(0) }


return MergeMany(willShow, willHide)
.eraseToAnyPublisher()
}
}


struct ResponderView_Previews: PreviewProvider {
static var previews: some View {
ResponderView<UITextField>.init(isFirstResponder: .constant(false)) {
$0.placeholder = "Placeholder"
}.previewLayout(.fixed(width: 300, height: 40))
}
}

ResponderTextField ——这是一个方便的 ResponderView 文本字段包装器。

struct ResponderTextField: View {
var placeholder: String
@Binding var text: String
@Binding var isFirstResponder: Bool
private var textFieldDelegate: TextFieldDelegate


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


var body: some View {
ResponderView<UITextField>(isFirstResponder: $isFirstResponder) {
$0.text = self.text
$0.placeholder = self.placeholder
$0.delegate = self.textFieldDelegate
}
}
}


// MARK: - TextFieldDelegate
private extension ResponderTextField {
final class TextFieldDelegate: NSObject, UITextFieldDelegate {
@Binding private(set) var text: String


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


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


struct ResponderTextField_Previews: PreviewProvider {
static var previews: some View {
ResponderTextField("Placeholder",
text: .constant(""),
isFirstResponder: .constant(false))
.previewLayout(.fixed(width: 300, height: 40))
}
}

用得好。

struct SomeView: View {
@State private var login: String = ""
@State private var password: String = ""
@State private var isLoginFocused = false
@State private var isPasswordFocused = false


var body: some View {
VStack {
ResponderTextField("Login", text: $login, isFirstResponder: $isLoginFocused)
ResponderTextField("Password", text: $password, isFirstResponder: $isPasswordFocused)
}
}
}

这是一个可以与 自我反省协同工作的视图修改器,它适用于 AppKit MacOS,Xcode 11.5

    struct SetFirstResponderTextField: ViewModifier {
@State var isFirstResponderSet = false


func body(content: Content) -> some View {
content
.introspectTextField { textField in
if self.isFirstResponderSet == false {
textField.becomeFirstResponder()
self.isFirstResponderSet = true
}
}
}
}

因为 SwiftUI 2不支持第一响应者,所以我使用这个解决方案。它是肮脏的,但是当您只有1个 UITextField和1个 UIWindow时,它可能适用于某些用例。

import SwiftUI


struct MyView: View {
@Binding var text: String


var body: some View {
TextField("Hello world", text: $text)
.onAppear {
UIApplication.shared.windows.first?.rootViewController?.view.textField?.becomeFirstResponder()
}
}
}


private extension UIView {
var textField: UITextField? {
subviews.compactMap { $0 as? UITextField }.first ??
subviews.compactMap { $0.textField }.first
}
}

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

  1. 在 Xcode,打开你的项目,导航到文件→快速软件包→添加软件包依赖..。
  2. 粘贴存储库 URL (https://github.com/SwiftUIX/SwiftUIX)并单击 Next。
  3. 对于 Rules,选择 Branch (将分支设置为 master)。
  4. 单击 Finish。
  5. 打开项目设置,将 SwiftUI.Framework 添加到“链接框架和库”中,将 Status 设置为“可选”。

更多资讯: 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")
}
}
}
}

我们有一个解决方案,可以轻松控制第一反应者。

Https://github.com/mobilinked/mbswiftuifirstresponder

TextField("Name", text: $name)
.firstResponder(id: FirstResponders.name, firstResponder: $firstResponder, resignableUserOperations: .all)


TextEditor(text: $notes)
.firstResponder(id: FirstResponders.notes, firstResponder: $firstResponder, resignableUserOperations: .all)

响应链

我在 iOS13 + 上用 SwiftUI 为 跨平台的第一响应者处理没有子类视图或自定义视图代表做了这个小软件包

Https://github.com/amzd/responderchain

如何应用它来解决你的问题

现场授权,迅速

...
// Set the ResponderChain as environmentObject
let rootView = ContentView().environmentObject(ResponderChain(forWindow: window))
...

ContentView.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")
}
}
}
}

不是真正的答案,只是建立在 Casper 的伟大的解决方案与一个方便的修改-

struct StartInput: ViewModifier {
    

@EnvironmentObject var chain: ResponderChain
    

private let tag: String
    

    

init(tag: String) {
self.tag = tag
}
    

func body(content: Content) -> some View {
        

content.responderTag(tag).onAppear() {
DispatchQueue.main.async {
chain.firstResponder = tag
}
}
}
}




extension TextField {
    

func startInput(_ tag: String = "field") -> ModifiedContent<TextField<Label>, StartInput> {
self.modifier(StartInput(tag: tag))
}
}

就像..

TextField("Enter value:", text: $quantity)
.startInput()

IOS 15.0 +

MacOS 12.0 + ,

Mac Catalyst 15.0 + ,

TvOS 15.0 + ,

Watch OS 8.0 +

如果只有一个 TextField,则使用 focused(_:)

聚焦(_:)

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

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
}
}
}
}


如果有多个文本字段,则使用 focused(_:equals:)

聚焦(_: 等于:)

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

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)
}
}
}
}
}

SwiftUI 文档


更新

我用 xCode version 13.0 beta 5 (13A5212g)测试过,很有效

在我的例子中,我想要立即对一个文本字段进行聚焦,我使用了 .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
}
            

}
}
}

扩展@Joshua Kifer 的回答,如果你正在处理导航动画在使用内省制作文本字段第一响应时出现故障的问题。用这个:

import SchafKit


@State var field: UITextField?


TextField("", text: $value)
.introspectTextField { textField in
field = textField
}
.onDidAppear {
field?.becomeFirstResponder()
}

关于此解决方案的更多细节 给你

我采取了一个有点不同的方法-而不是 UITextField的基础上的 UIViewRepresentable i 使它基于 UIView和插入它在 SwiftUI 视图层次结构与 background修饰符。在 UIView内部,我添加了逻辑来查找子视图和父视图中的 canBecomeFirstResponder的第一个视图。

private struct FocusableUIView: UIViewRepresentable {
var isFirstResponder: Bool = false


class Coordinator: NSObject {
var didBecomeFirstResponder = false
}


func makeUIView(context: UIViewRepresentableContext<FocusableUIView>) -> UIView {
let view = UIView()
view.backgroundColor = .clear
return view
}


func makeCoordinator() -> FocusableUIView.Coordinator {
return Coordinator()
}


func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<FocusableUIView>) {
guard uiView.window != nil, isFirstResponder, !context.coordinator.didBecomeFirstResponder else {
return
}


var foundRepsonder: UIView?
var currentSuperview: UIView? = uiView
repeat {
foundRepsonder = currentSuperview?.subviewFirstPossibleResponder
currentSuperview = currentSuperview?.superview
} while foundRepsonder == nil && currentSuperview != nil


guard let responder = foundRepsonder else {
return
}


DispatchQueue.main.async {
responder.becomeFirstResponder()
context.coordinator.didBecomeFirstResponder = true
}
}
}


private extension UIView {
var subviewFirstPossibleResponder: UIView? {
guard !canBecomeFirstResponder else { return self }


for subview in subviews {
if let firstResponder = subview.subviewFirstPossibleResponder {
return firstResponder
}
}


return nil
}
}




这里有一个例子,如何使用它使 TextField 自动对焦(+ 奖金利用 @FocusState新的 iOS15api)。

extension View {
@ViewBuilder
func autofocus() -> some View {
if #available(iOS 15, *) {
modifier(AutofocusedViewModifiers.Modern())
} else {
modifier(AutofocusedViewModifiers.Legacy())
}
}
}


private enum AutofocusedViewModifiers {
struct Legacy: ViewModifier {
func body(content: Content) -> some View {
content
.background(FocusableUIView(isFirstResponder: isFocused))
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
isFocused = true
}
}
}


@State private var isFocused = false
}


@available(iOS 15, *)
struct Modern: ViewModifier {
func body(content: Content) -> some View {
content
.focused($isFocused)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
isFocused = true
}
}
}


@FocusState private var isFocused: Bool
}
}


内容视图示例:

struct ContentView: View {
@State private var text = ""


var body: some View {
VStack {
TextField("placeholder", text: $text)
Text("some text")
}
.autofocus()
}
}


正确的 SwiftUI 方法是使用上面提到的 @FocusState。但是这个 API 只适用于 iOS15。如果您正在使用 iOS14或 iOS13,您可以使用 Focuser 库,它是按照 AppleAPI 建模的。

Https://github.com/art-technologies/swift-focuser

enter image description here

这是一个示例代码。你会注意到 API 看起来几乎和苹果一模一样,但是 Focuser 也提供了使用键盘来移动第一响应者的链条,这非常方便。

enter image description here

如果你对 @ Joshua Kifer@ ahaze的反应有任何问题, 我已经用 在父类上使用修饰符解决了我的问题,而不是用 TextField 本身。

我所做的:

TextField("Placeholder text...", text: $searchInput)
.introspectTextField { textField in
textField.becomeFirstResponder()
}

我是如何解决我的问题的:

   YourParentStruct(searchInput: $searchInput)
.introspectTextField { textField in
textField.becomeFirstResponder()
}

为了清楚起见,我将把父结构的定义放在下面

   struct YourParentStruct: View {
@Binding var searchInput: String


var body: some View {
HStack {
TextField("Placeholder text...", text: $searchInput)
.padding()
.background(Color.gray)
.cornerRadius(10)
}
}
}

在它的 很简单 表格 IOS13(没有使用任何第三方 sdk/repo,或者如果你没有升级到 iOS 14使用 focus修饰)

struct CustomTextField: UIViewRepresentable {
            

func makeUIView(context: Context) -> UITextField {
UITextField(frame: .zero)
}
    

func updateUIView(_ uiView: UITextField, context: Context) {
uiView.becomeFirstResponder()
}
}

用法:

struct ContentView : View {


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

SwiftUI

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
}
}
}
}

描述: 添加一个包含文本字段大小写的枚举,以及一个包装在 @FocusState中的、具有该枚举类型的属性。添加 focused(_:equals:)修饰符,使其具有与枚举大小写相等的绑定值。现在,您可以将 focusedField更改为您希望光标位于其中的任何文本字段,或者通过将 nil 分配给 focus edField 来放弃第一响应者。

我知道这已经太晚了,但如果它帮助任何人,这是我怎么做。

import SwiftUI
import Introspect


struct MyView: View {
    

@Binding var text1: String
@Binding var text2: String
    

@State private var toggleTF: Bool = false


var body: some View {
        

TextField("TextField 1", text: $text1)
.introspectTextField{ tF in
if toggleTF {
tF.becomeFirstResponder()
}
}
        

TextField("TextField 2", text: $text1)
.introspectTextField{ tF in
if !toggleTF {
tF.becomeFirstResponder()
}
}
        

Button("Toggle textfields") {
toggleTF.toggle()
}
}
}