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
相关产品推荐
相关产品推荐

