使用BSImagePicker从iCloud取图导致UI冻结问题求助
解决BSImagePicker选择iCloud图片时UI冻结的问题
看起来你遇到的是典型的主线程被耗时操作阻塞问题——iCloud里的图片并非本地直接可用的资源,而是需要从云端下载的占位符(PHAsset),如果你的代码在主线程同步等待下载完成,UI自然会卡住直到下载结束。下面给你一套具体的修复方案:
核心问题分析
BSImagePicker返回的选中项是PHAsset对象,对于iCloud中的资源,这个对象只是一个“占位标记”,真正的图片数据需要通过PHImageManager异步请求获取。如果你的代码里用了同步阻塞的方式获取图片,就会把负责UI渲染的主线程卡死。
具体修复步骤
1. 重构图片获取函数为异步模式
把原来同步返回图片的逻辑改成异步回调,让下载操作在后台线程执行:
private func loadImage(for asset: PHAsset, targetSize: CGSize, completion: @escaping (UIImage?) -> Void) { let requestOptions = PHImageRequestOptions() // 必须开启这个选项,允许从iCloud下载资源 requestOptions.isNetworkAccessAllowed = true // 优先返回高质量图片,同时允许渐进式加载(可选) requestOptions.deliveryMode = .highQualityFormat requestOptions.resizeMode = .fast // 用PHImageManager异步请求图片 PHImageManager.default().requestImage( for: asset, targetSize: targetSize, contentMode: .aspectFill, options: requestOptions ) { [weak self] image, info in // 回调可能在后台线程,必须切回主线程更新UI DispatchQueue.main.async { completion(image) } } }
2. 修改选中图片后的处理逻辑
不要在选中回调里直接同步加载图片,而是先添加占位视图,再异步更新内容:
// 初始化BSImagePicker示例 let imagePicker = BSImagePickerViewController() imagePicker.maxNumberOfSelections = 10 // 根据需求设置最大选中数 // 选中资源的回调逻辑 imagePicker.didSelectAssets = { [weak self] selectedAssets in guard let self = self else { return } // 清空滑块,先添加带加载状态的占位视图 self.imageSlider.removeAllItems() for asset in selectedAssets { // 创建容器视图,包含占位图和加载指示器 let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80)) let placeholderImage = UIImage(systemName: "photo.fill")?.withTintColor(.gray) let placeholderImageView = UIImageView(image: placeholderImage) placeholderImageView.contentMode = .scaleAspectFill placeholderImageView.clipsToBounds = true placeholderImageView.frame = containerView.bounds let activityIndicator = UIActivityIndicatorView(style: .medium) activityIndicator.center = containerView.center activityIndicator.startAnimating() containerView.addSubview(placeholderImageView) containerView.addSubview(activityIndicator) self.imageSlider.addItem(view: containerView) // 异步加载图片,完成后更新UI self.loadImage(for: asset, targetSize: containerView.bounds.size) { image in activityIndicator.stopAnimating() activityIndicator.removeFromSuperview() if let loadedImage = image { placeholderImageView.image = loadedImage } } } } // 弹出图片选择器 present(imagePicker, animated: true)
3. 额外优化建议
- 添加请求取消逻辑:如果用户在下载过程中退出页面,记得取消未完成的图片请求,避免内存泄漏:
// 用数组记录所有图片请求的ID var imageRequestIDs: [PHImageRequestID] = [] // 在loadImage里保存请求ID let requestID = PHImageManager.default().requestImage(...) imageRequestIDs.append(requestID) // 页面销毁时取消所有未完成的请求 deinit { imageRequestIDs.forEach { PHImageManager.default().cancelImageRequest($0) } } - 限制最大选中数:避免一次性下载大量iCloud图片,既影响用户体验也容易导致内存飙升。
- 错误处理:可以通过
requestImage回调的info参数判断下载是否失败,给用户显示错误提示(比如替换成错误图标)。
这样修改后,图片下载会在后台线程进行,主线程不会被阻塞,UI也就不会冻结了,用户还能看到清晰的加载状态,体验会好很多。
内容的提问来源于stack exchange,提问作者Mohammed Riyadh
相关产品推荐
相关产品推荐

