SwiftUI中TabView轮播用AsyncImageView如何在用户看首图时预加载下一张
图片轮播组件预加载解决方案
你的代码存在两个核心问题导致无法实现无感知切换:一是缓存逻辑被手动注释,预加载的图片无法留存;二是没有提前触发下一张图片加载的逻辑。按照以下步骤修改即可实现目标效果:
步骤1:开启缓存能力
找到ImageLoader的load方法,取消缓存逻辑的注释,否则预加载的图片不会存入缓存,后续切换页面仍会重新请求:
// 去掉receiveOutput行的注释 cancellable = URLSession.shared.dataTaskPublisher(for: url) .map { UIImage(data: $0.data) } .replaceError(with: nil) .handleEvents(receiveSubscription: { [weak self] _ in self?.onStart() }, receiveOutput: { [weak self] in self?.cache($0) }, // 取消该行注释 receiveCompletion: { [weak self] _ in self?.onFinish() }, receiveCancel: { [weak self] in self?.onFinish() }) .subscribe(on: Self.imageProcessingQueue) .receive(on: DispatchQueue.main) .sink { [weak self] in self?.image = $0 }
同时可以删除#warning("Caching the image are disabled")的提示。
步骤2:添加预加载触发逻辑
修改ImagesCaruselView,监听当前选中下标的变化,提前加载相邻的图片存入缓存,用户切换时直接从缓存读取即可跳过加载动画:
struct ImagesCaruselView: View { @Binding var selectedImageToSee: Int let urls: [String] // 预加载指定下标的图片存入缓存 private func preloadImage(at index: Int) { guard index >= 0, index < urls.count else { return } guard let url = URL(string: urls[index]) else { return } let loader = ImageLoader(url: url, cache: Environment(\.imageCache).wrappedValue) loader.load() } var body: some View { TabView(selection: $selectedImageToSee) { ForEach(0..<urls.count, id: \.self) { index in AsyncImageView( url: URL(string: urls[index])!, placeholder: { ProgressView().scaleEffect(1.5).progressViewStyle(CircularProgressViewStyle(tint: .gray)) }, image: { Image(uiImage: $0) } ) .padding() .tag(index) // 必须加tag关联selection,否则监听不生效 } } .tabViewStyle(.page) .indexViewStyle(.page(backgroundDisplayMode: .always)) // 首次进入页面预加载第二张图 .onAppear { preloadImage(at: 1) } // 切换页面时预加载上下两张图,支持双向滑动无感知 .onChange(of: selectedImageToSee) { newValue in preloadImage(at: newValue + 1) preloadImage(at: newValue - 1) } } }
可选优化
如果图片数量较多,可以调整预加载的数量(比如提前加载后2张),或者根据用户滑动方向调整预加载策略,减少不必要的流量消耗;也可以根据业务需求调整TemporaryImageCache的缓存上限,适配图片体积大小。
内容的提问来源于stack exchange,提问作者ViOS
相关产品推荐
相关产品推荐

