在 watchOS 中使用 environmentObject

Posted

技术标签:

【中文标题】在 watchOS 中使用 environmentObject【英文标题】:Using environmentObject in watchOS 【发布时间】:2019-06-12 06:15:00 【问题描述】:

我正在尝试在 watchOS6 应用中使用 environmentObject 将我的数据模型绑定到我的视图。

我在 Xcode 11 中创建了一个简单的独立 Watch 应用。

我创建了一个新的DataModel

import Combine
import Foundation
import SwiftUI

final class DataModel: BindableObject 

    let didChange = PassthroughSubject<DataModel,Never>()

    var aString: String = "" 
        didSet 
            didChange.send(self)
        
    


在我的ContentView 结构中,我使用@EnvironmentObject 绑定这个类 -

struct ContentView : View 

    @EnvironmentObject private var dataModel: DataModel

    var body: some View 
        Text($dataModel.aString.value)
    

最后,我尝试将DataModel 的实例注入到HostingController 类中的环境中-

class HostingController : WKHostingController<ContentView> 
    override var body: ContentView 
        return ContentView().environmentObject(DataModel())
    

但是,我得到一个错误:

Cannot convert return expression of type '_ModifiedContent<ContentView, _EnvironmentKeyWritingModifier<DataModel?>>' to return type 'ContentView'

错误是因为WKHostingController 是一个需要具体类型的泛型——在这种情况下为WKHostingController&lt;ContentView&gt;

类似的方法在 ios 应用中与 UIHostingController 完美配合,因为 UIHostingController 不是泛型类。

还有其他方法可以将环境注入到 watchOS 视图中吗?

【问题讨论】:

这能回答你的问题吗? How to inject .environmentObject() in watchOS6 【参考方案1】:

您可以使用类型擦除,AnyView 在 SwiftUI 的情况下为 View

我会重构 WKHostingController 以返回 AnyView

这对我来说似乎编译得很好。

class HostingController : WKHostingController<AnyView> 
    override var body: AnyView 
        return AnyView(ContentView().environmentObject(DataModel()))
    

【讨论】:

谢谢。我试过AnyView,但我没有用过AnyView(ContentView()) 这对我不起作用,我收到以下错误:“'AnyView' 类型的属性'body' 无法覆盖'ContentView' 类型的属性” 几个月后才回到这个问题上,新鲜的眼睛会有所帮助。我在通用协议声明中仍然有 ContentView,更改为 AnyView 并且可以工作。 如何让子 ContentView 在最新的 WatchOS 应用程序模板中访问它?我在我的主要 ContentView 上工作,但是当我使用 NavigationLink 路由到视图时,我无法弄清楚 ContentView 如何访问相同的 @EnvironmentObject 这是一个糟糕的答案,因为AnyView 性能很重。长话短说,将您的数据模型放在内容视图中。 Here's better solution【参考方案2】:

对于像 Brett(在 cmets 中)这样的人来说,

"Property 'body' with type 'AnyView' cannot override a property with type 'ContentView'"

我得到了同样的错误,因为我没有替换返回值并包装了正在返回的 ContentView。

即。这是我第一次尝试的样子..注意 WKHostingController&lt;ContentView&gt; 那应该是 WKHostingController&lt;AnyView&gt;

class HostingController : WKHostingController<ContentView> 
    override var body: AnyView 
        return AnyView(ContentView().environmentObject(DataModel()))
    

【讨论】:

虽然我已经做了那一点,但我没有用 AnyView 替换 WKHostingController 中的 ContentView,这对我有用。感谢您的回复。【参考方案3】:

添加到 Matteo 的精彩答案,

如果你想使用委托,那么这样使用:

class HostingController : WKHostingController<AnyView> 
    override var body: AnyView 
        var contentView = ContentView()
        contentView.environmentObject(DataModel())
        contentView.delegate = self
        let contentWrapperView = AnyView(contentView)
        return contentWrapperView
    

【讨论】:

以上是关于在 watchOS 中使用 environmentObject的主要内容,如果未能解决你的问题,请参考以下文章

watchOS 3 在设备和手表之间共享的类中使用 WCSession

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

如何在 watchOS 5 上使用 WebKit?

在 WatchOS 中使用列表时滚动行为不正确

watchOS 6 WKInterfaceImage 在解除分配时崩溃

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