绑定到SwiftUI中的只读属性

时间:2020-01-07 19:42:27

标签: swift swiftui combine

我有一个看起来像这样的模型类型:

enum State {
    case loading
    case loaded([String])
    case failed(Error)

    var strings: [String]? {
        switch self {
        case .loaded(let strings): return strings
        default: return nil
        }
    }
}

class MyApi: ObservableObject {
    private(set) var state: State = .loading

    func fetch() {
        ... some time later ...
        self.state = .loaded(["Hello", "World"])
    }
}

我正在尝试使用它来驱动SwiftUI视图。

struct WordListView: View {
    @EnvironmentObject var api: MyApi

    var body: some View {
        ZStack {
            List($api.state.strings) {
                Text($0)
            }
        }
    }
}

正是在这里我的假设失败了。我正在尝试获取要加载的List中呈现的字符串的列表,但无法编译。

编译器错误为Generic parameter 'Subject' could not be inferred,经过一番谷歌搜索后得知绑定是双向的,因此无法同时读取我的private(set)和正在读取的State枚举上的var -仅。

这似乎没有任何意义-视图无法告诉api是否正在加载,这绝对应该是单向数据流!

我想我的问题是

  1. 是否有一种方法可以在SwiftUI中获得单向绑定-即某些UI会根据其无法更改的值进行更新。

  1. 我应该如何构建此代码!我很可能以无法与SwiftUI一起使用的方式编写代码,但是我在网上看到的所有教程都巧妙地忽略了加载/错误状态之类的东西。

1 个答案:

答案 0 :(得分:4)

您实际上不需要绑定。

决定是否需要绑定的一种直观方法是询问:

此视图是否需要修改传递的值?

在您的情况下,答案是否定的。 List不需要修改api.state(例如,与文本字段或滑块相反),它只需要在任何给定时刻使用其当前值即可。这就是@State对于但是的意思,因为状态不是视图的属于(请记住,Apple表示每个状态必须对视图私有) ),您正确地使用了某种形式的ObservableObject(通过环境)。

最后遗失的部分是用@Published标记应触发更新的任何属性,这很方便触发objectWillChange信号并指示任何观察视图重新计算其主体。

所以,像这样的事情会完成:

class MyApi: ObservableObject {
    @Published private(set) var state: State = .loading

    func fetch() {
        self.state = .loaded(["Hello", "World"])
    }
}

struct WordListView: View {
    @EnvironmentObject var api: MyApi

    var body: some View {
        ZStack {
            List(api.state.strings ?? [], id: \.self) {
                Text($0)
            }
        }
    }
}