在 SwiftUI 视图中结合 onChange 和 onAppear 事件?
Combine onChange and onAppear events in SwiftUI view?
我在使用 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)")
}
}
在 onAppear
和 onChange
两种情况下,我都想 运行 someLogic(counter:)
。有没有更好的方法来获得这种行为或将它们结合起来?
看起来 onReceive
可能是您需要的。而不是:
.onChange(of: model.counter, perform: someLogic)
.onAppear { someLogic(counter: model.counter) }
你可以这样做:
.onReceive(model.$counter, perform: someLogic)
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
onReceive
但是,计数器的发布者也会在初始化视图时发出该值。这将使 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
请注意 onReceive
是 而不是 等价物 的 onChange
+onAppear
。
onAppear
在视图 出现时被调用 但在某些情况下,视图可能会在不触发 onAppear
.
的情况下再次初始化
Apple 理解这个要求,所以他们给了我们 task(id:priority:_:)
这将 运行 当视图数据结构描述的视图出现在屏幕上时以及提供给 [=12= 的值时的操作] 变化(它必须是 Equatable
)。它的额外好处是,如果值发生变化,操作将被取消,这样下一个操作就不会与第一个操作重叠;如果视图消失,它也会被取消。
例子
...
let number: Int
...
.task(id: number){
print("task") // called both on appear and when changed. If an await method is called, it is cancelled if the number changes or if the View disappears.
someState = await fetchSomething(number)
}
.onAppear {
print("onAppear") // only called during appear.
}
.onChange(of: number) { number in
print("onChange") // only called when number is changed.
}
我在使用 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)")
}
}
在 onAppear
和 onChange
两种情况下,我都想 运行 someLogic(counter:)
。有没有更好的方法来获得这种行为或将它们结合起来?
看起来 onReceive
可能是您需要的。而不是:
.onChange(of: model.counter, perform: someLogic)
.onAppear { someLogic(counter: model.counter) }
你可以这样做:
.onReceive(model.$counter, perform: someLogic)
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
onReceive
但是,计数器的发布者也会在初始化视图时发出该值。这将使 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
请注意 onReceive
是 而不是 等价物 的 onChange
+onAppear
。
onAppear
在视图 出现时被调用 但在某些情况下,视图可能会在不触发 onAppear
.
Apple 理解这个要求,所以他们给了我们 task(id:priority:_:)
这将 运行 当视图数据结构描述的视图出现在屏幕上时以及提供给 [=12= 的值时的操作] 变化(它必须是 Equatable
)。它的额外好处是,如果值发生变化,操作将被取消,这样下一个操作就不会与第一个操作重叠;如果视图消失,它也会被取消。
例子
...
let number: Int
...
.task(id: number){
print("task") // called both on appear and when changed. If an await method is called, it is cancelled if the number changes or if the View disappears.
someState = await fetchSomething(number)
}
.onAppear {
print("onAppear") // only called during appear.
}
.onChange(of: number) { number in
print("onChange") // only called when number is changed.
}