I'm trying to extract the image data from a UIImageView so I can upload it to Firebase Storage. However, iv.image?.jpegData() is returning nil. I'm using the standard Kingfisher library method to add the image from the URL to the UIImageView.
Here's my code:
let url = URL(string: "https://pbs.twimg.com/profile_images/1229497999392477186/BMXkjVEJ_400x400.jpg")
let iv = UIImageView()
iv.kf.setImage(with: url)
if let png = iv.image?.jpegData(compressionQuality: .leastNormalMagnitude){
filePath.putData(png, metadata: nil){ metadata, error in
print("metadata: \(metadata) |error: \(error)") // doesn't print
}
}
Any idea why iv.image?.jpegData() is nil? I've also tried iv.image?.pngData() and that is also nil.
As
iv.kf.setImage(with: url)
is asynchronous iv.image?.jpegData()/iv.image?.pngData() will be nil until the image is loaded from the server
setImage doesn't change the image property automatically. It has to potentially download the image from the Internet, which takes time.
Luckily, you can know when the download is completed by adding a completionHandler:
iv.kf.setImage(with: url, completionHandler: { result in
guard case .success(let imageResource) = result else {
// an error has occurred!
return
}
if let png = imageResource.image.jpegData(compressionQuality: .leastNormalMagnitude){
filePath.putData(png, metadata: nil){ metadata, error in
print("metadata: \(metadata) |error: \(error)") // doesn't print
}
}
})
As #Sh_Khan has explained you need to wait for the async call to finish. Here's is the fix:
let url = URL(string: "https://pbs.twimg.com/profile_images/1229497999392477186/BMXkjVEJ_400x400.jpg")
let iv = UIImageView()
iv.kf.setImage(with: url) { _ in
if let png = iv.image?.jpegData(compressionQuality: .leastNormalMagnitude){
filePath.putData(png, metadata: nil){ metadata, error in
print("metadata: \(metadata) |error: \(error)")
}
}
}
Related
I'm using a code I got off here to download some images and present them in a collection view.
Heres the code:
func downloadImage (url: URL, completion: () -> Void){
let session = URLSession(configuration: .default)
let downloadPicTask = session.dataTask(with: url) { (data, response, error) in
if let e = error {
print("Error downloading image \(e)")
} else {
if let res = response as? HTTPURLResponse {
print("Downloaded image with response code \(res.statusCode)")
if let imageData = data {
let image = UIImage(data: imageData)
self.globalImages.append(image!)
print("what does this say?-->", self.globalImages.count)
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
completion()
downloadPicTask.resume()
}
And I'm calling the download image in view did load where URL is the URL. (this URL works and I can download image).
downloadImage(url: url) { () -> () in
collectionView.ReloadData()
}
The completion handler I've tried calls reloadData() way too soon. I'm wanting it to be called when the image is finished downloading? so then the image can be displayed as soon as it's downloaded.
What is wrong with this code?
Please help!
You would call the completion handler as soon as you have the image. So, in your code:
if let imageData = data {
let image = UIImage(data: imageData)
self.globalImages.append(image!)
print("what does this say?-->", self.globalImages.count)
// call the completion handler here
Be aware, though, that you are likely to have other issues caused by data sharing across multiple threads, and also that your idea of storing the downloaded images successively in an array (globalImages) is not likely to work correctly.
I'm uploading an image with a transparent background to firebase. The background becomes white when I downloaded it somehow. Any idea how to fix this?
Here's my putData function.
let uploadData = imageview.image!.jpegData(compressionQuality: 0.75)
let uploadTask = imagesRef.putData(uploadData!, metadata: nil, completion: { (metadata, error) in
guard let metadata = metadata else {
return
}
And here is the download function.
URLSession.shared.dataTask(with: NSURL(string: imageURL as! String)! as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print("ERROR LOADING IMAGES FROM URL: \(String(describing: error))")
DispatchQueue.main.async {
imageView.image = UIImage()
}
return
}
DispatchQueue.main.async {
if let downloadedImage = UIImage(data: data!) {
imageCache.setObject(downloadedImage, forKey: NSString(string: imageURL!))
imageView.image = downloadedImage
}
}
}).resume()
I'm uploading an image with a transparent background to firebase. The
background becomes white when I downloaded it somehow. Any idea how to
fix this?
You're doing it right, except you're getting getting a JPEG instead of PNG data ;) Remember that JPEG does not support transparent BG.
Hope it helps!
I am attempting to pull an image url from Firebase Users profile.
When I call:-
#IBOutlet weak var profileImage: UIImageView?
profileImage?.image = Auth.auth().currentUser?.photoURL
I get the following error appear:-
Cannot assign value of type 'URL?' to type 'UIImage?'
I try to use the fixes that xcode provides but these only lead to further issues.
When I print:-
print(Auth.auth().currentUser?.photoURL as Any)
I get the following output, so I know there is an image there.
Optional(https://graph.facebook.com/10X60X70X35X101X5/picture)
How am I able to pull this image in, or is it possible to convert it to a string so I am able to use it?
Thanks in advance.
You can't assign url to image , use SDWebImage to download it asynchronously to not block main thread
if let url = Auth.auth().currentUser?.photoURL {
self.profImageV.sd_setImage(with: url , placeholderImage: UIImage(named: "edit2.png"))
}
The error message is very clear, you're trying to assign a URL? type to UIImage? type. Since it's a URL? you should first unwrap it, and download the image with this url. When you have your UIimage then you can assign it.
Actually you are converting URL type to UIImage type, that is not possible. You need to download image from url and set in an image view or wherever you want to use.
DispatchQueue.global(qos: .background).async {
if let url = URL(string: <#imageUrl#>) {
do {
let imgData = try Data(contentsOf: url)
if let image = UIImage(data: imgData) {
DispatchQueue.main.async {
self.imageView.image = image
}
}
} catch {
debugPrint("From catch block: Image could not be downloaded !!")
}
}
}
You need to download the image and then set the imageView's image to the downloaded image. You can do this using URLSession's dataTask:
// Playground code
import UIKit
import PlaygroundSupport
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
let session = URLSession.shared
let url = URL(string: "http://via.placeholder.com/400x400")!
_ = session.dataTask(with: url) { data, response, error in
guard error == nil else {
print(error)
return
}
guard let data = data else {
print("[DEBUG] - No data returned")
return
}
DispatchQueue.main.async {
imageView.image = UIImage(data: data)
}
}.resume()
PlaygroundPage.current.liveView = imageView
Or you can use a library such as KingFisher to handle it for you, then you just call it like so:
let url = URL(string: "url_of_your_image")
imageView.kf.setImage(with: url)
In my project, I show a UITableView, which currently has text describing a show's name and genre loading from a remote JSON file.
That all works. What I want next is to use the URL from the JSON file and load a thumbnail next to each show.
Using a tutorial, I have added a function to download the remote image with a print to test if it's successful.
if let shows_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let shows_obj = shows_list[i] as? NSDictionary
{
let show_name = shows_obj["show"] as? String
let show_genre = shows_obj["genre"] as? String
let show_image = shows_obj["thumbnail"] as? String
TableData.append(show_name! + " | " + show_genre!)
let testPictureURL = URL(string: show_image!)!
let session = URLSession(configuration: .default)
// Here's the download task where I'm grabbing the image
let downloadPicTask = session.dataTask(with: testPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
if let res = response as? HTTPURLResponse {
print("Downloaded picture with response code \(res.statusCode)")
if let imageData = data {
// Now I know I have data, so I think I can use UIImage to convert it into an image
let image = UIImage(data: imageData)
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
downloadPicTask.resume()
}
There are three items in the JSON array, and I get three printed statements that the picture was download: but the image does not appear.
My theory: since this is a table, maybe I have to add this as an accessory, but there isn't an image accessory subclass.
I am new to Swift -- do you have any ideas about how I should append this uploaded image to the table.
This is probably being caused by the asynchronous behavior of URLSession so when the requested image returns the view is already loaded.
To solve that, you can use a callback, for instance:
func myFunction(completion: (returnedImage -> UIIMage) -> Void){
//...
let downloadPicTask = session.dataTask(with: testPictureURL) { (data, response, error) in
//...
let image = UIImage(data: imageData)
completion(returnedImage: image)
//...
}
downloadPicTask.resume()
}
}
By using a callback, let's say that you have a method called myFunction(completion:), so now when you call the method you can handle whatever comes back from completion:
myFunction { (image) in
DispatchQueue.main.async { cell.imageView.image = image }
}
when my app wants to download avatar(profile image) it will crash and show me the Error but my image is not nil But the application detect that its nil here is the Error massage
fatal error: unexpectedly found nil while unwrapping an Optional value
and here is the print of my image Data and image that proves the image is not nil
, {0, 0}
10059 bytes
and here is my codes for downloading Avatar(profile Image)
let avatarUrl = URL(string: "http://example.com/uploads/avatar/\(EmailSignInViewController.avatar)")!
let session = URLSession(configuration: .default)
// Define a download task. The download task will download the contents of the URL as a Data object and then you can do what you wish with that data.
let downloadPicTask = session.dataTask(with: avatarUrl) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
// It would be weird if we didn't have a response, so check for that too.
if let res = response as? HTTPURLResponse {
print("Downloaded personal picture with response code \(res.statusCode)")
if let imageData = data {
// Finally convert that Data into an image and do what you wish with it.
print(imageData)
print(profileViewController.userImage)
if #available(iOS 10, *) {
profileViewController.userImage = UIImage(data: imageData )!
} else {
profileViewController.userImage = UIImage(named: "user.jpg")!
}
// Do something with your image.
} else {
print("Couldn't get image: Image is nil")
}
}
else {
print("Couldn't get response code for some reason")
}
}
}
downloadPicTask.resume()