You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@FocusState变量变更监听失效,.onChange仅首次触发如何解决?

问题根因
  1. 代码存在绑定错误:两个TextField的.focused(_:equals:)修饰器都绑定了.nameLast枚举值,点击手机号输入框时focusedField不会切换为.phoneNumber,自然不会触发后续的变更回调。
  2. 旧版onChange(of:perform:)的取值逻辑坑:iOS17以下的旧版onChange闭包中,直接读取被监听的focusedField属性拿到的是变更前的旧值,只有闭包传入的参数才是变更后的新值,你原有代码打印的是全局focusedField,会误以为变更没有触发。
  3. 不需要额外维护prevFocusedField状态,iOS17+的新版onChange已经提供了新旧值两个入参,旧版本也可以通过入参与属性值的差值拿到旧焦点。
修复后可运行实现
struct FocusView: View {
    @State private var nameLast = "Ispum"
    @State private var phoneNumber = "+41 79 888 88 88"
    
    enum Field {
        case nameLast
        case phoneNumber
    }
    
    @FocusState private var focusedField: Field?
    
    var body: some View {
        VStack {
            TextField("Last Name", text: $nameLast)
                .focused($focusedField, equals: .nameLast)
            TextField("Phone Number", text: $phoneNumber, prompt: Text(""))
                // 修正绑定的枚举值
                .focused($focusedField, equals: .phoneNumber)
        }
        // iOS17+ 新版onChange,直接获取新旧焦点
        .onChange(of: focusedField) { oldFocus, newFocus in
            // oldFocus即为刚刚失去焦点的输入框,直接对应做校验即可
            switch oldFocus {
            case .nameLast:
                // 执行姓氏校验逻辑
                print("校验姓氏:\(nameLast)")
            case .phoneNumber:
                // 执行手机号校验逻辑
                print("校验手机号:\(phoneNumber)")
            case nil:
                // 首次加载无旧焦点,无需处理
                break
            }
        }
        // 如果需要兼容iOS15/iOS16,用旧版onChange写法
        /*
        .onChange(of: focusedField) { newFocus in
            // 此时focusedField是旧值,newFocus是新值
            switch focusedField {
            case .nameLast:
                print("校验姓氏:\(nameLast)")
            case .phoneNumber:
                print("校验手机号:\(phoneNumber)")
            case nil:
                break
            }
        }
        */
        .textFieldStyle(.roundedBorder)
        .padding()
    }
    
    // 校验逻辑实现示例
    private func validateLastName(_ value: String) {
        // 你的校验逻辑
    }
    
    private func validatePhoneNumber(_ value: String) {
        // 你的校验逻辑
    }
}
更优实现建议

如果校验逻辑和输入框强绑定,也可以单独给每个TextField加失去焦点监听,避免所有校验逻辑堆在同一个onChange里:

  • 对每个输入框单独监听focusedField == 当前枚举值的变化,当值从true变为false时触发校验即可。
  • 如果需要支持用户点击回车提交校验,可额外给TextField加.onSubmit修饰器触发对应校验。

内容的提问来源于stack exchange,提问作者appfrosch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 07:15:04