You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 15下SwiftUI指定TextField绑定键盘工具栏出现异常行为

问题根因

你遇到的是SwiftUI的固有行为:键盘层级的工具栏会在键盘弹出前完成初始渲染,不会跟随focus状态的变更做实时刷新,所以你在外层视图统一加的toolbar里的条件判断会和实际focus状态不同步。另外如果你在toolbar里只返回条件视图,空状态下系统会直接判定工具栏无内容,自动隐藏,导致所有输入框都看不到工具栏。


最优解决方案

不要把toolbar修饰符加在全局的外层视图上,直接把toolbar绑定到需要显示特殊工具栏的TextField本身,完全不需要额外做focus状态判断:

struct Test: View {
    
    @State private var name: String = ""
    @State private var address: String = ""
    @State private var username: String = ""
    @State private var password: String = ""
    
    enum Field: Hashable {
        case nameField
        case addressField
        case usernameField
        case passwordField
    }
    @FocusState private var focusedField: Field?
    
    var body: some View {
        Form {
            Section {
                TextField("Name", text: $name)
                    .focused($focusedField, equals: .nameField)
                    .submitLabel(.next)
                    // 普通输入框加通用工具栏
                    .toolbar {
                        ToolbarItemGroup(placement: .keyboard) {
                            Button("Done", action: {
                                focusedField = nil
                            })
                        }
                    }
                
                TextField("Address", text: $address)
                    .focused($focusedField, equals: .addressField)
                    .submitLabel(.next)
                    // 仅地址输入框加带Test按钮的专属工具栏
                    .toolbar {
                        ToolbarItemGroup(placement: .keyboard) {
                            Button("Test", action: {})
                            Button("Done", action: {
                                focusedField = nil
                            })
                        }
                    }
                
                TextField("Username", text: $username)
                    .focused($focusedField, equals: .usernameField)
                    .submitLabel(.next)
                    // 普通输入框加通用工具栏
                    .toolbar {
                        ToolbarItemGroup(placement: .keyboard) {
                            Button("Done", action: {
                                focusedField = nil
                            })
                        }
                    }
                
                SecureField("Password", text: $password)
                    .focused($focusedField, equals: .passwordField)
                    .submitLabel(.done)
                    // 普通输入框加通用工具栏
                    .toolbar {
                        ToolbarItemGroup(placement: .keyboard) {
                            Button("Done", action: {
                                focusedField = nil
                            })
                        }
                    }
            }
        }
        // 外层不再统一添加toolbar
    }
}

简化重复代码

如果觉得重复写通用工具栏太冗余,可以做View扩展封装通用逻辑:

extension View {
    func addDefaultKeyboardToolbar(focused: FocusState<Test.Field?>.Binding) -> some View {
        self.toolbar {
            ToolbarItemGroup(placement: .keyboard) {
                Button("Done", action: {
                    focused.wrappedValue = nil
                })
            }
        }
    }
}

之后普通输入框直接调用.addDefaultKeyboardToolbar(focused: $focusedField)即可,特殊输入框单独定义专属工具栏即可。


兼容备选方案

如果你一定要在外层统一管理工具栏,可以给focus状态绑定动画强制刷新工具栏,也能解决显示不同步的问题:

// 外层toolbar保持你原来的写法
.toolbar {
    ToolbarItemGroup(placement: .keyboard) {
        if focusedField == .addressField {
            Button("Test", action: {})
        }
        Button("Done", action: {
            focusedField = nil
        })
    }
}
// 加动画强制跟随focus状态刷新
.animation(.easeInOut(duration: 0.1), value: focusedField)

如果需要完全隐藏部分输入框的工具栏,还是第一种方案更可靠。

内容的提问来源于stack exchange,提问作者Darren

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 11:06:03