如何在SwiftUI视图模型(view model)中使用@FocusState?
解决方案
@FocusState 是SwiftUI专属的视图层属性包装器,不支持直接定义在ObservableObject类型的视图模型中,我们可以通过双向绑定桥接的方式,把焦点状态的控制权托管给视图模型。
1. 改造视图模型
在ViewModel中新增@Published修饰的焦点状态属性,以及对应的业务操作方法,所有焦点相关的逻辑判断都可以统一放在VM中处理:
import Combine import SwiftUI class ViewModel: ObservableObject { @Published var textField: String = "" // 托管的焦点状态 @Published var isTextFieldFocused: Bool = false func activateTextFieldFocus() { // 可在此处添加额外业务校验,比如符合条件才允许聚焦 isTextFieldFocused = true } func deactivateTextFieldFocus() { isTextFieldFocused = false } }
2. 视图层做绑定桥接
视图层保留@FocusState,直接和ViewModel的焦点属性做双向绑定即可:
struct ContentView: View { @ObservedObject private var viewModel = ViewModel() @FocusState private var hasFocus: Bool var body: some View { Form { TextField("Text", text: $viewModel.textField) .focused($hasFocus) // 双向绑定VM的焦点属性和视图的FocusState .onChange(of: viewModel.isTextFieldFocused) { newValue in hasFocus = newValue } .onChange(of: hasFocus) { newValue in viewModel.isTextFieldFocused = newValue } Button("Set Focus") { // 调用VM的方法触发焦点变化,业务逻辑完全收敛到VM内 viewModel.activateTextFieldFocus() } } } }
如果需要全局复用绑定逻辑,可以提取通用View扩展:
extension View { func sync<T: Equatable>(_ published: Binding<T>, with focusState: Binding<T>) -> some View { self .onChange(of: published.wrappedValue) { focusState.wrappedValue = $0 } .onChange(of: focusState.wrappedValue) { published.wrappedValue = $0 } } } // 使用时直接调用即可:.sync($viewModel.isTextFieldFocused, with: $hasFocus)
多输入框场景扩展
如果是多字段的复杂表单,可以用枚举管理所有可聚焦的字段,更适合复杂的焦点跳转逻辑:
// 定义所有可聚焦的表单字段 enum FormField: Hashable { case username case password case verificationCode } // ViewModel中对应修改属性 @Published var focusedField: FormField? = nil // 视图层FocusState对应修改类型 @FocusState private var focusedField: FormField?
绑定逻辑和单字段完全一致,所有焦点跳转逻辑(比如输入完用户名自动跳到密码框)都可以放到ViewModel中处理。
内容的提问来源于stack exchange,提问作者G. Marc
相关产品推荐
相关产品推荐

