SwiftUI中如何用@FocusState保持键盘打开且避免焦点切换弹跳?
解决SwiftUI TextField焦点切换时的弹跳问题
问题根源
这种轻微弹跳通常是因为焦点变化时TextField的内置样式(如roundedBorder)会调整边框宽度、内部padding或阴影,导致布局尺寸发生微小变化,触发SwiftUI的默认布局动画,从而产生跳动效果。
解决方案
1. 固定TextField尺寸
给TextField添加固定宽高,避免焦点变化时尺寸波动:
struct NameInputView: View { @State private var firstName = "" @State private var lastName = "" @FocusState private var focusedField: Field? enum Field { case firstName, lastName } var body: some View { VStack(spacing: 16) { TextField("First Name", text: $firstName) .focused($focusedField, equals: .firstName) .textFieldStyle(.roundedBorder) .frame(height: 44) // 固定高度 .onSubmit { focusedField = .lastName } TextField("Last Name", text: $lastName) .focused($focusedField, equals: .lastName) .textFieldStyle(.roundedBorder) .frame(height: 44) // 固定高度 .onSubmit { focusedField = nil } Button("Done") { focusedField = nil } } .padding() } }
2. 使用自定义TextField样式(彻底避免布局变化)
自定义样式让焦点仅改变颜色而非布局尺寸,从根源消除跳动:
struct CustomTextFieldStyle: TextFieldStyle { let isFocused: Bool func _body(configuration: TextField<Self._Label>) -> some View { configuration .padding(.horizontal, 12) .padding(.vertical, 8) .background(Color(.systemGray6)) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(isFocused ? Color.accentColor : Color.clear, lineWidth: 1) ) } } struct NameInputView: View { @State private var firstName = "" @State private var lastName = "" @FocusState private var focusedField: Field? enum Field { case firstName, lastName } var body: some View { VStack(spacing: 16) { TextField("First Name", text: $firstName) .focused($focusedField, equals: .firstName) .textFieldStyle(CustomTextFieldStyle(isFocused: focusedField == .firstName)) .onSubmit { focusedField = .lastName } TextField("Last Name", text: $lastName) .focused($focusedField, equals: .lastName) .textFieldStyle(CustomTextFieldStyle(isFocused: focusedField == .lastName)) .onSubmit { focusedField = nil } Button("Done") { focusedField = nil } } .padding() } }
3. 禁用焦点切换时的动画
如果不需要焦点变化的过渡动画,可以直接禁用:
// 在切换焦点时包裹无动画逻辑 .onSubmit { withAnimation(.none) { focusedField = .lastName } } // 或者给TextField添加针对焦点变化的无动画修饰符 TextField("First Name", text: $firstName) .focused($focusedField, equals: .firstName) .textFieldStyle(.roundedBorder) .animation(.none, value: focusedField)
效果验证
应用上述任意方案后,焦点切换时布局尺寸不再发生变化,弹跳效果会完全消失。
内容的提问来源于stack exchange,提问作者agmcoder
相关产品推荐
相关产品推荐

