You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift/SwiftUI中如何预加载Async Image以避免加载动画?

解决SwiftUI AsyncImage重复显示加载动画的问题

系统自带的AsyncImage虽然方便,但在多视图切换时经常会因为缓存未命中或者视图重建,导致每次导航都显示加载动画。下面给你几个可行的解决思路:

1. 自定义带本地缓存的AsyncImage(最可靠)

自己实现缓存逻辑,完全掌控图片的加载和复用,彻底避免重复加载的问题:

先写一个全局的图片缓存类:

import SwiftUI

class ImageCache {
    static let shared = ImageCache()
    private let cache = NSCache<NSString, UIImage>()
    
    private init() {}
    
    func get(forKey key: String) -> UIImage? {
        cache.object(forKey: key as NSString)
    }
    
    func set(_ image: UIImage, forKey key: String) {
        cache.setObject(image, forKey: key as NSString)
    }
}

然后封装一个自定义的异步图片视图,优先从缓存读取:

struct CachedAsyncImage: View {
    let url: URL?
    let placeholder: () -> View
    let image: (UIImage) -> View
    
    @State private var cachedImage: UIImage?
    @State private var isLoading = false
    
    var body: some View {
        Group {
            if let cachedImage = cachedImage {
                image(cachedImage)
            } else {
                placeholder()
                    .onAppear {
                        guard !isLoading, let url = url else { return }
                        isLoading = true
                        
                        // 先查缓存
                        if let cached = ImageCache.shared.get(forKey: url.absoluteString) {
                            cachedImage = cached
                            isLoading = false
                            return
                        }
                        
                        // 缓存没有就网络加载
                        URLSession.shared.dataTask(with: url) { data, _, error in
                            guard let data = data, let image = UIImage(data: data), error == nil else {
                                isLoading = false
                                return
                            }
                            // 存到缓存
                            ImageCache.shared.set(image, forKey: url.absoluteString)
                            DispatchQueue.main.async {
                                cachedImage = image
                                isLoading = false
                            }
                        }.resume()
                    }
            }
        }
    }
}

使用示例:

CachedAsyncImage(url: URL(string: "你的用户头像URL")) {
    // 用默认头像代替加载动画,体验更好
    Image(systemName: "person.circle")
        .font(.largeTitle)
        .foregroundColor(.gray)
} image: { uiImage in
    Image(uiImage: uiImage)
        .resizable()
        .scaledToFill()
        .clipShape(Circle())
}

2. 提前预加载头像

在用户还没进入目标视图前,就把需要的头像提前加载并缓存。比如在App启动时,或者主界面的onAppear里:

// 比如在根视图的onAppear回调中
.onAppear {
    // 假设你有需要预加载的头像URL列表
    let preloadAvatarURLs = [
        URL(string: "用户A头像URL"),
        URL(string: "用户B头像URL")
    ].compactMap { $0 }
    
    for url in preloadAvatarURLs {
        // 缓存里没有才加载
        guard ImageCache.shared.get(forKey: url.absoluteString) == nil else { continue }
        URLSession.shared.dataTask(with: url) { data, _, error in
            guard let data = data, let image = UIImage(data: data), error == nil else { return }
            ImageCache.shared.set(image, forKey: url.absoluteString)
        }.resume()
    }
}

这样用户后续导航到对应视图时,头像已经在缓存里,直接显示,不会有加载动画。

3. 优化系统AsyncImage的缓存配置

系统AsyncImage默认使用URLCache,你可以增大缓存容量,确保头像能被持久缓存:

在App入口文件里配置:

@main
struct YourApp: App {
    init() {
        // 设置内存缓存10MB,磁盘缓存50MB,根据你的需求调整
        let customCache = URLCache(memoryCapacity: 10 * 1024 * 1024, diskCapacity: 50 * 1024 * 1024)
        URLCache.shared = customCache
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

另外要注意,你的图片服务器返回的响应头需要包含合理的缓存策略(比如Cache-Control: public, max-age=86400),这样URLCache才会正确缓存图片资源。

4. 避免视图重建触发重复加载

如果每次导航到目标视图时,视图都会被重新初始化,也会导致AsyncImage重新发起请求。可以试试这些优化:

  • 将头像视图提取为独立的子View,用@StateObject或@ObservedObject保持加载状态
  • 对于iOS 16及以上版本,使用navigationDestination的缓存机制,避免视图重复创建
  • 尽量不要在导航目标视图的初始化方法里做过多操作,减少视图重建的概率

最后,如果你不想替换系统AsyncImage,也可以把占位符从ProgressView换成默认头像,这样即使加载过程中用户也看不到转圈,体验会平滑很多。

内容的提问来源于stack exchange,提问作者lionsfan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 17:15:32