SwiftUI 双向绑定到枚举案例中 ObservableObject 中的值

Posted

技术标签:

【中文标题】SwiftUI 双向绑定到枚举案例中 ObservableObject 中的值【英文标题】:SwiftUI two-way binding to value inside ObservableObject inside enum case 【发布时间】:2020-05-26 18:57:04 【问题描述】:

我正在尝试观察 ObservableObject 中包含的 bool 值的变化,这是 enum 案例中的值。这是我试图实现的示例,但使用当前方法我收到错误Use of unresolved identifier '$type1Value'

import SwiftUI
import Combine

class ObservableType1: ObservableObject 
    @Published var isChecked: Bool = false


enum CustomEnum 
    case option1(ObservableType1)


struct Parent: View 
    var myCustomEnum: CustomEnum
    var body: AnyView 
        switch myCustomEnum 
        case .option1(let type1Value):
            AnyView(Child(isChecked: $type1Value.isChecked)) // <- error here
        
    


struct Child: View 
    @Binding var isChecked: Bool
    var body: AnyView 
        AnyView(
            Image(systemName: isChecked ? "checkmark.square" : "square")
            .onTapGesture 
                self.isChecked = !self.isChecked
        )
    

我正在尝试从界面更新isChecked 的值,但由于我想要ObservableObject 包含enum 中的属性,例如CustomEnum 不知道该怎么做,或者如果它是甚至可能。我选择了一个枚举,因为会有多个具有不同ObservableObject 值的枚举选项,并且Parent 将根据CustomEnum 选项生成不同的子视图。如果它具有任何相关性,则Parent 将从ArrayCustomEnum 值中接收myCustomEnum 值。这甚至可能吗?如果没有,我有什么选择?谢谢!

【问题讨论】:

问题是您只能获得声明为@ObservedObject 的变量的$obj.prop 语法——而不是ObservableObject 【参考方案1】:

好吧,永远不要说永远...我已经为这种情况找到了有趣的解决方案,它甚至允许删除 AnyView。使用 Xcode 11.4 / ios 13.4 测试

提供完整的可测试模块,以防万一。

// just for test
struct Parent_Previews: PreviewProvider 
    static var previews: some View 
        Parent(myCustomEnum: .option1(ObservableType1()))
    


// no changes
class ObservableType1: ObservableObject 
    @Published var isChecked: Bool = false


// no changes
enum CustomEnum 
    case option1(ObservableType1)


struct Parent: View 
    var myCustomEnum: CustomEnum

    var body: some View 
        self.processCases() // function to make switch work
    

    private func processCases() -> some View 
        switch myCustomEnum 
        case .option1(let type1Value):
            // main part !!
            return ObservedHolder(value: type1Value)  object in
                Child(isChecked: object.isChecked)
            
        
    


// just remove AnyView
struct Child: View 
    @Binding var isChecked: Bool
    var body: some View 
        Image(systemName: isChecked ? "checkmark.square" : "square")
            .onTapGesture 
                self.isChecked = !self.isChecked
            
    

这是一个组织者

struct ObservedHolder<T: ObservableObject, Content: View>: View 
    @ObservedObject var value: T
    var content: (ObservedObject<T>.Wrapper) -> Content

    var body: some View 
        content(_value.projectedValue)
    

【讨论】:

这确实有效,但如果您有第二个类型,如 .option2(let type2Value),其值为 Child2,则返回类型仍需要包含在 AnyView 中,但无论如何,这都按预期工作.谢谢百万!【参考方案2】:

这可能适用于您的情况:

import SwiftUI

class ObservableType1: ObservableObject 
    @Published var isChecked: Bool = false


enum CustomEnum 
    case option1(ObservableType1)


struct Parent: View 
    var myCustomEnum: CustomEnum
    var body: AnyView 
        switch (myCustomEnum) 
            case .option1:
                return AnyView(Child())
            default: return AnyView(Child())
        
    


struct Child: View 

    @ObservedObject var type1 = ObservableType1()

    var body: AnyView 
        AnyView(
            Image(systemName: self.type1.isChecked ? "checkmark.square" : "square")
            .onTapGesture 
                self.type1.isChecked.toggle()
        )
    

【讨论】:

以上是关于SwiftUI 双向绑定到枚举案例中 ObservableObject 中的值的主要内容,如果未能解决你的问题,请参考以下文章

由枚举驱动的 SwiftUI 选择器:值未更新

Angular 2:如何从枚举创建单选按钮并添加双向绑定?

SwiftUI — Combine 实践 双向绑定和时间机器

浅析vue的双向数据绑定

VUE实现双向数据绑定的原理

SwiftUI:导航在带有部分的列表中无法正常工作