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

如何优化Swift中含大量图片的ViewController加载耗时?

缩短SectionViewController加载时间的优化策略

看起来你遇到的核心问题是同步预加载了所有65MB的图片资源——在初始化大量AnatomyView实例时,你直接用UIImage(named:)同步加载了所有图片,这不仅会阻塞主线程导致长达10秒的卡顿,还会一次性占用大量内存,完全拖垮了App的启动和前台恢复体验。结合你提到的ScrollView最多同时显示8-10张图的场景,我给你整理了几个针对性的优化方案:

1. 延迟加载图片,仅在需要显示时加载

把AnatomyView里的图片实例换成文件名存储,不要在初始化时加载UIImage。这样初始化AnatomyView的时间会趋近于0,因为只是存储字符串和元数据,完全不涉及磁盘IO和图片解码。

修改后的AnatomyView示例:

class AnatomyView {
    var viewName: String = ""
    // 存储图片文件名而非UIImage实例
    var normalImageName: String = ""
    var markedImageName: String = ""
    var stepImageNames: [String] = []
    var attribution: String = ""

    init(viewName: String, normalImageName: String, markedImageName: String, stepImageNames: [String], attribution: String) {
        self.viewName = viewName
        self.normalImageName = normalImageName
        self.markedImageName = markedImageName
        self.stepImageNames = stepImageNames
        self.attribution = attribution
    }

    // 异步加载图片的方法,后台线程加载+解码,主线程返回结果
    func loadNormalImage(completion: @escaping (UIImage?) -> Void) {
        DispatchQueue.global(qos: .userInitiated).async {
            // UIImage(named:)会缓存图片,适合常驻资源;大图片可用contentsOfFile避免缓存
            guard let image = UIImage(named: self.normalImageName) else {
                DispatchQueue.main.async { completion(nil) }
                return
            }
            // 提前解码图片,避免主线程解码卡顿
            _ = image.cgImage
            DispatchQueue.main.async { completion(image) }
        }
    }

    // 同理实现markedImage和step图片的加载方法
    func loadMarkedImage(completion: @escaping (UIImage?) -> Void) {
        // 重复类似逻辑
    }
}

对应的SectionViewController实例初始化:

let lateralYShoulder = AnatomyView(
    viewName: "Lateral Y",
    normalImageName: "Lateral Y Normal Unmarked.jpg",
    markedImageName: "Lateral Y Normal Marked.jpg",
    stepImageNames: ["Lateral Y Step 1.jpg", "Lateral Y Step 2.jpg"],
    attribution: "Case courtesy of Mr Andrew Murphy\nRadiopaedia.org, rID: 48080"
)

之后在ScrollView的可见区域变化时(比如scrollViewDidScroll或使用UICollectionView的cellForItemAt),调用这些加载方法,只有当某个AnatomyView即将进入屏幕时才加载对应的图片。

2. 利用异步图片加载库简化逻辑

如果不想自己写异步加载的代码,可以用成熟的第三方库,比如SDWebImage或Kingfisher。这些库不仅能自动处理异步加载、缓存,还能优化图片解码、内存管理,甚至支持WebP/HEIC等高效图片格式。

以SDWebImage为例,你可以直接给UIImageView设置本地图片的路径:

// 假设你有一个显示normalImage的UIImageView
if let imagePath = Bundle.main.path(forResource: lateralYShoulder.normalImageName, ofType: nil) {
    let imageURL = URL(fileURLWithPath: imagePath)
    imageView.sd_setImage(with: imageURL, placeholderImage: UIImage(named: "Logo.jpg"))
}

这些库会自动在后台加载、解码图片,加载完成后更新UI,完全不阻塞主线程。

3. 优化图片资源本身

65MB的图片体积是导致加载慢的重要原因之一,你可以从这几个方面优化:

  • 转换为高效格式:把PNG转成HEIC或WebP,这两种格式的压缩率是PNG的2-3倍,质量损失几乎可以忽略。Xcode支持直接导入HEIC图片,WebP需要借助库(比如SDWebImage支持WebP解码)。
  • 适配屏幕分辨率:为不同设备提供@2x、@3x的图片,不要用远超显示需求的大图。比如如果图片在屏幕上只显示500px宽,就不要用2000px宽的原图。
  • 移除不必要的Alpha通道:如果图片不需要透明背景,把Alpha通道去掉,可以显著减小体积并加快解码速度。

4. 采用Apple官方的按需资源(On-Demand Resources)

如果你的图片资源不是启动时必须的,可以用On-Demand Resources把图片分成多个资源包。App启动时只会下载核心资源,当用户滚动到需要特定图片的区域时,系统会自动后台下载对应的资源包。这不仅能减少启动时的加载时间,还能减小App的初始安装包大小。

设置方法:在Xcode中把图片拖到"On Demand Resources"标签下,标记为不同的标签(比如按身体部位分组),然后在代码中请求对应的资源标签:

let resourceRequest = NSBundleResourceRequest(tags: ["shoulder-images"])
resourceRequest.beginAccessingResources { error in
    if let error = error {
        // 处理资源加载错误
        return
    }
    // 资源已下载,可以加载图片了
}

5. 优化ScrollView的图片加载范围

因为你的ScrollView最多同时显示8-10张图,所以可以只加载当前可见区域+前后各1-2张的图片,其他图片在滚动出屏幕时释放内存(比如把UIImageView的image设为nil)。

如果用的是UIScrollView,可以在scrollViewDidScroll方法中计算可见区域,判断哪些子视图需要加载图片;如果换成UICollectionView会更方便,它自带的复用机制会自动管理单元格的创建和销毁,你只需要在cellForItemAt中加载当前单元格需要的图片即可。

6. 绝对避免主线程阻塞

一定要确保所有图片加载、解码操作都在后台线程执行,绝对不要在主线程批量调用UIImage(named:)——之前的代码就是在主线程同步加载所有图片,直接导致了10秒的卡顿,主线程被阻塞时App完全无法响应用户操作,看起来就像崩溃了一样。


按照这些方案优化后,你的SectionViewController初始化时间会降到毫秒级,图片加载会分散到用户滚动的过程中,完全不会出现10秒的卡顿。优先实施前两个方案(延迟加载+异步加载),这两个是见效最快的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:06:14