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

Posted

技术标签:

【中文标题】在 SwiftUI 视图中结合 onChange 和 onAppear 事件?【英文标题】:Combine onChange and onAppear events in SwiftUI view? 【发布时间】:2020-10-16 15:23:08 【问题描述】:

我正在使用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)")
    

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

【问题讨论】:

不确定我是否完全理解您的用例,但我认为您可以使用 .onReceive(model.$counter, perform: someLogic) 【参考方案1】:

看起来onReceive 可能是您需要的。而不是:

.onChange(of: model.counter, perform: someLogic)
.onAppear  someLogic(counter: model.counter) 

你可以这样做:

.onReceive(model.$counter, perform: someLogic)

onChangeonReceive 的区别在于后者在视图初始化时也会触发。


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 的情况下再次初始化。

【讨论】:

谢谢,正是我需要的。而且行为比onAppear好!【参考方案2】:

Apple 理解这一要求,因此他们给了我们task(id:priority:_:),它将在 View 数据结构描述的视图出现在屏幕上以及提供给 id: 的值发生变化时运行操作(它必须是 Equatable )。它的额外好处是,如果值发生更改,操作将被取消,因此下一个操作不会与第一个操作重叠;如果 View 消失,它也会被取消。

示例

...
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.

【讨论】:

以上是关于在 SwiftUI 视图中结合 onChange 和 onAppear 事件?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 工作表中未触发深层链接 onChange

SwiftUI 无法添加 onChange 事件

SwiftUI 2 .onChange() 中的选取器不会更改 UINavigationBar.appearance()

结合 SwiftUI 远程获取数据 - ObjectBinding 不更新视图

SwiftUI 将几何阅读器与 ForEach 结合使用

SwiftUI 系列教程—— 与 UIKit 结合的自定义视图