I’m using SwiftUI to build a list with images from an API. There’s some lagging while the image is being downloaded. I can understand that the issue comes from the CatRowView, because when I replace it with a Image(systemName:
it doesn’t lag, but AsyncImage
is async, so I don’t know why this happens.
final class CatsGridViewModel: ObservableObject {
private let apiService: APIService
private (set) var page = 0
@Published var catsArray: [Cat] = []
var isLoading = false
init(apiService: APIService = APIService()) {
self.apiService = apiService
}
func isLastCat(id: String) -> Bool {
return catsArray.last?.id == id
}
@MainActor
func fetchCats() async {
if !isLoading {
isLoading = true
let array = await apiService.fetchImages(.thumb, page: page, limit: 25)
catsArray.append(contentsOf: array)
page += 1
isLoading = false
}
}
}
struct CatGridView: View {
@StateObject var viewModel = CatsGridViewModel()
var body: some View {
NavigationStack {
ScrollView {
LazyVStack {
ForEach(viewModel.catsArray) { cat in
VStack {
CatRowView(cat: cat)
.padding(.horizontal)
}.onAppear {
if viewModel.isLastCat(id: cat.id) {
Task {
await viewModel.fetchCats()
}
}
}
}
}
}
.task {
await viewModel.fetchCats()
}
.navigationTitle("Cats: (viewModel.page)")
}
}
}
struct CatRowView: View {
var cat: Cat
var body: some View {
HStack {
AsyncImage(url: URL(string: cat.url)!) { image in
image
.resizable()
.clipShape(Circle())
.frame(width: 100, height: 100)
} placeholder: {
ProgressView()
.frame(width: 100, height: 100)
}
}
}
}
2
Answers
.task
replaces@StateObject
so try removing that class. The basics are return cats as a result from your async func. Put this async func in a struct that is not main actor, egCatsController
which usually is anEnvironmentKey
. Call it from.task
and set the result on an@State
.Use
task(id:page)
for pages.I downloaded your project and made changes to two files: ContentView and Cat
Sword/Model/Cat:
Sword/View/ContenView
The final result is better
But
I recommend adding a simple cache using NSCache. This allows images that have already been downloaded to be stored in memory, avoiding repeated downloading and rendering when scrolling through the list.
This modifications should make the scrolling smoother and reduce the need to re-download images when scrolling back up.
Let me know how it performs!