簡體   English   中英

ObservableObject 中的 ObservedObject 不刷新視圖

[英]ObservedObject inside ObservableObject not refreshing View

我試圖在執行異步請求時顯示活動指示器。 我所做的是創建一個 ActivityTracker object 來跟蹤發布者的生命周期。 此 ActivityTracker 是一個 ObservableObject 並將存儲在視圖 model 中,該視圖也是一個 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但結果相同,我的文本沒有更新。 請注意,將活動跟蹤器直接存儲在視圖中會起作用,但這不是我想要的。

我在這里錯過了什么嗎?

尚不支持嵌套的 ObservableObjects。 當您要使用這些嵌套對象時,您需要在數據發生更改時自行通知對象。 我希望以下代碼可以幫助您解決問題。

首先使用: import Combine

然后聲明您的 model 和子模型,它們都需要使用@ObservableObject屬性才能工作。 (不要忘記@Published屬性)

我制作了一個名為Model的父 model 和兩個子模型Submodel1 和 Submodel2 當您在更改數據時使用父 model 時: model.submodel1.count ,您需要使用通知程序以讓視圖自行更新。

AnyCancellables通知父 model 本身,在這種情況下,視圖將自動更新。

復制代碼並自己使用,然后在使用時嘗試重新制作代碼。 希望這有幫助,祝你好運!

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()
        }
    }
}

當你想使用這個Model時,就像正常使用 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")
    }
}

如果你有一堆東西,你可以這樣做:

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()
            })
        })
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM