I have a bunch of annotations on a map, which all have a custom photos. Some of the photos may not be downloaded to the application yet from Firebase, so if they do not the image available, it defaults to a white circle image and initiates a download. When the download completes, it does not set the new image. How can I go about this?
Heres some code:
func generateAnnotations() {
for photo in photos {
let annotation = DetailPhotoPointAnnotation()
let latitude = photo.latitude
let longitude = photo.longitude
annotation.coordinate.latitude = latitude
annotation.coordinate.longitude = longitude
if photo.image != nil {
annotation.image = photo.image
} else {
annotation.image = #imageLiteral(resourceName: "whiteCircle")
let path = getDocumentsDirectory().appendingPathComponent(photo.uid)
if let image = UIImage(contentsOfFile: path) {
annotation.image = image
} else {
let ref = FIRStorage.storage().reference(forURL: photo.imageUrl)
ref.data(withMaxSize: 5*1024*1024, completion: { (data, error) in
if error != nil {
print(error!)
} else {
if let imageData = data {
if let image = UIImage(data: imageData) {
photo.assignImage(image: image)
annotation.image = image
}
}
}
})
}
}
self.coordinates.append(CLLocationCoordinate2DMake(latitude, longitude))
self.mapView.addAnnotation(annotation)
}
generateOverlay()
}
As you can see, it first looks to if the photo object contains an image. If it doesn't, it looks in the documents directory for that image. If its not there, it will finally download it. Any suggestions?
You need to do something like this. Go to main thread. And then update
DispatchQueue.main.async(execute: {
imageView.image = image
})
In my solution it works.
Hope this helps.
Related
I need to load an ImageView inside UIcollectionViewcell using a URL that I pass during initialisation:
func configureCellWith(messageModel : MessageModel){
guard let url = URL(string: messageModel.contentUrl!) else { return }
if url.isURLPhoto(){
likedImageView.sd_setImage(with: url, placeholderImage: nil)
}
else if url.isURLVideo(){
getThumbnailImageFromVideoUrl(url: url) { (image) in
self.likedImageView.image = image
}
}
If url is video I need to load the image in this way using this method:
func getThumbnailImageFromVideoUrl(url: URL, completion: #escaping ((_ image: UIImage?)->Void)) {
DispatchQueue.global().async {
let asset = AVAsset(url: url)
let avAssetImageGenerator = AVAssetImageGenerator(asset: asset)
avAssetImageGenerator.appliesPreferredTrackTransform = true
let thumnailTime = CMTimeMake(value: 2, timescale: 1)
do {
let cgThumbImage = try avAssetImageGenerator.copyCGImage(at: thumnailTime, actualTime: nil)
let thumbNailImage = UIImage(cgImage: cgThumbImage)
DispatchQueue.main.async {
completion(thumbNailImage)
}
} catch {
print(error.localizedDescription)
DispatchQueue.main.async {
completion(nil)
}
}
}
}
As visible I retrieve the initial frame of the video and I load it inside the cell, obviously since it's an asynchronous function it will take some time for loading the image, there's no problem In that.
The problem occurs when I scroll through the collection and I see that some cells display images which don't correspond to the correct ones.
Searching online I found out that I need to clear the image in prepareForReuse of the cell and so I did (both in case the image is loaded through sd_setImage and though getThumbnailImageFromVideoUrl function):
override func prepareForReuse() {
super.prepareForReuse()
self.likedImageView.image = UIImage()
self.likedImageView.image = nil
self.likedImageView.sd_cancelCurrentImageLoad()
}
but I still get images mismatched when scrolling thought the collection view, what could be the problem?
I think the issue is not with images, i guess its with video thumbnail. You generate a thumbnail on background thread synchronously but while setting it back to imageView you never bothered to find if the cell is reused and the image u just created is outdated or not.
So in your cell
var currentModel: MessageModel! = nil //declare a instance variable to hold model
... other code
func configureCellWith(messageModel : MessageModel){
self.currentModel = messageModel //keep a copy of model passed to u as argument
guard let url = URL(string: messageModel.contentUrl!) else { return }
if url.isURLPhoto(){
likedImageView.sd_setImage(with: url, placeholderImage: nil)
}
else if url.isURLVideo(){
getThumbnailImageFromVideoUrl(url: url) { (image) in
self.likedImageView.image = image
}
}
Finally in getThumbnailImageFromVideoUrl
func getThumbnailImageFromVideoUrl(url: URL, completion: #escaping ((_ image: UIImage?)->Void)) {
DispatchQueue.global().async {
let asset = AVAsset(url: url)
let avAssetImageGenerator = AVAssetImageGenerator(asset: asset)
avAssetImageGenerator.appliesPreferredTrackTransform = true
let thumnailTime = CMTimeMake(value: 2, timescale: 1)
do {
let cgThumbImage = try avAssetImageGenerator.copyCGImage(at: thumnailTime, actualTime: nil)
let thumbNailImage = UIImage(cgImage: cgThumbImage)
if url.absoluteString == currentModel.contentUrl { //check if image you generated is still valid or its no longer needed
DispatchQueue.main.async {
completion(thumbNailImage)
}
}
} catch {
print(error.localizedDescription)
DispatchQueue.main.async {
completion(nil)
}
}
}
I have been recently trying to download an image from Firebase and it is simply not cooperating. Any help would be appreciated!
This is the function I used to download the image.
func downloadDefaultProfileImg() {
let imgRef = Storage.storage().reference(forURL: myurllol).child("defaultProfilePicture.jpg")
imgRef.getData(maxSize: 1 * 1024 * 1024) {
data, error in
if let error = error {
print("Nice try idiot" + "\(error.localizedDescription)")
} else {
let image = UIImage(data: data!)
DispatchQueue.main.async {
self.image = image
}
}
}
}
This is the image variable I have defined at the top of my class:
var image: UIImage? = nil
And lastly, this is where I call my function:
self.downloadDefaultProfileImg()
guard let defaultProfileImage = self.image else {
print("nope1")
return
}
guard let imageData = defaultProfileImage.jpegData(compressionQuality: 0.4) else {
print("nope2")
return
}
"nope1" appears in the console. Any help would be greatly appreciated, thank you so much!
is there any way to modify/edit phasset image by cropping or edited one?
I have array of asset, I wanna crop image of asset by getting image from selected asset and passing it cropping controller and in return want to change that cropped image in selected asset.
There is my code to understand well
func presentCropViewController(with:IndexPath) {
self.allPhotos[with.item].getImage { (img) in
if let image = img{
self.indexPathForCropping = with
let cropViewController = CropViewController(image: image)
cropViewController.delegate = self
cropViewController.view.tintColor = UIColor.themeGreen()
self.present(cropViewController, animated: true, completion: nil)
}
}
}
after passing image from asset I got cropped image with this method
func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
cropViewController.dismiss(animated: true, completion: nil)
// Here i get the cropped image and want to update selected asset with this image
}
I'll appreciate if you mentioned down vote reason too so I prepare my question accordinglly
I figured out the solution may its not an efficient way to do but solved my problem
extension PHAsset {
func updateChanges(with img:UIImage,completion:#escaping(PHAsset?)->()){
PHPhotoLibrary.shared().performChanges({
// create cropped image into phphotolibrary
PHAssetChangeRequest.creationRequestForAsset(from: img)
}) { (success, error) in
if success{
// fetch request to get last created asset
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
fetchOptions.fetchLimit = 1
let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
if let asset = fetchResult.firstObject{
// replace your selected asset with new cropped one
completion(asset)
}else{
completion(nil)
}
}else{
completion(nil)
}
}
}
}
simply pass cropped/modified image and get new asset with same cropped/modified image
In my project trying to import GIF from UIImage picker. GIF image picked from gallery will certain actions like play and stop. Right now I can able to select GIF mage from gallery but result shows as image not gif image.
Can anyone help me out of this.
Try this it will help you.
Step1: Create a global variable
var IMG:String?
Step2: put this function in your code.
func getFileName(info: [String : Any]) -> String {
if let assetPath = info[UIImagePickerControllerReferenceURL] as? URL {
let result = PHAsset.fetchAssets(withALAssetURLs: [assetPath], options: nil)
let asset = result.firstObject
let fileName = asset?.value(forKey: "filename")
let fileUrl = URL(string: fileName as! String)
if let name = fileUrl?.deletingPathExtension().lastPathComponent {
print(name)
//let assetPath = info[UIImagePickerControllerReferenceURL] as! NSURL
if (assetPath.absoluteString.hasSuffix("JPG")) {
sonu = "JPG"
print("JPG")
}
else if (assetPath.absoluteString.hasSuffix("PNG")) {
sonu = "PNG"
print("PNG")
}
else if (assetPath.absoluteString.hasSuffix("GIF")) {
sonu = "GIF"
print("GIF")
}
else {
sonu = "Unknown"
print("Unknown")
}
return name
}
}
return ""
}
Step3: Set this code in your ImagePickerController
if let image = info[UIImagePickerControllerEditedImage] as? UIImage
{
imageview.image = image
let IMG = getFileName(info: info)
print(IMG)
if IMG == "JPG"
{
imageDataForApi = UIImagePNGRepresentation(image)
}
else if IMG == "GIF"
{
imageDataForApi = UIImageJPEGRepresentation(image, 0)
}
}
self.dismiss(animated: true) { () -> Void in
}
I use this code to load users usernames when a user search for another user
var user: PFUser? {
didSet {
userNameLabel.text = user?.username
}
}
How can I do the same thing but for profile pictures?
Here is what you can do.
case 1
If you need to download the image and display then -
Create a property that holds url for the image.
Asynchronously download image and display
So code would look this this
var pictureURL: NSURL?
var user: PFUser? {
didSet {
userNameLabel.text = user?.username
pictureURL = user?.pictureURL
displayPicture()
}
}
private func displayPicture() {
// Async download pic and display
}
case 2
If you have the image locally then just display -
var user: PFUser? {
didSet {
userNameLabel.text = user?.username
userImage.image = UIImage(named: "Picturename")
}
}
You can not save your image file with UIImage format on Parse.com. You can store the file with PFFile and you have to convert your image to PFFile for saving and getting the image.
You can save your Image with this function;
private func saveLogo() {
let yourLogoImageFile = UIImage(named: "YourLogoFileName")
if let imageData = UIImageJPEGRepresentation(yourLogoImageFile!, 0.1) {
// You got the PFFile you can use this for save.
let imagePFFile = PFFile(name: "logo.png", data: imageData)
logo = imagePFFile // Send this PFFile to your variable or just save.
// Saving...
imagePFFile?.saveInBackground()
}
}
After that you can get your logo PFFile from Parse and you have to convert it to UIImage with this function;
private func getLogo() {
if let image = yourLogoVariable {
image.getDataInBackgroundWithBlock() {
(let imageData, error) in
if error == nil {
if let logoImageData = imageData {
if let logoImage = UIImage(data: logoImageData) {
// logoImage is the UIImage you can use this for your UIImageView.
}
}
}
}
}
}