ObservableObject内部的ObservedObject不刷新视图 [英] ObservedObject inside ObservableObject not refreshing View

查看:80
本文介绍了ObservableObject内部的ObservedObject不刷新视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试执行异步请求时显示活动指示器.我所做的是创建一个ActivityTracker对象,该对象将跟踪发布者的生命周期.此ActivityTracker是一个ObservableObject,将存储在也是ObservableObject的视图模型中.

I'm trying to display an activity indicator when performing an async request. What I did is creating an ActivityTracker object that will track life cycle of a publisher. This ActivityTracker is an ObservableObject and will be stored in the view model which also is an ObservableObject.

似乎这种设置并没有刷新View.这是我的代码:

It seems that this kind of setup isn't refreshing the View. Here's my code:

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 ,但结果相同,我的文本未更新.请注意,将活动跟踪器直接存储在视图中将起作用,但这不是我想要的.

I also tried to declare my ActivityTracker as @Published but same result, my text is not updated. Note that storing the activity tracker directly in the view will work but this is not what I'm looking for.

我在这里错过了什么吗?

Did I miss something here ?

推荐答案

尚不支持嵌套的ObservableObjects.当您要使用这些嵌套对象时,需要在数据更改时自行通知这些对象.希望以下代码可以帮助您解决问题.

Nested ObservableObjects is not supported yet. When you want to use these nested objects, you need to notify the objects by yourself when data got changed. I hope the following code can help you with your problem.

首先使用:导入合并

然后声明您的模型和子模型,它们都需要使用 @ObservableObject 属性来工作.(不要忘记 @Published 属性aswel)

Then declare your model and submodels, they all need to use the @ObservableObject property to work. (Do not forget the @Published property aswel)

我制作了一个名为 Model 的父模型和两个子模型 Submodel1&子模型2 .当您在更改数据e.x时使用父模型: model.submodel1.count 时,您需要使用一个通知程序,以使View自行更新.

I made a parent model named Model and two submodels Submodel1 & Submodel2. When you use the parent model when changing data e.x: model.submodel1.count, you need to use a notifier in order to let the View update itself.

AnyCancellables 会通知父模型本身,在这种情况下,视图将自动更新.

The AnyCancellables notifies the parent model itself, in that case the View will be updated automatically.

复制代码并自己使用,然后在使用时尝试重新制作代码.希望这会有所帮助,祝你好运!

Copy the code and use it by yourself, then try to remake your code while using this. Hope this helps, goodluck!

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的常规用法一样使用它即可.

When you want to use this Model, just use it like normal usage of the 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")
    }
}

这篇关于ObservableObject内部的ObservedObject不刷新视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆