如何在Observable类型中使用SwiftUI的FocusState?
在SwiftUI Observable类型中管理FocusState的解决方案
由于@FocusState是SwiftUI视图专属的属性包装器,无法直接在@Observable类中使用,但可以通过以下两种方式将焦点控制逻辑迁移到Observable对象中,实现业务逻辑与视图的分离:
方案一:将FocusState的Binding传递给Observable方法
直接在Observable类的验证方法中接收FocusState的绑定对象,完成焦点设置:
修改后的FormState类
@Observable class FormState { var name = "" var answer = "" var showAlert = false var alertMessage = "" // 接收FocusState的Binding参数,在方法内控制焦点 func validateForm(focus: Binding<Field?>) -> Bool { if name.isEmpty { focus.wrappedValue = .name alertMessage = "请输入你的名字。" showAlert = true return false } else if answer.isEmpty { focus.wrappedValue = .answer alertMessage = "请给出你的答案。" showAlert = true return false } else if answer != "Rush" { focus.wrappedValue = .answer alertMessage = "错误!再试一次!\n(提示:不是Metallica)" showAlert = true return false } else { alertMessage = "摇滚起来,\(name)!\n你答对了!" showAlert = true return true } } enum Field: Hashable, Codable { case name, answer } }
修改后的视图代码
struct ObservableVersion: View { @Environment(FormState.self) var state @FocusState private var focus: FormState.Field? var body: some View { Form { @Bindable var state = state TextField("姓名", text: $state.name) .focused($focus, equals: .name) Section("世界上最好的金属乐队是?") { TextField("乐队名称", text: $state.answer) .focused($focus, equals: .answer) } Button("确认") { // 将FocusState的绑定传递给Observable的验证方法 _ = state.validateForm(focus: $focus) } .alert(state.alertMessage, isPresented: $state.showAlert) { Button("确定") {} } } } } #Preview { ObservableVersion() .environment(FormState()) }
方案二:在Observable中维护焦点状态,与View的FocusState双向同步
在Observable类中新增一个存储焦点状态的属性,通过视图的onChange实现与@FocusState的双向同步:
修改后的FormState类
@Observable class FormState { var name = "" var answer = "" var showAlert = false var alertMessage = "" // 在Observable中维护焦点状态 var focusField: Field? = nil enum Field: Hashable, Codable { case name, answer } func validateForm() -> Bool { if name.isEmpty { focusField = .name alertMessage = "请输入你的名字。" showAlert = true return false } else if answer.isEmpty { focusField = .answer alertMessage = "请给出你的答案。" showAlert = true return false } else if answer != "Rush" { focusField = .answer alertMessage = "错误!再试一次!\n(提示:不是Metallica)" showAlert = true return false } else { alertMessage = "摇滚起来,\(name)!\n你答对了!" showAlert = true return true } } }
修改后的视图代码
struct ObservableVersion: View { @Environment(FormState.self) var state @FocusState private var focus: FormState.Field? var body: some View { Form { @Bindable var state = state TextField("姓名", text: $state.name) .focused($focus, equals: .name) Section("世界上最好的金属乐队是?") { TextField("乐队名称", text: $state.answer) .focused($focus, equals: .answer) } Button("确认") { _ = state.validateForm() } .alert(state.alertMessage, isPresented: $state.showAlert) { Button("确定") {} } } // 同步Observable的焦点状态到View的FocusState .onChange(of: state.focusField) { _, newValue in focus = newValue } // 可选:同步View的焦点变化回Observable,确保状态一致 .onChange(of: focus) { _, newValue in state.focusField = newValue } } } #Preview { ObservableVersion() .environment(FormState()) }
方案对比
- 方案一:实现简单,无需额外同步逻辑,适合单个视图控制焦点的场景。
- 方案二:让Observable完全掌控焦点状态,适合需要在多个视图或业务逻辑中控制焦点的复杂场景。
内容的提问来源于stack exchange,提问作者Curious Jorge
相关产品推荐
相关产品推荐

