WatchOS 在条件视图中使用 ObservableObject 导致运行时错误

Posted

技术标签:

【中文标题】WatchOS 在条件视图中使用 ObservableObject 导致运行时错误【英文标题】:WatchOS Using ObservableObject in Conditional in View Causing Runtime Error 【发布时间】:2021-11-18 16:10:50 【问题描述】:

我使用ObservableObject 来保持用户是否订阅我的应用的状态,并根据订阅状态显示不同的视图。这在 Xcode 13 和 WatchOS 8 之前运行良好,但现在这会导致运行时错误 runtime: SwiftUI: Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update. 并且,绑定不会根据错误更新。这发生在 Xcode 13.1 和 13.2b2

下面的代码重现了错误:

struct MultiPageView: View 
    @ObservedObject var subscribed = SubscribedModel.shared
    
    var body: some View 
        if subscribed.value 
            TabView 
                ViewOne()
                ViewTwo()
                ViewThree()
                ToggleView()
            
            .tabViewStyle(PageTabViewStyle())
         else 
            TabView 
                ViewOne()
                ToggleView()
            
            .tabViewStyle(PageTabViewStyle())
        
    


struct ToggleView: View 
    @ObservedObject var subscribed = SubscribedModel()

    var body: some View 
        Toggle(isOn: $subscribed.value) 
            Text("Subscribed")
        
    


class SubscribedModel: ObservableObject 
    public static let shared = SubscribedModel.shared
    
    @Published var value: Bool = false

为简洁起见,我只列出ViewOne,但ViewTwoViewThree 相同,但文字不同:

struct ViewOne: View 
    var body: some View 
        Text("View One")
            .padding()
    

如果您导航到ToggleView(),并切换开关,错误会立即弹出。有什么建议可以解决这个问题吗?

根据@LoremIpsum 评论更新:

struct MultiPageView: View 
    @StateObject var subscribed = SubscribedModel()
    
    var body: some View 
        if subscribed.value 
            TabView 
                ViewOne()
                ViewTwo()
                ViewThree()
                ToggleView(subscribed: $subscribed.value)
            
            .tabViewStyle(PageTabViewStyle())
         else 
            TabView 
                ViewOne()
                ToggleView(subscribed: $subscribed.value)
            
            .tabViewStyle(PageTabViewStyle())
        
    


struct ToggleView: View 
    @Binding var subscribed: Bool

    var body: some View 
        Toggle(isOn: $subscribed) 
            Text("Subscribed")
        
    

它现在正在TabViews 之间切换,但错误仍然存​​在,并且会立即出现。删除了 DerivedData 并清理了构建文件夹。有什么想法吗?

我将补充一点,相同的基本代码在 ios 15 上运行良好。只是 WatchOS 弹出错误。

【问题讨论】:

切换模式与多页面视图不同。此外,如果您在视图中初始化观察到的对象,则应该使用状态对象。但在这种情况下,只是不要在切换中初始化,从多页面视图中获取它作为参数 我稍微更新了代码,因为我使用的是 .shared 单例,而不是新实例。但是,我根据您的建议对其进行了更改,并且只有轻微的改进。错误仍然存​​在。 哪一行出现错误? 那是疯狂的部分。它不与实际项目或 MRE 的任何行相关联。我有一个线程跟踪,但我在符号化它时遇到了问题。我提出了一个关于使用 otool 获取 ASLR 幻灯片的问题,但没有得到任何回应。我通过消除过程走到了这一步,MRE 确认​​这与视图中的条件在视图之外被更改有关。 Runtime Error 断点在@main 处无用地停止,并且跟踪没有帮助。 【参考方案1】:

我很长一段时间都遇到同样的问题,并且在 Xcode 13.2.1 上仍然发生这种情况。

似乎是 watchOS 上的 TabView 存在问题,因为如果您将 TabView 替换为另一个 View,错误就会消失。

解决方案是使用TabView 的初始化程序,其值为selection:init(selection:content:)

1 定义一个属性供选择

@State private var selection = 0

2 更新 TabView

来自

TabView 
    // content

TabView(selection: $selection) 
    // content


更新您的代码如下所示:

struct MultiPageView: View 
    @StateObject var subscribed = SubscribedModel()
    @State private var selection = 0
    
    var body: some View 
        if subscribed.value 
            TabView(selection: $selection) 
                ViewOne()
                ViewTwo()
                ViewThree()
                ToggleView(subscribed: $subscribed.value)
            
            .tabViewStyle(PageTabViewStyle())
         else 
            TabView(selection: $selection) 
                ViewOne()
                ToggleView(subscribed: $subscribed.value)
            
            .tabViewStyle(PageTabViewStyle())
        
    

基本上只是为TabView.selection 定义一个@State 属性,并在两个TabViews 上使用它(使用单独的属性也可以)。

【讨论】:

你是怎么遇到这个解决方案的?奇迹般有效!我会把它拿回来看看它在生产应用程序中是如何工作的,但是谢谢! 天才,错误消失了。感谢您的解决方案

以上是关于WatchOS 在条件视图中使用 ObservableObject 导致运行时错误的主要内容,如果未能解决你的问题,请参考以下文章

如何在 WatchOS 中使用背景视图填充整个 Button 区域?

在 WatchOS 上启动视图

在 watchOS 中,是不是可以显示没有通向根视图的状态栏按钮的视图?

如何在 watchOS 上像文本视图一样显示文本

watchOS 应用程序在显示睡眠后重置为根视图控制器

WatchOS SwiftUI 动画视图调整父级大小