如何让SwiftUI响应ViewModel的状态变化进行刷新?
在SwiftUI中使用基于Combine CurrentValueSubject的ViewModel
步骤1:让ViewModel适配SwiftUI的状态监听
你的ViewModel需要遵循SwiftUI的ObservableObject协议,这样SwiftUI才能自动感知状态变化。结合已有的CurrentValueSubject,有两种适配方式:
方式A:将CurrentValueSubject同步到@Published属性
保留原有Combine逻辑的同时,通过@Published属性给SwiftUI提供可监听的状态:
import Combine import SwiftUI // 定义状态枚举 enum ViewModelState { case idle case loading case success(data: [String]) case error(message: String) } class MyViewModel: ObservableObject { // 原有状态管理的CurrentValueSubject private let stateSubject = CurrentValueSubject<ViewModelState, Never>(.idle) // SwiftUI可监听的@Published状态 @Published private(set) var state: ViewModelState = .idle private var cancellables = Set<AnyCancellable>() init() { // 订阅stateSubject,同步更新@Published属性 stateSubject .assign(to: \.state, on: self) .store(in: &cancellables) // 触发初始网络请求 fetchData() } func fetchData() { // 模拟网络服务返回的Combine发布者 let networkPublisher = Future<[String], Error> { promise in DispatchQueue.global().asyncAfter(deadline: .now() + 2) { promise(.success(["条目1", "条目2", "条目3"])) } } stateSubject.send(.loading) networkPublisher .receive(on: DispatchQueue.main) .sink(receiveCompletion: { [weak self] completion in switch completion { case .failure(let error): self?.stateSubject.send(.error(message: error.localizedDescription)) case .finished: break } }, receiveValue: { [weak self] data in self?.stateSubject.send(.success(data: data)) }) .store(in: &cancellables) } }
方式B:直接利用CurrentValueSubject触发状态通知
如果不想额外维护@Published属性,可以手动通过objectWillChange发布状态更新信号:
class MyViewModel: ObservableObject { let stateSubject = CurrentValueSubject<ViewModelState, Never>(.idle) private var cancellables = Set<AnyCancellable>() init() { // 当stateSubject有新值时,通知SwiftUI视图更新 stateSubject .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &cancellables) fetchData() } // fetchData方法同方式A }
步骤2:在SwiftUI视图中绑定ViewModel
在SwiftUI视图中通过@StateObject(iOS 14+)或@ObservedObject(iOS 13)持有ViewModel,根据状态渲染UI:
struct MySwiftUIView: View { // iOS 14+推荐用@StateObject,确保ViewModel生命周期与视图绑定 @StateObject private var viewModel = MyViewModel() // iOS 13需用@ObservedObject,需从父视图传入ViewModel实例 // @ObservedObject var viewModel: MyViewModel var body: some View { VStack(spacing: 20) { switch viewModel.state { case .idle: Text("点击按钮加载数据") case .loading: ProgressView("加载中...") case .success(let data): List(data, id: \.self) { item in Text(item) } case .error(let message): VStack { Text("加载失败") Text(message) .foregroundColor(.red) } } Button("重新加载") { viewModel.fetchData() } .disabled(viewModel.state == .loading) } .padding() } }
关键注意事项
@StateObjectvs@ObservedObject:iOS 14及以上优先用@StateObject,它能自动管理ViewModel的生命周期;iOS 13则用@ObservedObject,但需要确保父视图正确传入实例。- 线程安全:网络请求的结果必须切换到主线程(通过
receive(on: DispatchQueue.main)),避免后台线程更新UI状态。 - 内存管理:订阅时使用
[weak self]避免循环引用,用Set<AnyCancellable>管理所有订阅的生命周期。
内容的提问来源于stack exchange,提问作者iOSer
相关产品推荐
相关产品推荐

