I've got an array of image URLs (all from storage, not internet). How can I show the images themselves?
Simplified code:
#Binding var files:[URL]//Array of image URLs taken from an NSOpenPanel instance
Form{
if files.count>0{
Image(files[0].path)//Problem
}
}
You say NSOpenPanel, so I'm making going to make the assumption here that we don't need to worry about waiting to load the image over a network:
if let nsImage = NSImage(contentsOf: url) {
Image(nsImage: nsImage)
}
Related
I am building a sandboxed app for macOS with SwiftUI. I have a NSImage that is displayed as Image(nsImage: myNSImage). I want that View to support drag and drop, meaning that it can be dragged to any location that can receive image files.
Here’s my approach:
Image(nsImage: myNSImage)
.onDrag({
let itemProvider = NSItemProvider()
itemProvider.suggestedName = "image.png"
itemProvider.registerDataRepresentation(for: UTType.png) {
loadHandler in
loadHandler(nsImage.pngRepresentation, nil)
print("loadHandler completed") // Never prints !!
return nil
}
return itemProvider
})
This way I can drag the View. But it seems like it isn’t able to provide the image.
The “drop”, i.e. on the Desktop, is simply not working. I would expect that it saves an "image.png" in the destination URL, but it doesn’t.
How can I implement .onDrag so that it provides a image file based on NSImage?
Edit: I already have tried different UTTypes, for example .tiff in combination with nsImage.tiffRepresentation without luck.
I am following this tutorial to learn about drag and drop with SwiftUI.
https://swiftui-lab.com/drag-drop-with-swiftui/
This is my first time working with getting an image from a URL and the code in the tutorial uses NSImages for macOS, however, I need to build for iOS, therefore, NSImage must be changed to UIImage.
I was able to translate the NSImage to UIImage in the DragableImage struct, however, I am having trouble with the GridCell struct image. Specifically with unwrapping the URL into the img variable and returning it as an overlay on top of the Rectangle. See below code.
struct GridCell: View {
let active: Bool
let url: URL?
var body: some View {
// translate NSImage to UIImage here
let img = Image(nsImage: url != nil ? NSImage(byReferencing: url!) : NSImage())
.resizable()
.frame(width: 150, height: 150)
return Rectangle()
.fill(self.active ? Color.green : Color.clear)
.frame(width: 150, height: 150)
.overlay(img)
}
}
Any help to translate NSImage to UIImage is much appreciated.
If you're using iOS 15 - use AsyncImage instead to load from URL.
let img = AsyncImage(url: url)
Documentation here: https://developer.apple.com/documentation/swiftui/asyncimage
If you need to support iOS 14 or lower, one way to create a UIImage from URL is by defining an extension:
extension UIImage {
static func fromUrl(url: URL?) -> UIImage {
if let url = url,
let data = try? Data(contentsOf: url),
let image = UIImage(data: data) {
return image
} else {
return UIImage()
}
}
}
Then use it as:
let img = Image(uiImage: UIImage.fromUrl(url: url))
Note: if using UIImage and loading remote images, you probably want to load the image asynchronously, not on main thread and maybe in .onAppear - but that is beyond the scope of this question.
What I want to achieve
I want the KFImage to always read from memory and disk cache, not redownloading the image every time. The user will have a smooth experience without seeing the image downloading for a few seconds everytime the view loads.
What is wrong
The KFImage view load its images from the profileImages variable, which is initialized as empty list. That list does not get populated with URLs until getProfileImage() called which takes a few seconds.
Even though the image URL is the same, there would be a few seconds of gap right after the view appears, so KFImage will not load from its cache, and need to redownload images. Is it possible to make the KFImage view always load from cache even if URL change? Or is there another solution to my problem?
import KingfisherSwiftUI
struct ProfileSubview: View {
#State var profileImages: [URL] = []
var body: some View {
ForEach(0..<profileImages.count, id: \.self){index in
KFImage(profileImages[index])
}
}
.onAppear{
getProfileImage()
}
}
func getProfileImage(){
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
let storage = Storage.storage().reference().child("User_Profile")
storage.downloadURL{url, error in
if let error = error{
print("Error downloading image, \(error)")
}
profileImages.append(url!)
}
}
I have a bit of code in my app that generates a QR Code and scales it up (code reference I used from this link from Hackng with Swift. Now, I'm using the share sheet to allow the user to save the qr code to their camera roll and, it is working, but saving the image low res, and it saves to the camera roll blurry (and i assume if it is shared via other methods it will also be blurry)
Here is the code of my share sheet function:
struct ActivityView: UIViewControllerRepresentable {
let activityItems: [Any]
let applicationActivities: [UIActivity]?
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
return UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityView>) {
}
}
and here's the code in my view struct:
.sheet(isPresented: $showShareSheet) {
ShareSheet(activityItems: [self.qrCodeImage])
}
Is there a trick to remove the interpolation on the image when it saves to the share sheet like the .interpolation(.none) on the image view itself?
Your problem is that the QR code image is actually tiny! Like really tiny:
Printing description of image:
<UIImage:0x60000202cc60 anonymous {23, 23}>
When you share this image, the way it will be displayed is dependant on the program or app that will display it, and is out of control of your app as far as I know.
However,
there is a way that you could potentially make it "pretty" in other apps, and this would be to increase the resolution to a larger amount so that when it's rendered it'll appear to have "sharp" pixels.
How would this be accomplished? I think I have an example buried somewhere in old code, I'll dig into it and see if I can find you an example ;)
Edit
I found the code:
extension UIImage {
func resized(toWidth width: CGFloat) -> UIImage? {
let canvasSize = CGSize(width: round(width), height: CGFloat(ceil(width/size.width * size.height)))
UIGraphicsBeginImageContextWithOptions(canvasSize, false, scale)
defer { UIGraphicsEndImageContext() }
let context = UIGraphicsGetCurrentContext();
context?.interpolationQuality = .none
// Set the quality level to use when rescaling
draw(in: CGRect(origin: .zero, size: canvasSize))
let r = UIGraphicsGetImageFromCurrentImageContext()
return r
}
}
The trick is to provide a way to scale the image, but the real magic is on line 7:
context?.interpolationQuality = .none
If you exclude this line, you'll get blurry images, which is what the OS does by default because you don't generally want to see the pixel edges in images.
You could use this extension like so:
.sheet(isPresented: $showShareSheet) {
ShareSheet(activityItems: [self.qrCodeImage.resized(toWidth: 512) ?? UIImage()])
}
However, this may be resizing the image way more often than necessary. Optimally you'd resize it in the same function that you generate it.
I am working on an application, where I am loading some images from the server and showing it on table view using Alamofire. But the problem is it is taking so much time to load the image. I want to show the image, like a blurry image until the time image is not loading. Please, someone, help me.
cell.profileImageView?.pin_setImage(from: URL(string: modelArray[indexPath.row]))
This one is the solution using Kingfisher pod. You can set an activity Indicator while image is loaded.
let imageUrl = modelArray[indexPath.row]
//set activity indicator until image is loaded
cell.profileImageView?.kf.indicatorType = .activity
if let url = URL(string: imageUrl) {
let resource = ImageResource(downloadURL: url)
cell.profileImageView?.kf.setImage(with: resource)
}