0

我正在尝试使用存储在照片应用程序中的照片来实现 Gridview,以便用户可以选择一张照片并将其选为他的个人资料照片。在 SwiftUI 之前,我使用集合视图和 Photos Kit 来获取图像并将它们显示在网格中。

现在我切换到 SwiftUI,我尝试使用 LazyVGrid。我能够获取用户的所有照片并将它们显示在网格中。但是它使用了大量的内存。我之前有内存泄漏,但现在 Instruments 不再显示任何泄漏。

我认为它可能是,当网格对用户不可见时,它并没有真正卸载显示的图像。但是,如果您向上和向下滚动多次,它只会比以前使用更多的内存。就像网格总是在创建新视图,而旧视图不会被删除。我是在使用错误的东西还是误解了 LazyVGrid 的原理?

class PhotoLibrary: ObservableObject {

    

    @Published var assets = [PHAsset]()

    @Published var imageCachingManager: PHCachingImageManager = PHCachingImageManager()

    func requestAuthorization() {

        PHPhotoLibrary.requestAuthorization { [weak self] (status) in

            guard let self = self else { return }



            switch status {

            case .authorized:

                self.getAllPhotos()

            case .denied:

                break

            case .notDetermined:

                break

            case .restricted:

                break

            case .limited:

                self.getAllPhotos()

            @unknown default:

                break

            }

        }

    }



    private func getAllPhotos() {

        

        imageCachingManager.allowsCachingHighQualityImages = false

        

        

        let allPhotosOptions = PHFetchOptions()

        allPhotosOptions.includeHiddenAssets = false


        allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]

        

        let assets: PHFetchResult = PHAsset.fetchAssets(with: .image, options: allPhotosOptions)

        

        var _assets = [PHAsset]()

        

        assets.enumerateObjects({ asset, _, _ in

            _assets.append(asset)

        })

        

        DispatchQueue.main.async { [weak self] in

            self?.assets = _assets

        }

        

    }
    

    func resetCache() {

        imageCachingManager.stopCachingImagesForAllAssets()

        assets = []

    }

    

    func preCacheImages(assets: [PHAsset], size: CGSize) {

        DispatchQueue.global(qos: .userInitiated).async { [weak self] in

            guard let self = self else {

                return

            }

            

            let options = PHImageRequestOptions()

            options.deliveryMode = .opportunistic

            options.resizeMode = .fast

            options.isSynchronous = true

            options.isNetworkAccessAllowed = true

            

            self.imageCachingManager.startCachingImages(for: assets, targetSize: size, contentMode: .aspectFill, options: options)

        }

        

    }

    

    func fetchImage(index: Int, size: CGSize?) async -> UIImage? {

        

        guard let asset = assets[safe: index] else {

            return nil

        }

        

        let options = PHImageRequestOptions()

        options.deliveryMode = .opportunistic

        options.resizeMode = .fast

        options.isSynchronous = true

        options.isNetworkAccessAllowed = true

        

        return await withCheckedContinuation({

            [weak self] (continuation: CheckedContinuation<UIImage?, Never>) in

            

            guard let self = self else {

                return

            }

            

            self.imageCachingManager.requestImage(for: asset, targetSize: size ?? PHImageManagerMaximumSize, contentMode: .aspectFill, options: options) {(image, info) in

                continuation.resume(returning: image)

            }

        })

    }

    

    func removeFromCache(index: Int, size: CGSize) {

        DispatchQueue.global(qos: .userInitiated).async { [weak self] in

            

            guard let self = self else {

                return

            }

            

            guard let asset = self.assets[safe: index] else {

                return

            }

            

            let options = PHImageRequestOptions()

            options.deliveryMode = .opportunistic

            options.resizeMode = .fast

            options.isSynchronous = true

            options.isNetworkAccessAllowed = true

            

            self.imageCachingManager.stopCachingImages(for: [asset], targetSize: size, contentMode: .aspectFill, options: options)

        }

    }

    

}
struct PhotoView: View {

    var index: Int

    let thumbnailSize: CGSize?

    weak var photoLibrary: PhotoLibrary?

    @State var image: UIImage? = nil

    

    var body: some View {

        HStack {

            if let image = image {

                Image(uiImage: image)

                    .resizable()

                    .square()

            } else {

                Color.label

                    .aspectRatio(1.0, contentMode: .fill)

            }

        }

        .onDisappear(perform: {

            unload()

        })

        .onAppear(perform: {

            Task {

                guard let photoLibrary = photoLibrary else {

                    return

                }



                let image = await photoLibrary.fetchImage(index: index, size: thumbnailSize ?? PHImageManagerMaximumSize)

                

                DispatchQueue.main.async {

                    self.image = image

                }

            }

        })

    }

    

    private func unload() {

        self.image = nil

    }

}
struct PickProfilePictureView: View {
    
    @ObservedObject var photoLibrary = PhotoLibrary()
    
    var body: some View {
        VStack(alignment: .leading) {
        
            GeometryReader { reader in
                let width = reader.size.width / 4 - 6
                ScrollView {
                    
                    LazyVGrid(columns: [GridItem(), GridItem(), GridItem(), GridItem()]) {
                        ForEach(photoLibrary.assets.indices, id: \.self) { index in
                            
                            PhotoView(index: index, thumbnailSize: CGSize(width: width, height: width), photoLibrary: photoLibrary)
                                .contentShape(Rectangle())
                                .onDisappear(perform: {
                                    photoLibrary.removeFromCache(index: index, size: CGSize(width: width, height: width))
                                })
                        }
                    }
                }
                
            }
            
            Spacer()
        }
        .ignoresSafeArea(.container, edges: .bottom)
        .onAppear {
            self.photoLibrary.requestAuthorization()
        }
    }
}
4

0 回答 0