How to create a Photo Album to display photos by SwiftUI ?

My train of thought:

  1. Create a ScrollView with SwiftUI and use LazyVGrid for layout and performance optimization
  2. Use .onApear {} to perform the Photos fetchResult operation
  3. Dynamically load photos in LazyVGrid (turn asset into UIImage, then into Image View)

code show as below:

import SwiftUI

import UIKit

import Photos



struct ContentView: View {

    var albumManager = Album()

    @State var count: Int = 0



    var thumbnailSize: CGSize{

        CGSize(width: 150, height: 150)

    }

    

    var body: some View {

        VStack {

            Text("Photos Count: \(count)")

                .font(.title)

                .foregroundColor(.blue)

            if count > 0  {

                ScrollView {

                    LazyVGrid(columns: [ GridItem(.adaptive(minimum: 150), spacing: 20)], spacing: 20)  {

                        ForEach(0 ..< count) { index in

                            let asset = albumManager.fetchResult.object(at: index)

                            var thumbImage: UIImage? = UIImage(systemName: "photo")

                            let aaa = albumManager.imageManager.requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil)

                            { image, _ in

                                if image != nil {

                                    thumbImage = image!

                                    print("\(asset.localIdentifier)")

                                }

                               

                            }

                            if thumbImage != nil {

                                Image(uiImage: thumbImage!)

                                    .resizable()

                                    .aspectRatio(contentMode: .fill)

                                    .frame(width: 150, height: 150)

                                    .cornerRadius(5)

                            }

                          

                        }

                    }

                }

               

            }

            

        }

        .onAppear {

            Task {

                await MainActor.run {

                    albumManager.loadAllImages()

                    albumManager.options.deliveryMode = .highQualityFormat

                    count = albumManager.count

                }

            }

        }

    }

}



class Album: ObservableObject {



    @Published var count: Int = 0

    @Published var fetchResult: PHFetchResult<PHAsset>!

    

    let allPhotosOptions = PHFetchOptions()

    let imageManager = PHCachingImageManager()

    var options = PHImageRequestOptions()

    

    func loadAllImages() {

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

        fetchResult = PHAsset.fetchAssets(with: allPhotosOptions)

        count = fetchResult.count

    }

}

Here comes the question: .requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil) { image, _ in} This code will asynchronously fetch photo thumbnails multiple times.

  • When options is set to nil, it will quickly get the image and load it immediately. But the thumbnails are blurry.
  • When options is set to .highQualityFormat, it will get high-quality images, but because the speed is not fast enough, it will not be loaded.

If you can use await to get the image directly, then all the problems are solved. But Apple only provides a callback for a closure here: .requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil) { image, _ in}

I also tried another solution: Use a @State variable to store image, so you can get instant refreshed images. But the pictures will be refreshed all the time, which is not easy to manage.

So, how can I handle this like the Cell in UIKit using CollectionView.

How to create a Photo Album to display photos by SwiftUI ?
 
 
Q