SwiftUI:密码输入框浮动占位符点击时未如期触发动画
SwiftUI浮动输入框问题修复与代码审查
问题根源
密码输入框点击时占位符无动画的核心原因:
SecureField未实现onEditingChanged回调,导致输入框获取焦点时isActive状态未更新,只有输入内容改变text时才会间接触发状态变化。- 代码存在变量名逻辑混淆:
CustomTextView的isActive参数实际控制占位符位置,但传入条件(!self.isActive && self.text.isEmpty)易引发逻辑误解。
修复方案
1. 给SecureField添加状态更新回调
在SecureField中添加与TextField一致的onEditingChanged处理,确保点击输入框时立即更新激活状态:
SecureField("", text: $text, onEditingChanged: { editing in self.isActive = editing print("----FieldActive \(self.isActive)") }, onCommit: {})
2. 修正逻辑混淆与变量命名
将CustomTextView的isActive参数重命名为isPlaceholderInField,让逻辑更直观:
- 输入框未激活且文本为空时,占位符显示在输入框内(
isPlaceholderInField = true) - 输入框激活或文本不为空时,占位符浮动到上方(
isPlaceholderInField = false)
3. 修复图标参数错误
左侧图标处的ImageView(name: rightIcon)改为ImageView(name: leftIcon),避免图标显示错误。
4. 用枚举替代字符串类型判断
将type的字符串参数改为枚举,避免硬编码拼写错误:
enum TextFieldType: String { case email, password }
完整修正后的代码
FloatingTextFieldView.swift
import SwiftUI @available(iOS 15.0, *) struct FloatingTextFieldView: View { var type: TextFieldType var placeholder: String var rightIcon: String? var leftIcon: String? @Binding var text: String var onTap: () -> Void = {} private enum Field: Int, Hashable { case fieldName } @State private var isActive: Bool = false @FocusState private var focusField: Field? var body: some View { VStack(alignment: .leading) { ZStack(alignment: .leading) { HStack { if let leftIcon = leftIcon { ImageView(name: leftIcon) .onTapGesture { print("----Left Icon Tapped with \(text)") } } if type == .password { SecureField("", text: $text, onEditingChanged: { editing in self.isActive = editing print("----FieldActive \(self.isActive)") }, onCommit: {}) .padding(.leading, leftIcon == nil ? 0 : 8) .padding(.trailing, rightIcon == nil ? 0 : 8) .focused($focusField, equals: .fieldName) } else { TextField("", text: $text, onEditingChanged: { editing in self.isActive = editing print("----FieldActive \(self.isActive)") }) { } .padding(.leading, leftIcon == nil ? 0 : 8) .padding(.trailing, rightIcon == nil ? 0 : 8) .focused($focusField, equals: .fieldName) } if let rightIcon = rightIcon { ImageView(name: rightIcon) .onTapGesture { print("----Right Icon Tapped with \(text)") } } } .padding() .overlay( RoundedRectangle(cornerRadius: 5) .stroke(Color.gray, lineWidth: 1) ) CustomTextView( placeholder: placeholder, color: .red, backgroundColor: .white, callback: {}, isPlaceholderInField: !isActive && text.isEmpty ) } } .padding() } } enum TextFieldType: String { case email, password }
CustomTextView.swift
import SwiftUI struct CustomTextView: View { var placeholder: String? = "" var color: Color var backgroundColor: Color var callback: ()->Void var isPlaceholderInField: Bool var body: some View { Text(placeholder ?? "placeholder") .foregroundColor(color) .padding(.horizontal, 10) .background(backgroundColor) .offset(x: isPlaceholderInField ? 10 : 20, y: isPlaceholderInField ? -35 : 0) .scaleEffect(isPlaceholderInField ? 0.8 : 1, anchor: .leading) .animation(.easeInOut(duration: 0.4), value: isPlaceholderInField) .onTapGesture { callback() } } }
额外优化点
- 修正
CutomTextView的拼写错误为CustomTextView,消除编译问题。 - 给左右图标点击的打印信息添加区分,方便调试。
- 让
CustomTextView的点击手势调用传入的callback,增强组件交互性。 - 使用枚举
TextFieldType替代字符串判断,提升代码类型安全性与可读性。
内容的提问来源于stack exchange,提问作者Nicolas
相关产品推荐
相关产品推荐

