从视图外的函数访问@StateObject

Access @StateObject from function outside of view

我有一个 class (WatchlistClass),它符合“ObservableObject”协议并包含一个@Published var(监视列表)。 var 又包含一整套股票和一些信息 ([StockInformation]),这些信息应该用当前的股票价格和其他信息更新我的视图 (WatchlistView)。该部分工作得很好,但 class 应该访问视图中的 @StateObject 以更改数据。在 class 中直接访问它是行不通的,因为它不从 @StateObject 读取数据。我试图直接访问视图中的@StateObject,但这也创建了一个具有空数组的 class 的新实例。在 @Published var 上使用“static”会引发错误。在我的一生中,我无法弄清楚如何直接在视图内访问 @StateObject 以读取和修改它包含的数据。任何帮助将不胜感激。

class WatchlistClass: ObservableObject {
    static let shared = WatchlistClass()
    
    @Published var watchlist: [StockInformation] = []
    
    struct StockInformation: Identifiable {
        ...
    }
    

    func WatchlistTasksGetFundamentalsDelegate(string: String) {
        ...            
            DispatchQueue.main.async {
                self.watchlist.append(stockInformation) // This works and updates the view as expected
            }
        ...
    }


    private let timer = Timer.scheduledTimer(withTimeInterval: 4.0, repeats: true) { _ in
        if self.watchlist.isEmpty == false { // This does not work
            ...
    }
}
struct WatchlistView: View {
    @StateObject var watchlistClass = WatchlistClass()
    ...
}

使用单例模式时,您通常希望在对象上声明 init 为私有对象,这样您就可以确定只能创建它的一个实例。

然后,要访问该单例版本,您将使用 WatchlistClass.shared:

class WatchlistClass: ObservableObject {
    static let shared = WatchlistClass()
    
    @Published var watchlist: [StockInformation] = []
    
    struct StockInformation: Identifiable {
        //...
    }
    
    private init() { }
}

struct WatchlistView: View {
    @StateObject var watchlistClass = WatchlistClass.shared
    
    var body: some View {
        //...
    }
}

就您的 Timer 而言,在此处获得有关其服务目的的更多信息会很有帮助。例如,如果它只是检查 watchlist 以对其做出反应,我的建议是使用 Combine 来观看它而不是 Timer.

如果单例版本的目的只是尝试访问 Timer 并使其能够访问数组,也许像这样的模式会更好:

import Combine

class WatchlistClass: ObservableObject {
    
    @Published var watchlist: [StockInformation] = []
    
    private var cancellable : AnyCancellable?
    
    struct StockInformation: Identifiable {
        var id = UUID()
        //...
    }
    
    public init() {
        cancellable = Timer.publish(every: 1, on: .main, in: .default)
            .autoconnect()
            .receive(on: RunLoop.main)
            .sink { _ in
                if !self.watchlist.isEmpty {
                    
                }
            }
    }
}

struct WatchlistView: View {
    @StateObject var watchlistClass = WatchlistClass()
    
    var body: some View {
        Text("Hello, world!")
    }
}

这将在 init 上创建 Timer 并授予它访问实例的 watchlist 数组的权限。

你的 timer 是一个实例变量,但它的闭包不是 class 的实例并且没有 self.

您将不得不做一些事情来将“self”放入计时器块的范围内。一种方法是在实例的成员函数中创建计时器:

private func makeTimer() -> Timer {
    return Timer.scheduledTimer(withTimeInterval: 4.0, repeats: true) { [weak self] _ in
        if let empty = self?.watchlist.isEmpty,
            empty == false {
        }
    }
}

当您调用 makeTimer() 时,您的代码将在实例的上下文中执行。它将可以访问 self.

请注意,我已经更改了您的块,以便它捕获 self“弱”,因为计时器可能存在于对象的生命周期之外,因此您必须积极应对这种可能性。

您可以从初始化程序中调用 makeTimer。