SwiftUI 视图中的 onReceive 导致无限循环

Posted

技术标签:

【中文标题】SwiftUI 视图中的 onReceive 导致无限循环【英文标题】:onReceive in SwiftUI view causes infinite loop 【发布时间】:2020-05-07 21:52:26 【问题描述】:

在一个 SwiftUI 应用程序中,我有一个 ObservableObject 来跟踪用户设置:

class UserSettings: ObservableObject 
    @Published var setting: String?

我有一个视图模型来控制我的视图的状态:

class TestViewModel: ObservableObject 
    @Published var state: String = ""

我有我的看法。当用户设置改变时,我想让视图模型更新视图的状态:

struct HomeView: View 
    @EnvironmentObject var userSettings: UserSettings
    @ObservedObject var viewModel = TestViewModel()

    var body: some View 
        Text(viewModel.state)
            .onReceive(userSettings.$setting)  setting in
                self.viewModel.state = setting
            
    

UserSettings.setting 在另一个视图中更改时,它会导致我视图上的onReceive 在无限循环中被调用,我不明白为什么。我看到了this question,这个循环对我来说很有意义,因为观察到的ObservableObject 的状态会随着观察而改变。

但是,在我的情况下,我没有更改观察到的对象(环境对象)状态。我正在观察环境对象并更改重绘视图的视图模型状态。

视图是否重绘了导致问题的原因?每次重绘视图时都会调用onReceive 吗?

有没有更好的方法来完成我想做的事情?

编辑:这是我的问题的一个大大简化的版本。在我的应用程序中,视图模型负责根据用户的设置执行网络请求并更新视图的状态,例如显示错误消息或加载指示器。

【问题讨论】:

【参考方案1】:

从所描述的场景中,我看不到在视图模型中重复设置的原因。可以直接从userSettings显示值,如

struct HomeView: View 
    @EnvironmentObject var userSettings: UserSettings
    @ObservedObject var viewModel = TestViewModel()

    var body: some View 
        Text(userSettings.setting)
    

【讨论】:

感谢您抽出宝贵时间回答,但正如我在对问题的编辑中解释的那样,我确实需要设置 viewModel。在我的应用程序中,我需要调用视图模型中的一个函数来执行网络请求,并且视图模型会在请求执行时设置视图的状态(例如显示错误消息、加载指示器等) 你能想到为什么会发生循环行为吗?这对我来说是真正令人头疼的事情。 @gnarlybracket,关于简化问题的简化答案,因为我没有看到你需要自己推断的真实代码 - 逻辑中的循环,而不是 swiftui - 解决方案,如图所示,打破它。【参考方案2】:

每当您有一个带有 @ObservedObject 的 onReceive 设置另一个(或相同)@ObservedObject 的已发布值时,如果这些已发布的属性以某种方式显示,您就有创建无限循环的风险。

让你的 onReceive 验证接收到的值实际上是在更新一个值,而不仅仅是设置相同的值,否则它将无限设置/重绘。在这种情况下,例如:

.onReceive(userSettings.$setting)  setting in
          if setting != self.viewModel.state 
            self.viewModel.state = setting
          
      

【讨论】:

谢谢!!!这实际上是唯一让我的代码正常工作的事情。你是个传奇!!【参考方案3】:

您可以通过将@ObservedObject 切换为@StateObject 来防止无限地重新渲染视图主体。

【讨论】:

以上是关于SwiftUI 视图中的 onReceive 导致无限循环的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI视图onReceive方法接收“冗余”事件的解决

当 ObservedObject 更改时,在 SwiftUI 视图中未调用 onReceive

如何在 onReceive 计时器关闭 SwiftUI iOS 中导航另一个视图

SwiftUI onReceive 不适用于 UIPasteboard 发布者

SwiftUI:输入字段键盘动画导致选项卡视图中的其他视图动画

当 Publisher 参数没有要发出的任何新值时,为啥会调用 SwiftUI View 上的 onReceive 块?