我试图在一个面向iOS 16的应用程序中的滚动视图上使用可刷新修改器。但是,在拉取以刷新手势期间,异步任务被取消。
下面是演示该问题的一些代码和随附的视频以及打印错误的图像:
ExploreViemModel.swift
class ExploreViewModel: ObservableObject {
@Published var randomQuotes: [Quote] = []
init() {
Task {
await loadQuotes()
}
}
@MainActor
func loadQuotes() async {
let quotesURL = URL(string: "https://type.fit/api/quotes")!
do {
let (data, urlResponse) = try await URLSession.shared.data(from: quotesURL)
guard let response = urlResponse as? HTTPURLResponse else { print("no response"); return}
if response.statusCode == 200 {
let quotes = try JSONDecoder().decode([Quote].self, from: data)
randomQuotes.append(contentsOf: quotes)
}
} catch {
debugPrint(error)
debugPrint(error.localizedDescription)
}
}
func clearQuotes() {
randomQuotes.removeAll()
}
}
ContentView.swift
import SwiftUI
struct ContentView: View {
@StateObject private var exploreVM = ExploreViewModel()
var body: some View {
NavigationStack {
ExploreView()
.environmentObject(exploreVM)
.refreshable {
exploreVM.clearQuotes()
await exploreVM.loadQuotes()
}
}
}
}
Explore.swift
import SwiftUI
struct ExploreView: View {
@EnvironmentObject var exploreVM: ExploreViewModel
var body: some View {
ScrollView {
VStack {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 140.0), spacing: 24.0)], spacing: 24.0) {
ForEach(exploreVM.randomQuotes) { quote in
VStack(alignment: .leading) {
Text("\(quote.text ?? "No Text")")
.font(.headline)
Text("\(quote.author ?? "No Author")")
.font(.caption)
}
.frame(minWidth: 0, maxWidth: .infinity)
.frame(height: 144.0)
.border(Color.red, width: 2.0)
}
}
}
.padding()
.navigationTitle("Explore")
}
}
}
2条答案
按热度按时间fhg3lkii1#
当调用
exploreVM.clearQuotes()
时,将导致body
在清除数组时进行重绘。.refreshable
也会被重绘,因此正在使用的上一个"任务"会被取消。这正是SwiftUI的本质。
有几种方法可以克服这个问题,最简单的方法是通过使用
id
来"坚持"任务。如果使用上述方法,则应删除
ExploreViewModel
的init
中的"浮动"Task
。另一种方法是在url调用返回之前阻止重绘。
是等待更改数组直到有响应。
选项1更能抵抗取消,对于短时间调用是可以的。它不会等待调用返回来关闭ProgressView。
选项2从ViewModel内部提供了更多的控制,但是视图仍然可以由其他人重新绘制。
选项3可能是苹果设想的过程,但也容易受到其他重画。
4c8rllxm2#
async/await和
.task
的目的是消除对引用类型的需要。