Get thumbnails working in a non-awful way
- ID
322fe4b- date
2023-06-10 07:48:29+00:00- author
Alex Chan <alex@alexwlchan.net>- parent
7f5afe6- message
Get thumbnails working in a non-awful way- changed files
6 files, 65 additions, 45 deletionsBlinkReviewer/BlinkReviewer/Photos/PhotosLibrary.swiftBlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swiftBlinkReviewer/BlinkReviewer/Views/ThumbnailList.swiftBlinkReviewer/BlinkReviewer/Views/Thumbnails/FavoriteOverlay.swiftBlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailImage.swiftBlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailList.swift
Changed files
BlinkReviewer/BlinkReviewer/Photos/PhotosLibrary.swift (5418) → BlinkReviewer/BlinkReviewer/Photos/PhotosLibrary.swift (6756)
diff --git a/BlinkReviewer/BlinkReviewer/Photos/PhotosLibrary.swift b/BlinkReviewer/BlinkReviewer/Photos/PhotosLibrary.swift
index 7d94dc2..59fffe6 100644
--- a/BlinkReviewer/BlinkReviewer/Photos/PhotosLibrary.swift
+++ b/BlinkReviewer/BlinkReviewer/Photos/PhotosLibrary.swift
@@ -123,7 +123,7 @@ class PhotosLibrary: NSObject, ObservableObject, PHPhotoLibraryChangeObserver {
}
}
- func state(for asset: PHAsset) -> ReviewState? {
+ func state(of asset: PHAsset) -> ReviewState? {
if self.rejectedAssets.contains(asset) {
return .Rejected
}
@@ -138,4 +138,37 @@ class PhotosLibrary: NSObject, ObservableObject, PHPhotoLibraryChangeObserver {
return nil
}
+
+ // Implements a basic cache for thumbnail images.
+ //
+ // Thumbnail images are small and easily reused; I've put them here because
+ // we already pass this class around as a shared @EnvironmentObject.
+ //
+ // For some reason SwiftUI insists on trying to recreate all the thumbnail
+ // views when you step between images -- I think there's probably a way to
+ // have it cache the views rather than me doing it manually, but I'm not
+ // smart enough to debug that. If I don't cache it, there's a "flash" as
+ // it reloads the thumbnails every time.
+ //
+ // TODO: Investigate using SwiftUI to do this.
+ // TODO: If that doesn't work, replace this Dictionary with NSCache or an
+ // LRU cache. For some reason NSCache didn't store entries when I tried it,
+ // but I didn't try for very long.
+ private var thumbnailCache = Dictionary<PHAsset, PHAssetImage>()
+
+ func getThumbnail(for asset: PHAsset) -> PHAssetImage {
+ if let cachedThumbnail = thumbnailCache[asset] {
+ return cachedThumbnail
+ }
+
+ let newThumbnail = PHAssetImage(
+ asset,
+ size: CGSize(width: 70, height: 70),
+ deliveryMode: .fastFormat
+ )
+
+ thumbnailCache[asset] = newThumbnail
+
+ return newThumbnail
+ }
}
BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift (10284) → BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift (10283)
diff --git a/BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift b/BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift
index b5ea9b3..b2fe40e 100644
--- a/BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift
+++ b/BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift
@@ -97,7 +97,7 @@ struct PhotoReviewer: View {
case let e where e.characters == "1" || e.characters == "2" || e.characters == "3":
print("time to review!")
- let state = photosLibrary.state(for: focusedAsset)
+ let state = photosLibrary.state(of: focusedAsset)
let approved = getAlbum(withName: "Approved")
let rejected = getAlbum(withName: "Rejected")
BlinkReviewer/BlinkReviewer/Views/ThumbnailList.swift (3016) → BlinkReviewer/BlinkReviewer/Views/ThumbnailList.swift (3015)
diff --git a/BlinkReviewer/BlinkReviewer/Views/ThumbnailList.swift b/BlinkReviewer/BlinkReviewer/Views/ThumbnailList.swift
index ca7830f..e56d6d0 100644
--- a/BlinkReviewer/BlinkReviewer/Views/ThumbnailList.swift
+++ b/BlinkReviewer/BlinkReviewer/Views/ThumbnailList.swift
@@ -34,7 +34,7 @@ struct ThumbnailList: View {
// Text("asset \(index)")
ThumbnailImage(
thumbnail: PHAssetImage(asset, size: CGSize(width: 70, height: 70), deliveryMode: .opportunistic),
- state: photosLibrary.state(for: asset),
+ state: photosLibrary.state(of: asset),
isFavorite: asset.isFavorite,
isSelected: photosLibrary.assets2.count - 1 - index == selectedAssetIndex
).onTapGesture {
BlinkReviewer/BlinkReviewer/Views/Thumbnails/FavoriteOverlay.swift (841) → BlinkReviewer/BlinkReviewer/Views/Thumbnails/FavoriteOverlay.swift (854)
diff --git a/BlinkReviewer/BlinkReviewer/Views/Thumbnails/FavoriteOverlay.swift b/BlinkReviewer/BlinkReviewer/Views/Thumbnails/FavoriteOverlay.swift
index 4bb1685..0adb2fe 100644
--- a/BlinkReviewer/BlinkReviewer/Views/Thumbnails/FavoriteOverlay.swift
+++ b/BlinkReviewer/BlinkReviewer/Views/Thumbnails/FavoriteOverlay.swift
@@ -5,14 +5,14 @@ import Photos
///
/// This is meant to match the way favorite items are marked in Photos.
struct FavoriteHeartIcon: ViewModifier {
- let asset: PHAsset
+ let isFavorite: Bool
- init(_ asset: PHAsset) {
- self.asset = asset
+ init(_ isFavorite: Bool) {
+ self.isFavorite = isFavorite
}
func body(content: Content) -> some View {
- if asset.isFavorite {
+ if isFavorite {
content.overlay(alignment: Alignment(horizontal: .leading, vertical: .bottom)) {
Image(systemName: "heart.fill")
.foregroundColor(.white)
@@ -26,7 +26,7 @@ struct FavoriteHeartIcon: ViewModifier {
}
extension View {
- func favoriteHeartIcon(for asset: PHAsset) -> some View {
- modifier(FavoriteHeartIcon(asset))
+ func favoriteHeartIcon(_ isFavorite: Bool) -> some View {
+ modifier(FavoriteHeartIcon(isFavorite))
}
}
BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailImage.swift (2012) → BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailImage.swift (1827)
diff --git a/BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailImage.swift b/BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailImage.swift
index d8d96f7..25a2503 100644
--- a/BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailImage.swift
+++ b/BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailImage.swift
@@ -7,32 +7,30 @@ import Photos
/// mean some information gets cropped out -- that's okay, these are only
/// small previews, not complete images.
struct NewThumbnailImage: View {
- @EnvironmentObject var photosLibrary: PhotosLibrary
-
- var asset: PHAsset
+ @ObservedObject var assetImage: PHAssetImage
+ var state: ReviewState?
var isFocused: Bool
+ var isFavorite: Bool
private var size: CGFloat
private var cornerRadius: CGFloat
-
- @ObservedObject var assetImage: PHAssetImage
-
- init(_ asset: PHAsset, isFocused: Bool) {
- self.asset = asset
+
+ // Implementation note: the reason we pass in a bunch of individual
+ // properties rather than the whole asset is because we need an
+ // @EnvironmentObject (the PhotosLibrary) to create the PHAssetImage,
+ // so we can stick the latter in an @ObservedObject.
+ //
+ // But EnvironmentObject values aren't passed down until you call the
+ // `body` method, which is too late! So instead we have the parent
+ // view call into PhotosLibrary and pass in the relevant values here.
+ init(_ assetImage: PHAssetImage, state: ReviewState?, isFavorite: Bool, isFocused: Bool) {
+ self.assetImage = assetImage
+ self.state = state
+ self.isFavorite = isFavorite
self.isFocused = isFocused
self.size = isFocused ? 70 : 50
self.cornerRadius = isFocused ? 7 : 5
-
- self.assetImage = PHAssetImage(
- asset,
- size: CGSize(width: self.size, height: self.size),
- deliveryMode: .fastFormat
- )
- }
-
- private var state: ReviewState? {
- photosLibrary.state(for: asset)
}
var body: some View {
@@ -45,20 +43,6 @@ struct NewThumbnailImage: View {
.reviewStateBorder(for: state, with: cornerRadius)
.reviewStateIcon(for: state)
.reviewStateColor(isRejected: state == .Rejected)
- .favoriteHeartIcon(for: asset)
- }
-}
-
-struct NewThumbnailImage_Previews: PreviewProvider {
- static var asset: PHAsset = PHAsset.fetchAssets(with: nil).firstObject!
-
- static var previews: some View {
- NewThumbnailImage(asset, isFocused: false)
- .environmentObject(PhotosLibrary())
- .previewDisplayName("thumbnail, not focused")
-
- NewThumbnailImage(asset, isFocused: true)
- .environmentObject(PhotosLibrary())
- .previewDisplayName("thumbnail, focused")
+ .favoriteHeartIcon(isFavorite)
}
}
BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailList.swift (539) → BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailList.swift (607)
diff --git a/BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailList.swift b/BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailList.swift
index c8990ad..bd777d0 100644
--- a/BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailList.swift
+++ b/BlinkReviewer/BlinkReviewer/Views/Thumbnails/NewThumbnailList.swift
@@ -9,13 +9,16 @@ import SwiftUI
struct NewThumbnailList: View {
@EnvironmentObject var photosLibrary: PhotosLibrary
- @EnvironmentObject var thumbnailManager: ThumbnailManager
@Binding var focusedAssetIndex: Int
var body: some View {
PHAssetHStack(photosLibrary.assets2) { asset, index in
- NewThumbnailImage(asset, isFocused: index == focusedAssetIndex)
- .environmentObject(photosLibrary)
+ NewThumbnailImage(
+ photosLibrary.getThumbnail(for: asset),
+ state: photosLibrary.state(of: asset),
+ isFavorite: asset.isFavorite,
+ isFocused: index == focusedAssetIndex
+ )
}
}
}