在 SwiftUI 视图中组合 onChange 和 onAppear 事件?

Tru*_*an1 3 ios swift swiftui combine

我正在使用onChange修饰符观察视图上的属性。但是,我也希望在初始值上运行同一段代码,因为有时数据会被注入初始化程序或稍后异步加载。

例如,我有一个注入模型的视图。有时,此模型中包含数据(如预览),或者从网络异步检索。

class MyModel: ObservableObject {
    @Published var counter = 0
}

struct ContentView: View {
    @ObservedObject var model: MyModel
    
    var body: some View {
        VStack {
            Text("Counter: \(model.counter)")
            Button("Increment") { model.counter += 1 }
        }
        .onChange(of: model.counter, perform: someLogic)
        .onAppear { someLogic(counter: model.counter) }
    }
    
    private func someLogic(counter: Int) {
        print("onAppear: \(counter)")
    }
}
Run Code Online (Sandbox Code Playgroud)

在这两个onAppear和onChange的情况下,我想运行someLogic(counter:)。有没有更好的方法来获得这种行为或将它们结合起来?

paw*_*222 5

看起来onReceive可能正是您所需要的。代替:

.onChange(of: model.counter, perform: someLogic)
.onAppear { someLogic(counter: model.counter) }
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

.onReceive(model.$counter, perform: someLogic)
Run Code Online (Sandbox Code Playgroud)

onChange和之间的区别在于onReceive后者也会在视图初始化时触发。


onChange

如果您仔细查看onChange,您会发现它仅在值更改时执行操作(并且在初始化视图时不会发生这种情况)。

/// Adds a modifier for this view that fires an action when a specific
/// value changes.
/// ...
@inlinable public func onChange<V>(of value: V, perform action: @escaping (V) -> Void) -> some View where V : Equatable
Run Code Online (Sandbox Code Playgroud)

接收

但是,计数器的发布者也会在初始化视图时发出该值。这将使onReceive执行作为参数传递的操作。

/// Adds an action to perform when this view detects data emitted by the
/// given publisher.
/// ...
@inlinable public func onReceive<P>(_ publisher: P, perform action: @escaping (P.Output) -> Void) -> some View where P : Publisher, P.Failure == Never
Run Code Online (Sandbox Code Playgroud)

只需要注意onReceive是不是一个等同的onChange+ onAppear。

onAppear在视图出现时调用,但在某些情况下,视图可能会在不触发的情况下再次初始化onAppear。