SwiftUI与Combine中无法将AnyPublisher<Bool, Never>绑定到@State变量的问题求助
SwiftUI与Combine中无法将AnyPublisher<Bool, Never>绑定到@State变量的问题求助
大家好,我最近在SwiftUI结合Combine开发时碰到了一个头疼的问题,折腾了好一阵都没解决,想请各位大佬帮忙看看。
为了方便复现问题,我简化了自己的项目代码,先给大家看下我的数据结构:
struct Item: Identifiable { let id = UUID() let isChecked: AnyPublisher<Bool, Never> let name: String }
然后我写了一个ItemCell视图,本意是当isChecked这个Publisher发布新值时,视图能自动更新状态:
struct ItemCell: View { var item: Item @State var imgState: Bool = false var cancellables = Set<AnyCancellable>() init(_ item: Item) { self.item = item item.isChecked .print("State for item \(item.name): ") // 这里能看到状态输出 .assign(to: \.imgState, on: self) .store(in: &cancellables) } var body: some View { HStack { Image(systemName: imgState ? "checkmark.circle.fill" : "circle") Text(item.name) } } }
现在的问题是,虽然控制台的print语句能正常输出isChecked的状态变化,但视图里的图标就是不会跟着更新。我试了好几种方式都没搞定,想问问大家这到底是哪里出问题了?有没有靠谱的解决办法?
补充:后来我整理了几种可行的修改方案,给大家参考下:
方案一:用SwiftUI自带的onReceive修饰符
把订阅逻辑移到body里用onReceive处理,这样就不用手动管理订阅的生命周期了:
struct ItemCell: View { var item: Item @State private var imgState: Bool = false var body: some View { HStack { Image(systemName: imgState ? "checkmark.circle.fill" : "circle") Text(item.name) } .onReceive(item.isChecked) { newValue in print("State for item \(item.name): \(newValue)") imgState = newValue } } }
这个方案亲测有效,onReceive会自动帮我们处理订阅的创建和销毁,完全符合SwiftUI的视图生命周期逻辑。
方案二:用@StateObject管理订阅
如果还是想保留assign的方式,可以把订阅逻辑放到一个ObservableObject里,用@StateObject来持有它,避免视图重建时订阅丢失:
class CellViewModel: ObservableObject { @Published var imgState: Bool = false private var cancellables = Set<AnyCancellable>() init(item: Item) { item.isChecked .print("State for item \(item.name): ") .assign(to: \.imgState, on: self) .store(in: &cancellables) } } struct ItemCell: View { var item: Item @StateObject private var viewModel: CellViewModel init(_ item: Item) { self.item = item self._viewModel = StateObject(wrappedValue: CellViewModel(item: item)) } var body: some View { HStack { Image(systemName: viewModel.imgState ? "checkmark.circle.fill" : "circle") Text(item.name) } } }
这个方案也能解决问题,因为@StateObject会由SwiftUI管理生命周期,不会随着视图重建而被销毁,订阅能一直保持有效。
后来我才明白,原来的问题根源在于:
- 原代码里的
cancellables是结构体的存储属性,SwiftUI的视图结构体在状态变化时会被重新创建,导致旧的订阅被取消,新订阅虽然会生成,但容易出现状态不同步的情况; - 直接在
init里给@State变量用assign赋值,绕过了SwiftUI的状态管理机制,视图无法正确感知到状态变化。
如果大家有其他更好的思路,也欢迎留言交流!
备注:内容来源于stack exchange,提问作者Mahi Al Jawad
相关产品推荐
相关产品推荐

