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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:04:45