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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 18:24:03