更新:
- 这个问题已经解决了**(请参见下面的回答)。正确的方法是通过投影
ObservableObject
来获得Binding
。例如,$options.refreshRate
。
- 这个问题已经解决了**(请参见下面的回答)。正确的方法是通过投影
TLDR版本:
如何获取SwiftUI Picker
(或其他依赖于本地Binding
的API)以立即更新我的ObservedObject
/EnvironmentObject
。
场景:
以下是我在创建的每个SwiftUI应用程序中始终需要做的事情...
- 我总是创建一些类来存储任何用户首选项(让我们称之为
Options
,我将其设置为ObservableObject
。 - 任何需要使用的设置都标记为
@Published
- 任何使用它的视图都会将其作为
@ObservedObject
或@EnvironmentObject
引入,并订阅更改。
这一切都很好地工作。我总是面临的麻烦是如何从用户界面设置这一点。从用户界面,这是我通常在做什么(这应该听起来很正常):
- 我有一些SwiftUI视图,如
OptionsPanel
,它驱动上面的Options
类,并允许用户选择他们的选项。 - 假设我们有一个由枚举定义的选项:
enum RefreshRate {
case low, medium, high
}
当然,我会在SwiftUI中选择Picker
来设置此参数...而Picker
API要求我的选择参数为Binding
。这就是我发现问题的地方...
问题:
为了让Picker
正常工作,我通常会使用一些本地Binding
来实现这个目的。但是,最终,我并不关心这个本地值。我关心的是立即将这个新值广播到应用程序的其他部分。当我选择一个新的刷新率时,我希望 * 立即 * 知道更改的那一刻。ObservableObject
(Options
类)对象很好地完成了这一点。我只是在更新一个本地的Binding
,我需要弄清楚的是如何在每次Picker
的状态改变时立即将其转换为ObservableObject
。
我有一个可行的解决方案......但我不喜欢它。下面是我的非理想解决方案:
非理想解决方案:
解决方案的第一部分实际上很好,但遇到了一个障碍...
在我的SwiftUI视图中,我可以使用另一个初始化器,而不是用@State
设置Binding
的最简单方法...
// Rather than this...
@ObservedObject var options: Options
@State var refreshRate: RefreshRate = .medium
// Do this...
@ObservedObject var options: Options
var refreshRate: Binding<RefreshRate>(
get: { self.options.refreshRate },
set: { self.options.refreshRate = $0 }
)
到目前为止,这是伟大的(理论上)!现在,我的本地Binding
直接链接到ObservableObject
。所有的变化Picker
立即广播到整个应用程序。
但这实际上并不奏效。这就是我必须做一些非常混乱和不理想的事情来让它奏效的地方。
上面的代码产生以下错误:
Cannot use instance member 'options' within property initializer; property initializers run before 'self' is available
这是我的(坏的)变通方法。它起作用了,但是很糟糕...Options
类提供了一个shared
示例作为静态属性,因此,在我的选项面板视图中,我执行以下操作:
@ObservedObject var options: Options = .shared // <-- This is still needed to tell SwiftUI to listen for updates
var refreshRate: Binding<RefreshRate>(
get: { Options.shared.refreshRate },
set: { Options.shared.refreshRate = $0 }
)
在实践中,这实际上在这种情况下是可行的。我并不真的需要有多个示例......只有一个。所以,只要我总是引用那个共享示例,一切都可以工作。但是感觉架构得不好。
那么... * 有人有更好的解决方案吗?* 这似乎是地球上每个应用程序都必须解决的问题,所以似乎一定有人有更好的方法。
(我知道有些人使用.onDisapear
将本地状态同步到ObservedObject
,但这也不理想。这不理想,因为我重视对应用程序的其余部分进行 * 立即 * 更新。)
2条答案
按热度按时间owfi6suc1#
好消息是你太努力了,太努力了。
ObservedObject
属性 Package 器可以为你创建这个Binding
,你只需要说$options.refreshRate
。这里有一个测试操场供你尝试:
同样值得注意的是,如果你想创建一个自定义的
Binding
,你写的代码几乎可以工作,只要把它改为计算属性而不是存储属性即可:lxkprmvk2#
如果我没理解错的话,您希望在SwiftUI中执行
Set a Published value in an ObservableObject from the UI (Picker, etc.)
。有很多方法可以做到这一点,我建议您使用
ObservableObject
类,并在视图中需要绑定的任何地方(如Picker
)直接使用它。下面的示例代码显示了一种设置代码以执行此操作的方法:
如果你真的希望有一个“无用的”中间局部变量,那么使用这个方法: