绑定到 SwiftUI 中的只读属性

dea*_*rne 9 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"])
    }
}
Run Code Online (Sandbox Code Playgroud)

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

struct WordListView: View {
    @EnvironmentObject var api: MyApi

    var body: some View {
        ZStack {
            List($api.state.strings) {
                Text($0)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正是在这里,我的假设失败了。List我试图获取加载时要在 my 中呈现的字符串列表,但它不会编译。

编译器错误是Generic parameter 'Subject' could not be inferred,经过一番谷歌搜索后,它告诉我绑定是双向的,因此不适用于private(set)State 枚举上的 my 和 var 为只读。

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

我想我的问题是

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

或者

  1. 我应该如何构建这段代码!我很可能正在以一种不适用于 SwiftUI 的风格编写代码,但我在网上看到的所有教程都巧妙地忽略了诸如加载/错误状态之类的内容。

All*_*ian 11

您实际上并不需要为此进行绑定。

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

这个视图需要修改传递的值吗?

就您而言,答案是否定的。不需要List修改api.state(例如与文本字段或滑块相反),它只需要在任何给定时刻的当前值。这就是@State目的,但由于状态不属于视图(请记住,苹果说每个状态必须是视图私有的),因此您正确地使用了某种形式的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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果我们有一些来自某处但需要在自定义视图中观察的 @Published 值类型属性,而不是“MyApi”,该怎么办? (2认同)