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

SwiftUI点击手势选择错误:自定义仿Instagram图片选择器选中项不符

问题根因

  • 核心原因是相册图片加载完成后append到数组的顺序可能和PHAsset枚举顺序不一致:你在后台队列逐个发起同步图片请求,不同大小的图片加载耗时不同,通过DispatchQueue.main.async回调插入数组的顺序就会错乱,导致界面展示的图片顺序和allPhotos数组的存储顺序不匹配,点击时拿到的photo自然和你看到的图片对应不上。
  • 次要问题是LazyVGrid的视图复用未绑定唯一标识:动态更新数组时SwiftUI可能复用旧视图的手势绑定,出现点击错位。
  • 额外错误:@Environment(\.dismiss)不能放在ObservableObject子类中,该属性包装器仅支持在SwiftUI视图中使用。

修复方案

首先调整SelectNewPostScreenManager的图片加载逻辑,保证数组顺序和枚举顺序一致:

class SelectNewPostScreenManager: ObservableObject {
    @Published var selectedPhoto: Photo?
    @Published var allPhotos: [Photo] = []

    init() {
        fetchPhotos()
    }

    private func assetsFetchOptions() -> PHFetchOptions {
        let fetchOptions = PHFetchOptions()
        let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
        fetchOptions.sortDescriptors = [sortDescriptor]
        return fetchOptions
    }

    func fetchPhotos() {
        print("Fetching Photos")
        let options = assetsFetchOptions()
        let allAssets = PHAsset.fetchAssets(with: .image, options: options)
        let assetCount = allAssets.count
        // 先创建占位数组,保证顺序固定
        var tempPhotos: [Photo?] = Array(repeating: nil, count: assetCount)
        let imageManager = PHImageManager.default()
        let targetSize = CGSize(width: 250, height: 250)
        let requestOptions = PHImageRequestOptions()
        requestOptions.isSynchronous = true
        
        DispatchQueue.global(qos: .background).async {
            allAssets.enumerateObjects { asset, index, _ in
                imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: requestOptions) { image, info in
                    guard let image = image else { return }
                    let photo = Photo(uiImage: image)
                    // 按索引插入到对应位置,保证顺序和枚举顺序一致
                    tempPhotos[index] = photo
                    // 所有图片加载完成后再一次性更新数组
                    if index == assetCount - 1 {
                        DispatchQueue.main.async {
                            self.allPhotos = tempPhotos.compactMap { $0 }
                        }
                    }
                }
            }
        }
    }
}

然后调整NewPostScreen的LazyVGrid部分,给每个图片视图绑定唯一ID,避免复用错位:

LazyVGrid(columns: columns, spacing: 1) {
    ForEach(manager.allPhotos, id: \.id) { photo in
        Image(uiImage: photo.uiImage)
            .resizable()
            .scaledToFill()
            .frame(maxWidth: UIScreen.main.bounds.width/3, minHeight: UIScreen.main.bounds.width/3, maxHeight: UIScreen.main.bounds.width/3)
            .clipped()
            .id(photo.id) // 绑定唯一标识,防止视图复用时手势错位
            .onTapGesture {
                manager.selectedPhoto = photo
            }
    }
}

可选优化

如果相册图片数量较多,一次性加载所有图片会占用过高内存,可以改为先加载低清缩略图,或者支持分页加载,同时保留索引对齐的逻辑即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 05:24:04