1

我有一个AsyncContentView在视图出现时处理数据加载并处理加载视图和内容的切换(取自此处swiftbysundell):

struct AsyncContentView<P:Parsable, Source:Loader<P>, Content: View>: View {
    
    @ObservedObject private var source: Source
    private var content: (P.ReturnType) -> Content
    
    init?(source: Source, reloadAfter reloadTime:UInt64 = 0, @ViewBuilder content: @escaping (P.ReturnType) -> Content) {
        self.source = source
        self.content = content
    }
    
    func loadInfo() {
        Task {
            await source.loadData()
        }
    }
    
    var body: some View {
        switch source.state {
        case .idle:
            return AnyView(Color.clear.onAppear(perform: loadInfo))
        case .loading:
            return AnyView(ProgressView("Loading..."))
        case .loaded(let output):
            return AnyView(content(output))
        }
    }
}

为了完整起见,这是Parsable协议:

protocol Parsable: ObservableObject {
    associatedtype ReturnType
    init()
    var result: ReturnType { get }
}

LoadingState和_Loader

enum LoadingState<Value> {
    case idle
    case loading
    case loaded(Value)
}

@MainActor
class Loader<P:Parsable>: ObservableObject {
    @Published public var state: LoadingState<P.ReturnType> = .idle
    func loadData() async {
        self.state = .loading
        await Task.sleep(2_000_000_000)
        self.state = .loaded(P().result)
    }
}

这是我正在使用的一些虚拟数据:

struct Interface: Hashable {
    let name:String
}
struct Interfaces {
    let interfaces: [Interface] = [
        Interface(name: "test1"),
        Interface(name: "test2"),
        Interface(name: "test3")
    ]
    var selectedInterface: Interface { interfaces.randomElement()! }
}

现在我把它们放在一起,这样就可以了。它处理async显示加载视图 2 秒的函数,然后使用提供的数据生成内容视图:

struct ContentView: View {
    
    class SomeParsableData: Parsable {
        typealias ReturnType = Interfaces
        required init() { }
        var result = Interfaces()
    }
    
    @StateObject var pageLoader: Loader<SomeParsableData> = Loader()
    @State private var selectedInterface: Interface?
    
    var body: some View {
        AsyncContentView(source: pageLoader) { result in
            Picker(selection: $selectedInterface, label: Text("Selected radio")) {
                ForEach(result.interfaces, id: \.self) {
                    Text($0.name)
                }
            }
            .pickerStyle(.segmented)
        }
    }
}

现在我遇到的问题是,这个数据包含应该选择哪个段。在我的真实应用程序中,这是一个获取数据的 Web 请求,其中包括选择了哪个段。

那么我怎样才能让这个视图更新selectedInterface @state属性呢?

如果我只是添加该行

self.selectedInterface = result.selectedInterface

进入我的AsyncContentView我得到这个错误

类型 '()' 不能符合 'View'

在此处输入图像描述

4

1 回答 1

1

您可以在onAppear生成的内容中执行此操作,但我认为最好不要直接执行,而是通过绑定(这就像对状态的外部存储的引用),例如

var body: some View {
    let selected = self.$selectedInterface
    AsyncContentView(source: pageLoader) { result in
        Picker(selection: selected, label: Text("Selected radio")) {
            ForEach(result.interfaces, id: \.self) {
                Text($0.name).tag(Optional($0))       // << here !!
            }
        }
        .pickerStyle(.segmented)
        .onAppear {
            selected.wrappedValue = result.selectedInterface  // << here !!
        }
    }
}
于 2021-08-17T10:41:48.053 回答