SwiftUI实现密码显隐切换时保持TextField第一响应状态
SwiftUI 密码框显隐切换时保持焦点与键盘不消失
问题场景
我在登录视图中实现了用户名和密码两个输入框,想要通过点击按钮切换密码框的明文/密文显示状态。当前实现代码如下:
ZStack(alignment: .trailing) { Group { if viewModel.isPasswordVisible { SecureField("", text: $viewModel.text) .keyboardType(.default) .focused($focusField, equals: .secure) } else { TextField("", text: $viewModel.text) .keyboardType(.emailAddress) .focused($focusField, equals: .plain) } } .autocorrectionDisabled() .textInputAutocapitalization(.none) .font(.system(size: Constants.Font.regularSize)) .padding() .padding(.trailing, textFieldPaddingTrailing + numberOfSymbolsInTextField) HStack { if viewModel.isSecureField { Button(action: { viewModel.isPasswordVisible.toggle() }) { Text(Image(systemName: viewModel.isPasswordVisible ? Constants.Image.eye : Constants.Image.eyeSlash)) .font(.system(size: Constants.Font.symbolSize)) .foregroundColor(DMSColor.SwiftUIColor.primaryText) } } } .padding(.trailing) }
显隐切换的逻辑正常,但存在一个问题:切换时键盘会自动隐藏。原因是isPasswordVisible切换时,原来的输入框被销毁,新的输入框重建后没有自动获取焦点,导致键盘收起。
解决方案
核心思路是避免销毁重建输入框,通过修改现有输入框的属性来实现显隐切换,同时确保焦点状态被正确保留。
优化后的代码
ZStack(alignment: .trailing) { // 使用单个TextField,通过修饰符控制密码显隐 TextField("", text: $viewModel.text) .keyboardType(.default) // 统一绑定到同一个焦点标识 .focused($focusField, equals: .password) .autocorrectionDisabled() .textInputAutocapitalization(.none) .font(.system(size: Constants.Font.regularSize)) .padding() .padding(.trailing, textFieldPaddingTrailing + numberOfSymbolsInTextField) // 控制密码是否隐藏:isPasswordVisible为true时显示明文,所以传入!isPasswordVisible .secure(!viewModel.isPasswordVisible) // 可选:根据显隐状态调整文本内容类型,适配系统行为 .textContentType(viewModel.isPasswordVisible ? .username : .password) if viewModel.isSecureField { Button(action: { withAnimation { viewModel.isPasswordVisible.toggle() } // 切换后强制重新设置焦点,确保输入框保持第一响应 DispatchQueue.main.async { focusField = .password } }) { Text(Image(systemName: viewModel.isPasswordVisible ? Constants.Image.eye : Constants.Image.eyeSlash)) .font(.system(size: Constants.Font.symbolSize)) .foregroundColor(DMSColor.SwiftUIColor.primaryText) } .padding(.trailing) } }
关键修改说明
- 替换输入框切换逻辑:不再用
if-else创建两个不同的TextField/SecureField,而是使用单个TextField配合secure(_:)修饰符控制密码显隐,这样视图不会被销毁重建,只是修改属性。 - 统一焦点绑定:将焦点绑定到同一个标识(比如
.password),避免切换时焦点丢失。 - 强制恢复焦点:在按钮点击事件中,通过
DispatchQueue.main.async延迟设置焦点,确保视图更新后输入框能重新获得第一响应状态,保持键盘不收起。
内容的提问来源于stack exchange,提问作者Shawn Frank
相关产品推荐
相关产品推荐

