ObservableObject 内部的 ObservedObject 不刷新视图
ObservedObject inside ObservableObject not refreshing View
我正在尝试在执行异步请求时显示 activity 指示符。
我所做的是创建一个 ActivityTracker 对象来跟踪发布者的生命周期。
此 ActivityTracker 是一个 ObservableObject,将存储在也是一个 ObservableObject 的视图模型中。
看来这种设置并没有刷新视图。这是我的代码:
struct ContentView: View {
@ObservedObject var viewModel = ContentViewModel()
var body: some View {
VStack(spacing: 16) {
Text("Counter: \(viewModel.tracker.count)\nPerforming: \(viewModel.tracker.isPerformingActivity ? "true" : "false")")
Button(action: {
_ = request().trackActivity(self.viewModel.tracker).sink { }
}) {
Text("Request")
}
}
}
}
class ContentViewModel: ObservableObject {
@Published var tracker = Publishers.ActivityTracker()
}
private func request() -> AnyPublisher<Void, Never> {
return Just(()).delay(for: 2.0, scheduler: RunLoop.main)
.eraseToAnyPublisher()
}
extension Publishers {
final class ActivityTracker: ObservableObject {
// MARK: Properties
@Published var count: Int = 0
var isPerformingActivity: Bool {
return count > 0
}
private var cancellables: [AnyCancellable] = []
private let counterSubject = CurrentValueSubject<Int, Never>(0)
private let lock: NSRecursiveLock = .init()
init() {
counterSubject.removeDuplicates()
.receive(on: RunLoop.main)
.print()
.sink { [weak self] counter in
self?.count = counter
}
.store(in: &cancellables)
}
// MARK: Private methods
fileprivate func trackActivity<Value, Error: Swift.Error>(
ofPublisher publisher: AnyPublisher<Value, Error>
) {
publisher
.receive(on: RunLoop.main)
.handleEvents(
receiveSubscription: { _ in self.increment() },
receiveOutput: nil,
receiveCompletion: { _ in self.decrement() },
receiveCancel: { self.decrement() },
receiveRequest: nil
)
.print()
.sink(receiveCompletion: { _ in }, receiveValue: { _ in })
.store(in: &cancellables)
}
private func increment() {
lock.lock()
defer { lock.unlock() }
counterSubject.value += 1
}
private func decrement() {
lock.lock()
defer { lock.unlock() }
counterSubject.value -= 1
}
}
}
extension AnyPublisher {
func trackActivity(_ activityTracker: Publishers.ActivityTracker) -> AnyPublisher {
activityTracker.trackActivity(ofPublisher: self)
return self
}
}
我也尝试将我的 ActivityTracker 声明为 @Published
,但结果相同,我的文本没有更新。
请注意,将 activity 跟踪器直接存储在视图中会起作用,但这不是我要找的。
我是不是漏掉了什么?
尚不支持嵌套的 ObservableObjects。
当你想使用这些嵌套对象时,你需要在数据改变时自己通知对象。
希望以下代码可以帮助您解决问题。
首先使用:import Combine
然后声明你的模型和子模型,它们都需要使用@ObservableObject
属性才能工作。 (别忘了 @Published
属性 aswel)
我创建了一个名为 Model 的父模型和两个子模型 Submodel1 和 Submodel2。当您在更改数据时使用父模型时 e.x: model.submodel1.count
,您需要使用通知程序才能让视图自行更新。
AnyCancellables 通知父模型本身,在这种情况下视图将自动更新。
复制代码并自己使用,然后尝试在使用时重新编写代码。希望这对您有所帮助,祝您好运!
class Submodel1: ObservableObject {
@Published var count = 0
}
class Submodel2: ObservableObject {
@Published var count = 0
}
class Model: ObservableObject {
@Published var submodel1 = Submodel1()
@Published var submodel2 = Submodel2()
var anyCancellable: AnyCancellable? = nil
var anyCancellable2: AnyCancellable? = nil
init() {
anyCancellable = submodel1.objectWillChange.sink { [weak self] (_) in
self?.objectWillChange.send()
}
anyCancellable2 = submodel2.objectWillChange.sink { [weak self] (_) in
self?.objectWillChange.send()
}
}
}
当您想使用此 模型 时,只需像正常使用 ObservedObjects 一样使用它。
struct Example: View {
@ObservedObject var obj: Model
var body: some View {
Button(action: {
self.obj.submodel1.count = 123
// If you've build a complex layout and it still won't work, you can always notify the modal by the following line of code:
// self.obj.objectWillChange.send()
}) {
Text("Change me")
}
}
如果你有 collection 的东西,你可以这样做:
import Foundation
import Combine
class Submodel1: ObservableObject {
@Published var count = 0
}
class Submodel2: ObservableObject {
var anyCancellable: [AnyCancellable] = []
@Published var submodels: [Submodel1] = []
init() {
submodels.forEach({ submodel in
anyCancellable.append(submodel.objectWillChange.sink{ [weak self] (_) in
self?.objectWillChange.send()
})
})
}
}
我正在尝试在执行异步请求时显示 activity 指示符。 我所做的是创建一个 ActivityTracker 对象来跟踪发布者的生命周期。 此 ActivityTracker 是一个 ObservableObject,将存储在也是一个 ObservableObject 的视图模型中。
看来这种设置并没有刷新视图。这是我的代码:
struct ContentView: View {
@ObservedObject var viewModel = ContentViewModel()
var body: some View {
VStack(spacing: 16) {
Text("Counter: \(viewModel.tracker.count)\nPerforming: \(viewModel.tracker.isPerformingActivity ? "true" : "false")")
Button(action: {
_ = request().trackActivity(self.viewModel.tracker).sink { }
}) {
Text("Request")
}
}
}
}
class ContentViewModel: ObservableObject {
@Published var tracker = Publishers.ActivityTracker()
}
private func request() -> AnyPublisher<Void, Never> {
return Just(()).delay(for: 2.0, scheduler: RunLoop.main)
.eraseToAnyPublisher()
}
extension Publishers {
final class ActivityTracker: ObservableObject {
// MARK: Properties
@Published var count: Int = 0
var isPerformingActivity: Bool {
return count > 0
}
private var cancellables: [AnyCancellable] = []
private let counterSubject = CurrentValueSubject<Int, Never>(0)
private let lock: NSRecursiveLock = .init()
init() {
counterSubject.removeDuplicates()
.receive(on: RunLoop.main)
.print()
.sink { [weak self] counter in
self?.count = counter
}
.store(in: &cancellables)
}
// MARK: Private methods
fileprivate func trackActivity<Value, Error: Swift.Error>(
ofPublisher publisher: AnyPublisher<Value, Error>
) {
publisher
.receive(on: RunLoop.main)
.handleEvents(
receiveSubscription: { _ in self.increment() },
receiveOutput: nil,
receiveCompletion: { _ in self.decrement() },
receiveCancel: { self.decrement() },
receiveRequest: nil
)
.print()
.sink(receiveCompletion: { _ in }, receiveValue: { _ in })
.store(in: &cancellables)
}
private func increment() {
lock.lock()
defer { lock.unlock() }
counterSubject.value += 1
}
private func decrement() {
lock.lock()
defer { lock.unlock() }
counterSubject.value -= 1
}
}
}
extension AnyPublisher {
func trackActivity(_ activityTracker: Publishers.ActivityTracker) -> AnyPublisher {
activityTracker.trackActivity(ofPublisher: self)
return self
}
}
我也尝试将我的 ActivityTracker 声明为 @Published
,但结果相同,我的文本没有更新。
请注意,将 activity 跟踪器直接存储在视图中会起作用,但这不是我要找的。
我是不是漏掉了什么?
尚不支持嵌套的 ObservableObjects。 当你想使用这些嵌套对象时,你需要在数据改变时自己通知对象。 希望以下代码可以帮助您解决问题。
首先使用:import Combine
然后声明你的模型和子模型,它们都需要使用@ObservableObject
属性才能工作。 (别忘了 @Published
属性 aswel)
我创建了一个名为 Model 的父模型和两个子模型 Submodel1 和 Submodel2。当您在更改数据时使用父模型时 e.x: model.submodel1.count
,您需要使用通知程序才能让视图自行更新。
AnyCancellables 通知父模型本身,在这种情况下视图将自动更新。
复制代码并自己使用,然后尝试在使用时重新编写代码。希望这对您有所帮助,祝您好运!
class Submodel1: ObservableObject {
@Published var count = 0
}
class Submodel2: ObservableObject {
@Published var count = 0
}
class Model: ObservableObject {
@Published var submodel1 = Submodel1()
@Published var submodel2 = Submodel2()
var anyCancellable: AnyCancellable? = nil
var anyCancellable2: AnyCancellable? = nil
init() {
anyCancellable = submodel1.objectWillChange.sink { [weak self] (_) in
self?.objectWillChange.send()
}
anyCancellable2 = submodel2.objectWillChange.sink { [weak self] (_) in
self?.objectWillChange.send()
}
}
}
当您想使用此 模型 时,只需像正常使用 ObservedObjects 一样使用它。
struct Example: View {
@ObservedObject var obj: Model
var body: some View {
Button(action: {
self.obj.submodel1.count = 123
// If you've build a complex layout and it still won't work, you can always notify the modal by the following line of code:
// self.obj.objectWillChange.send()
}) {
Text("Change me")
}
}
如果你有 collection 的东西,你可以这样做:
import Foundation
import Combine
class Submodel1: ObservableObject {
@Published var count = 0
}
class Submodel2: ObservableObject {
var anyCancellable: [AnyCancellable] = []
@Published var submodels: [Submodel1] = []
init() {
submodels.forEach({ submodel in
anyCancellable.append(submodel.objectWillChange.sink{ [weak self] (_) in
self?.objectWillChange.send()
})
})
}
}