SwiftUI Image does not display image from existing file - macos

In my SwiftUI app I have a view for people to edit a book's metadata, such as its title and author. In this view I have an image view that shows the book's cover image.
struct ImageView: View {
#Binding var book: Book
var body: some View {
// coverFile is the URL to the image file.
let image = NSImage(byReferencing:
book.metadata.coverFile)
Image(nsImage: image)
.aspectRatio(contentMode: .fit)
}
}
When I open a book that I've set a cover image and open the metadata editor, the image view displays nothing. The metadata editor has a button to choose a cover image. When I click the button and choose an image file from the file importer, the image appears in the image view. If I close the book, reopen it, and open the metadata editor, the image appears in the image view. But if I restart the app, open the book, and open the metadata editor, the image view displays nothing.
I have tried the various NSImage initializers. I tried building the image from a Data object and from NSBitmapImageRep.
extension Book {
func coverImage() -> NSImage {
do {
let data = try Data(contentsOf: metadata.coverFile)
return NSImage(data: data) ?? NSImage()
} catch {
Swift.print("Error reading the image data. Error: \(error)")
}
return NSImage()
}
}
struct ImageView: View {
#Binding var book: Book
var body: some View {
let image = book.coverImage()
Image(nsImage: image)
.aspectRatio(contentMode: .fit)
}
}
But when I set a breakpoint in the image view and check the image variable, it's either nil or invalid. When I check the coverFile variable, it displays the proper URL, including the .png extension.
In an AppKit version of this app, I have the same code to create the NSImage, and the image displays in the image view.
According to Apple's documentation, the NSImage byReferencing function does not attempt to retrieve the data from the specified URL or create any image representations from that data until an app attempts to draw the image or request information about it. How do I get the SwiftUI view to trigger retrieving the image data so it displays in the image view?
UPDATE
I created a computed property for the image in the ImageView struct.
var image: NSImage {
do {
let data = try Data(contentsOf: book.metadata.coverFile)
return NSImage(data: data) ?? NSImage()
} catch {
Swift.print("Error creating the image. Error: \(error)")
}
return NSImage()
}
When I run this code, the try Data call throws an exception, the catch block runs, and the following error message is printed in Xcode's Console:
Error creating the image. Error: Error Domain=NSCocoaErrorDomain
Code=257 "The file “ImageFile” couldn’t be opened because you
don’t have permission to view it."
Choosing a file from the file importer causes the data to be created properly. I have read/write permission for the folder where the image file is.

From what I understand the problem boils down to lazily initializing the NSImage.
Have you tried using init(contentsOfFile:) or init(contentsOf:)? It does not initialize it lazily therefore should not cause this problem.

The image does not display because of a file permissions problem. If I turn on the App Sandbox and give the app Read access to the Pictures folder, the cover image appears when I open the metadata editor.

Related

Unity changing from game object to ui Image

In unity I am fetching a UI image through GameObject.Find() However when I try to change the value of another image component to the result of the game object find I get the error:
Cannot implicitly convert type 'UnityEngine.GameObject' to 'UnityEngine.UI.Image' \[Assembly-CSharp\]csharp(CS0029)
Code:
Image image;
image = GameObject.Find("the other image");
Is there a way to fetch a UI Image game object through just it's name without it being set to a generic game object?
You are attempting to assign the value of a image to a gameObject, you simply need to get the image
Image image;
var imageGameObject = GameObject.Find("the other image");
if(imageGameObject == null)
{
//couldn't find the gameobject
}
image = imageGameObject.GetComponenet<Image>();
to get the actual image component

How to reference an image file in a SwiftUI iPad Playground?

I have added an image (jpg) from my Photo Album to the UserResources folder (via the + menu in the upper right of the Playground app). How do I reference that file when declaring a SwiftUI Image?
I have tried using the entire path:
let path: String = { Bundle.main.paths(forResourcesOfType: "JPG", inDirectory: nil)[0] }()
It seems to give me the path to the desired file, “PICT0024.JPG”:
/var/mobile/Containers/Data/PluginKitPlugin/F01FA67E-97CA-4590-915A-C8071507C6E0/Library/Application Support/Main Module Resources Bundles/55929C8C-46C9-4691-B86B-389D1473E9A8.bundle/Contents/Resources/PICT0024.JPG
I then declare:
Image(systemName: path)
But the image is not shown. I probably have the wrong reference, but cannot figure out what I need to specify.
Oh, just this
Image(“PICT0024.JPG”)
doesn’t work either.
What is the proper way to reference that image file?
Please confine answers to SwiftUI 5 and editing on an iPad. Using XCode at the moment isn’t an option.
Thanks.
Image(systemName: path)
this constructor one is for system SF Symbols
Image(“PICT0024.JPG”)
this constructor is for images in Assets catalog
The external images you have to use like this
Image(uiImage: UIImage(contentsOfFile: path))
import SwiftUI
import PlaygroundSupport
// Add image to the project with the "+" button,
// then select either the file, or image icon,
// then navigate to desired image,
// then add it to the iPad Playground project
let imageFileNameString = "IMG_2702.jpeg"
struct ContentView: View {
var body: some View {
// the method below requires the UIImage be unwrapped
Image(uiImage: UIImage(named: imageFileNameString)!)
// -or-
// The commented-out method below does not require any unwrapping.
// Typos will fail with "something went wrong around here" error
// Image(uiImage: UIImage(imageLiteralResourceName: imageFileNameString))
.resizable()
}
}
PlaygroundPage.current.setLiveView( ContentView() )
This is what I used:
https://www.appcoda.com/swiftui-swift-playgrounds-ipad-mac/
[Edit by original poster]
Summarizing from the article (worth a read because it discusses far more than how to access an image resource), here’s how to do it:
Type Image. and using the autocomplete bar, pick the moon and mountains icon (ImageLiteral)
That displays the “Images” popup. If you have not already added an image, choose either “Insert From...“, “Photo Library”, or “Take Photo”. Once you add an image, select it and press, “Use” (upper right corner of popover).
The result looks like this:
[The image here is of buttercups, Ranunculus acris.]

SwiftUI - onDrag Argument type 'NSImage' does not conform to expected type 'NSItemProviderWriting'

I'm trying several ways to implement image dragging (from my app to other apps) in macOS but none of them is working.
The image is a Data() object, which was taken from the clipboard, not an URL.
My code:
.onDrag {
return NSItemProvider(object: NSImage(data: self.item.value) ?? NSImage())
}
It says
Argument type 'NSImage' does not conform to expected type
'NSItemProviderWriting'
I tried with text and it's working. But can't find a way to drag an image.
The following works as Drag&Drop from testing SwiftUI app to TextEdit. Testing image image is stored in Assets.xcassets
Image("image")
.onDrag {
NSItemProvider(item: NSImage(named: "image")?.tiffRepresentation as NSSecureCoding?,
typeIdentifier: kUTTypeTIFF as String)
}

Loading gif image in background?

Description/ What I've tried
I imported a library that lets me beautifully load images asynchronously into my UITableView (https://github.com/natelyman/SwiftImageLoader) . The problem is that it doesn't support gifs. So I did case statement to independently retrieve the gif if need be and display the gif using another library.
Problem
Now the problem is that whenever I try loading a gif, the scrolling gets really choppy.
Question
Can someone please help me figure this out? Does it involve putting it on a background thread? If so, how?
Code
if let url = NSURL(string: arrayByVotes[indexPath.row].objectForKey("thumbnail") as NSString) {
let imageURL = arrayByVotes[indexPath.row].objectForKey("thumbnail") as NSString
if (imageURL.lowercaseString.rangeOfString("gif") != nil) {
cell.thumbnail.image = UIImage.animatedImageWithAnimatedGIFURL(url)
} else {
ImageLoader.sharedLoader.imageForUrl(arrayByVotes[indexPath.row].objectForKey("thumbnail") as NSString, completionHandler:{(image: UIImage?, url: String) in
cell.thumbnail.image = image
})
}
}
Maybe having many gif images in memory at the same time is affecting the performance of the scrolling. Try removing the images from the cells that are invisible and add the image only when the cell will show up. A good method for this would be tableView(_:willDisplayCell:forRowAtIndexPath:) from the UITableViewDelegate

How do I modify and return an NSImage?

This is probably a newb question, but I'm having trouble understanding how to manipulate NSImage instances.
I'm trying to create a method that will take an NSImage (an icon), draw another NSImage over it (a checkmark), then return the modified NSImage instance.
I've looked at the section "Drawing to an Image" in the Cocoa Drawing Guide, but I still can't figure out how to return a modified NSImage.
Looking for something like:
(NSImage*) drawCheckbox:(NSImage*)originalImage {
NSImage* checkbox = [NSImage imageNamed:#"checkbox"];
// create and return new NSImage with checkbox drawn over originalImage
}
TIA!
First, make a copy of the original image. Then, draw into the new image as described in the documentation you mentioned. Finally, return your altered image using the return statement.

Resources