Skip to main content

render a full-sized image

ID
7be2ee1
date
2023-06-08 06:59:03+00:00
author
Alex Chan <alex@alexwlchan.net>
parent
e6c1f9f
message
render a full-sized image
changed files
3 files, 101 additions, 13 deletions

Changed files

BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj (22748) → BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj (23190)

diff --git a/BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj b/BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj
index 37293c8..0084083 100644
--- a/BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj
+++ b/BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj
@@ -15,6 +15,7 @@
 		94D7510C2A31A798005859E7 /* BlinkReviewerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D7510B2A31A798005859E7 /* BlinkReviewerUITests.swift */; };
 		94D7510E2A31A798005859E7 /* BlinkReviewerUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D7510D2A31A798005859E7 /* BlinkReviewerUITestsLaunchTests.swift */; };
 		94D7511C2A31A7B1005859E7 /* ThumbnailItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D7511B2A31A7B1005859E7 /* ThumbnailItem.swift */; };
+		94D7511E2A31B243005859E7 /* PreviewImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D7511D2A31B243005859E7 /* PreviewImage.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -47,6 +48,7 @@
 		94D7510B2A31A798005859E7 /* BlinkReviewerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlinkReviewerUITests.swift; sourceTree = "<group>"; };
 		94D7510D2A31A798005859E7 /* BlinkReviewerUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlinkReviewerUITestsLaunchTests.swift; sourceTree = "<group>"; };
 		94D7511B2A31A7B1005859E7 /* ThumbnailItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailItem.swift; sourceTree = "<group>"; };
+		94D7511D2A31B243005859E7 /* PreviewImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewImage.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -136,6 +138,7 @@
 			isa = PBXGroup;
 			children = (
 				94D7511B2A31A7B1005859E7 /* ThumbnailItem.swift */,
+				94D7511D2A31B243005859E7 /* PreviewImage.swift */,
 			);
 			path = Views;
 			sourceTree = "<group>";
@@ -272,6 +275,7 @@
 			files = (
 				94D7511C2A31A7B1005859E7 /* ThumbnailItem.swift in Sources */,
 				94D750F22A31A796005859E7 /* ContentView.swift in Sources */,
+				94D7511E2A31B243005859E7 /* PreviewImage.swift in Sources */,
 				94D750F02A31A796005859E7 /* BlinkReviewerApp.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

BlinkReviewer/BlinkReviewer/ContentView.swift (1658) → BlinkReviewer/BlinkReviewer/ContentView.swift (1679)

diff --git a/BlinkReviewer/BlinkReviewer/ContentView.swift b/BlinkReviewer/BlinkReviewer/ContentView.swift
index ee99695..39dcb8c 100644
--- a/BlinkReviewer/BlinkReviewer/ContentView.swift
+++ b/BlinkReviewer/BlinkReviewer/ContentView.swift
@@ -19,23 +19,19 @@ struct AssetData: Codable, Identifiable {
 }
 
 struct ContentView: View {
-    var allPhotos: [AssetData] {
-        var photos: [AssetData] = []
+    var allPhotos: [PHAsset] {
+        var photos: [PHAsset] = []
         
         PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
             .enumerateObjects({ (asset, _, _) in
-                photos.append(
-                AssetData(
-                    localIdentifier: asset.localIdentifier,
-                    creationDate: asset.creationDate?.ISO8601Format(),
-                    isFavorite: asset.isFavorite
-                )
-            )
-        })
+                photos.append(asset)
+            })
         
         return photos
     }
     
+    @State private var selectedAsset: PHAsset? = nil
+    
     var body: some View {
         VStack {
             Divider()
@@ -47,16 +43,20 @@ struct ContentView: View {
                     .padding()
                     
                     LazyHStack(spacing: 10) {
-                        ForEach(allPhotos) { photo in
+                        ForEach(allPhotos, id: \.localIdentifier) { photo in
                             ThumbnailItem(label: "\(photo.localIdentifier)")
                         }
                     }.padding()
                 }.frame(height: 100)
             }
+            Divider()
             
+            if let thisSelectedAsset = selectedAsset {
+                PreviewImage(asset: thisSelectedAsset)
+            }
+        }.onAppear {
+            selectedAsset = allPhotos[0]
         }
-        Divider()
-        Spacer()
     }
 }
 

BlinkReviewer/BlinkReviewer/Views/PreviewImage.swift (0) → BlinkReviewer/BlinkReviewer/Views/PreviewImage.swift (2633)

diff --git a/BlinkReviewer/BlinkReviewer/Views/PreviewImage.swift b/BlinkReviewer/BlinkReviewer/Views/PreviewImage.swift
new file mode 100644
index 0000000..9eabcf8
--- /dev/null
+++ b/BlinkReviewer/BlinkReviewer/Views/PreviewImage.swift
@@ -0,0 +1,84 @@
+//
+//  PreviewImage.swift
+//  BlinkReviewer
+//
+//  Created by Alex Chan on 08/06/2023.
+//
+
+import Photos
+import SwiftUI
+
+extension PHAsset {
+    /// Create an NSImage at the given size.
+    func getImage(forWidth width: Double, forHeight height: Double) -> NSImage {
+    // This implementation is based on code in a Stack Overflow answer
+    // by Francois Nadeau: https://stackoverflow.com/a/48755517/1558022
+    //
+    // I've added more comments and error-handling logic.
+
+    let options = PHImageRequestOptions()
+    options.isSynchronous = true
+
+    // If i don't set this value, then sometimes I get an error like
+    // this in the `info` variable:
+    //
+    //      Error Domain=PHPhotosErrorDomain Code=3164 "(null)"
+    //
+    // This means that the asset is in the cloud, and by default Photos
+    // isn't allowed to download assets here.  Apple's documentation
+    // suggests adding this option as the fix.
+    //
+    // See https://developer.apple.com/documentation/photokit/phphotoserror/phphotoserrornetworkaccessrequired
+    options.isNetworkAccessAllowed = true
+
+    var image = NSImage()
+
+    PHImageManager.default()
+      .requestImage(
+        for: self,
+        targetSize: CGSize(width: width, height: height),
+        contentMode: .aspectFit,
+        options: options,
+        resultHandler: { (result, info) -> Void in
+
+          // If we fail to get a result, print a message to the user that
+          // includes the value of `info`.  For information about interpreting
+          // these keys, see Apple's documentation:
+          // https://developer.apple.com/documentation/photokit/phimagemanager/image_result_info_keys
+          switch (result, info) {
+          case let (result?, _):
+            image = result
+          case let (.none, info?):
+            fputs("Unable to create image:\n", stderr)
+            fputs("\(info)\n", stderr)
+            exit(1)
+          case (.none, .none):
+            fputs("Unable to create image:\n", stderr)
+            fputs("(unknown error)\n", stderr)
+            exit(1)
+          }
+        })
+
+    return image
+  }
+}
+
+struct PreviewImage: View {
+    var asset: PHAsset
+    
+    var body: some View {
+        VStack {
+            GeometryReader { geometry in
+                HStack {
+                    Spacer()
+                    
+                    Image(nsImage: asset.getImage(forWidth: geometry.size.width, forHeight: geometry.size.height))
+                        .resizable()
+                        .aspectRatio(contentMode: .fit)
+                    
+                    Spacer()
+                }
+            }
+        }.padding()
+    }
+}