My train of thought:
Create a ScrollView with SwiftUI and use LazyVGrid for layout and performance optimization
Use .onApear {} to perform the Photos fetchResult operation
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.