You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI报错:未找到ShortcutsViewModel类型的ObservableObject

问题

重构代码时,原本存在多个不相关的ShortcutsViewModel实例,尝试统一规范处理后遇到致命错误:

Thread 1: Fatal error: No ObservableObject of type ShortcutsViewModel found. A View.environmentObject(_:) for ShortcutsViewModel may be missing as an ancestor of this view.

补充说明:ContentView属于个人开发的包,已导入项目中。

相关代码片段

主视图

WidgetContentView(
    widgetView: WidgetViewShowing(
        maxOfItemsPerPage: 1,
        numberOfWidgetRows: 1
    ),
    viewModel: ShortcutsViewModel(
        widgetNamePrefix: "smallWidget",
        contract: ShortcutWidgetManager()
    )
)

创建StateObject的视图

public struct WidgetContentView<WidgetViewShowing: View>: View {
    private var widgetView: WidgetViewShowing
    private var total: Int {
        viewModel.shortcutsViewData?.count ?? 0
    }    
    @StateObject public var viewModel: ShortcutsViewModel
        
    public init(
        viewModel: ShortcutsViewModel
    ) {
        _viewModel = StateObject(wrappedValue: viewModel)
    }
    
    public var body: some View {
        ZStack {
            HStack(content: {    
                widgetView
                    .environmentObject(viewModel)
            })
        }
    }
}

报错视图(使用EnvironmentObject的位置)

public struct WidgetViewShowing: View {
    @EnvironmentObject var viewModel: ShortcutsViewModel
    
    var shortcutsViewDataList: [ShortcutViewData]? {
        viewModel.shortcutsViewData // 报错发生在此处
    }
}
错误原因

核心问题是视图初始化时机不匹配:你在主视图中提前创建了WidgetViewShowing实例并传入WidgetContentView,但此时WidgetContentView的@StateObject viewModel还未完成初始化,更没有将其注入到视图环境中。当WidgetViewShowing被创建时,它会立即尝试读取@EnvironmentObject,但环境中根本不存在对应的ShortcutsViewModel实例,因此触发致命错误。

修复方案

方案1:通过闭包延迟创建子视图

修改WidgetContentView,让它接收一个创建WidgetViewShowing的闭包,确保子视图在body渲染时才被创建,此时viewModel已经被注入到环境中。

修改后的WidgetContentView代码:

public struct WidgetContentView<WidgetViewShowing: View>: View {
    private let makeWidgetView: () -> WidgetViewShowing
    private var total: Int {
        viewModel.shortcutsViewData?.count ?? 0
    }    
    @StateObject public var viewModel: ShortcutsViewModel
        
    public init(
        viewModel: ShortcutsViewModel,
        @ViewBuilder makeWidgetView: @escaping () -> WidgetViewShowing
    ) {
        _viewModel = StateObject(wrappedValue: viewModel)
        self.makeWidgetView = makeWidgetView
    }
    
    public var body: some View {
        ZStack {
            HStack {    
                makeWidgetView()
                    .environmentObject(viewModel)
            }
        }
    }
}

主视图调用方式更新为:

WidgetContentView(
    viewModel: ShortcutsViewModel(
        widgetNamePrefix: "smallWidget",
        contract: ShortcutWidgetManager()
    )
) {
    WidgetViewShowing(
        maxOfItemsPerPage: 1,
        numberOfWidgetRows: 1
    )
}

方案2:直接传递ViewModel(替代EnvironmentObject)

如果不需要依赖环境传递,可以直接将viewModel作为参数传入WidgetViewShowing,避免环境依赖的问题。

修改WidgetViewShowing:

public struct WidgetViewShowing: View {
    let viewModel: ShortcutsViewModel
    let maxOfItemsPerPage: Int
    let numberOfWidgetRows: Int
    
    var shortcutsViewDataList: [ShortcutViewData]? {
        viewModel.shortcutsViewData
    }
    
    public init(
        maxOfItemsPerPage: Int,
        numberOfWidgetRows: Int,
        viewModel: ShortcutsViewModel
    ) {
        self.maxOfItemsPerPage = maxOfItemsPerPage
        self.numberOfWidgetRows = numberOfWidgetRows
        self.viewModel = viewModel
    }
}

修改WidgetContentView的初始化和body:

public struct WidgetContentView<WidgetViewShowing: View>: View {
    private let makeWidgetView: (ShortcutsViewModel) -> WidgetViewShowing
    private var total: Int {
        viewModel.shortcutsViewData?.count ?? 0
    }    
    @StateObject public var viewModel: ShortcutsViewModel
        
    public init(
        viewModel: ShortcutsViewModel,
        @ViewBuilder makeWidgetView: @escaping (ShortcutsViewModel) -> WidgetViewShowing
    ) {
        _viewModel = StateObject(wrappedValue: viewModel)
        self.makeWidgetView = makeWidgetView
    }
    
    public var body: some View {
        ZStack {
            HStack {    
                makeWidgetView(viewModel)
            }
        }
    }
}

主视图调用方式:

WidgetContentView(
    viewModel: ShortcutsViewModel(
        widgetNamePrefix: "smallWidget",
        contract: ShortcutWidgetManager()
    )
) { viewModel in
    WidgetViewShowing(
        maxOfItemsPerPage: 1,
        numberOfWidgetRows: 1,
        viewModel: viewModel
    )
}

内容的提问来源于stack exchange,提问作者izmatlDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 22:55:07