SwiftUI OTP输入框下划线颜色切换动画适配Xcode 12.3问题
问题根因
你当前的写法是给下划线视图添加了无绑定的全局隐式动画,会导致所在视图层级内所有状态变化(包括Text的文本内容更新、TextField宽度变化)都被附加了动画效果,就会出现数字不同步、下划线状态滞后的问题。
修复方案
把下划线的动画绑定到唯一触发源isNextTyped的值上,仅当该值变化时才触发颜色过渡动画,其他状态变更不受影响。
具体修改点
- 移除
otpText方法里下划线原有无差别的animation(.easeIn)修饰符,替换为带value绑定的动画声明 - 可自定义动画时长适配交互体验,不会影响其他控件的状态更新逻辑
修改后完整代码
import SwiftUI @available(iOS 13.0, *) class OTPViewModel: ObservableObject { @Published var otpField = "" { didSet { isNextTypedArr = Array(repeating: false, count: 6) guard otpField.count <= 6, otpField.last?.isNumber ?? true else { otpField = oldValue return } if otpField.count < 6 { isNextTypedArr[otpField.count] = true } } } var otp1: String { guard otpField.count >= 1 else { return "" } return String(Array(otpField)[0]) } var otp2: String { guard otpField.count >= 2 else { return "" } return String(Array(otpField)[1]) } var otp3: String { guard otpField.count >= 3 else { return "" } return String(Array(otpField)[2]) } var otp4: String { guard otpField.count >= 4 else { return "" } return String(Array(otpField)[3]) } var otp5: String { guard otpField.count >= 5 else { return "" } return String(Array(otpField)[4]) } var otp6: String { guard otpField.count >= 6 else { return "" } return String(Array(otpField)[5]) } @Published var isNextTypedArr = Array(repeating: false, count: 6) @Published var borderColor: Color = .black @Published var isEditing = false { didSet { isNextTypedArr = Array(repeating: false, count: 6) if isEditing && otpField.count < 6 { isNextTypedArr[otpField.count] = true } } } } @available(iOS 13.0, *) struct CXLRPOTPView: View { @ObservedObject var viewModel = OTPViewModel() let textBoxWidth = UIScreen.main.bounds.width / 8 let textBoxHeight = UIScreen.main.bounds.width / 8 let spaceBetweenLines: CGFloat = 16 let paddingOfBox: CGFloat = 1 var textFieldOriginalWidth: CGFloat { (textBoxWidth*6)+(spaceBetweenLines*3)+((paddingOfBox*2)*3) } var body: some View { VStack { ZStack { HStack (spacing: spaceBetweenLines){ otpText(text: viewModel.otp1, isNextTyped: $viewModel.isNextTypedArr[0]) otpText(text: viewModel.otp2, isNextTyped: $viewModel.isNextTypedArr[1]) otpText(text: viewModel.otp3, isNextTyped: $viewModel.isNextTypedArr[2]) otpText(text: viewModel.otp4, isNextTyped: $viewModel.isNextTypedArr[3]) otpText(text: viewModel.otp5, isNextTyped: $viewModel.isNextTypedArr[4]) otpText(text: viewModel.otp6, isNextTyped: $viewModel.isNextTypedArr[5]) } TextField("", text: $viewModel.otpField) { isEditing in viewModel.isEditing = isEditing } .frame(width: viewModel.isEditing ? 0 : textFieldOriginalWidth, height: textBoxHeight) .textContentType(.oneTimeCode) .foregroundColor(.clear) .accentColor(.clear) .background(Color.clear) .keyboardType(.numberPad) } } } @available(iOS 13.0, *) private func otpText(text: String, isNextTyped: Binding<Bool>) -> some View { return Text(text) .font(Font.custom("GTWalsheim-Regular", size: 34)) .frame(width: textBoxWidth, height: textBoxHeight) .background(VStack{ Spacer() RoundedRectangle(cornerRadius: 1) .frame(height: 2) .foregroundColor(isNextTyped.wrappedValue ? Color(hex: "#367878") : Color(hex: "#BCBEC0")) // 仅当isNextTyped值变化时才触发动画,不影响其他控件状态变更 .animation(.easeIn(duration: 0.2), value: isNextTyped.wrappedValue) }) .padding(paddingOfBox) } }
适配说明
该写法完全兼容Xcode12.3版本,动画仅作用于下划线颜色过渡,不会影响数字更新、TextField布局等其他逻辑,不会出现数字不同步、下划线滞后的问题。
内容的提问来源于stack exchange,提问作者Dreiohc
相关产品推荐
相关产品推荐

