【发布时间】:2021-10-19 05:48:55
【问题描述】:
我有一个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 我收到此错误
类型“()”不能符合“视图”
【问题讨论】:
-
您不能将这种类型的代码放在视图中。您可以尝试将其添加到 Picker:
.onAppear { self.selectedInterface = result.selectedInterface } -
谢谢。我看到这确实更新了
selectedInterface,但它似乎没有选择一个段。 -
@Darren 另一种方法是使用
let _ = ...。例如要在视图正文中打印,请执行let _ = print("test")。此方法与onAppear不同,因为每次重新计算body时都会发生这种情况,而不仅仅是第一次出现。 -
John Sundell:“可以肯定的是,上述模式对于更简单的视图非常有效——然而,将视图代码与数据加载和网络等任务混合并不是一个好的实践,因为随着时间的推移,这样做往往会导致非常混乱和相互交织的实现。” 所以,感谢您为我们提供了这种反模式的完美示例;)
标签: swift async-await swiftui swift5.5 swift-concurrency