如何解决URLSession加载图片时UIImageView(profilePic)的卡顿问题?
解决UIImageView加载图片卡顿问题
你的卡顿根源在于图片解码和渲染占用主线程,尤其是当图片尺寸大、分辨率高时,UIImage(data:)只是创建了对象,真正的解码操作会在图片首次显示时在主线程执行,直接阻塞UI刷新。另外当前代码没有缓存,重复加载同一图片还要重新下载,进一步加重卡顿。
给你几个针对性的解决办法:
1. 把图片解码移到后台线程执行
把解码操作放到后台,解码完成后再回主线程设置图片,避免阻塞UI:
private func addImage() { let donorId = donor.lastName guard let urlString = self.defaults.value(forKey: "url\(donorId)") as? String, let url = URL(string: urlString) else { return } let task = URLSession.shared.dataTask(with: url) { data, _, error in guard let data = data, error == nil else { self.donorsViewController.handle(error) return } // 后台线程处理解码 DispatchQueue.global(qos: .userInitiated).async { guard let image = UIImage(data: data) else { return } // 强制触发解码:通过绘制图片完成解码 UIGraphicsBeginImageContextWithOptions(image.size, false, UIScreen.main.scale) image.draw(at: .zero) let decodedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() // 回到主线程设置图片 DispatchQueue.main.async { self.profilePic.image = decodedImage } } } task.resume() }
2. 加个本地缓存,避免重复下载
用NSCache实现图片缓存,同一图片只下载一次,提升加载速度:
// 类里加个静态缓存对象 private static let imageCache = NSCache<NSString, UIImage>() private func addImage() { let donorId = donor.lastName guard let urlString = self.defaults.value(forKey: "url\(donorId)") as? String, let url = URL(string: urlString) else { return } let cacheKey = NSString(string: urlString) // 先查缓存,有直接用 if let cachedImage = Self.imageCache.object(forKey: cacheKey) { DispatchQueue.main.async { self.profilePic.image = cachedImage } return } let task = URLSession.shared.dataTask(with: url) { data, _, error in guard let data = data, error == nil else { self.donorsViewController.handle(error) return } DispatchQueue.global(qos: .userInitiated).async { guard let image = UIImage(data: data) else { return } // 强制解码 UIGraphicsBeginImageContextWithOptions(image.size, false, UIScreen.main.scale) image.draw(at: .zero) let decodedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() // 存进缓存 if let decodedImage = decodedImage { Self.imageCache.setObject(decodedImage, forKey: cacheKey) } DispatchQueue.main.async { self.profilePic.image = decodedImage } } } task.resume() }
3. 缩放图片到ImageView的实际尺寸
如果原图比你的profilePic大很多,先把图片缩到和ImageView一样大,减少内存占用和渲染时间:
// 新增缩放方法 private func resizeImage(_ image: UIImage, to targetSize: CGSize) -> UIImage? { let size = image.size let widthRatio = targetSize.width / size.width let heightRatio = targetSize.height / size.height // 取最小比例,保证图片完整显示 let scaleFactor = min(widthRatio, heightRatio) let newSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor) UIGraphicsBeginImageContextWithOptions(newSize, false, UIScreen.main.scale) image.draw(in: CGRect(origin: .zero, size: newSize)) let resizedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return resizedImage } // 在addImage里替换解码部分的代码 DispatchQueue.global(qos: .userInitiated).async { guard let image = UIImage(data: data) else { return } // 缩放到profilePic的尺寸 let targetSize = self.profilePic.bounds.size guard let resizedImage = self.resizeImage(image, to: targetSize) else { return } // 存缓存 Self.imageCache.setObject(resizedImage, forKey: cacheKey) DispatchQueue.main.async { self.profilePic.image = resizedImage } }
内容的提问来源于stack exchange,提问作者MartinG
相关产品推荐
相关产品推荐

