I want to load a gif image on a UIImage using JSQMessageView in cell. I can not access any UIImageView so I did this to change media view. But, the images do not animate. Is it possible to do what I want?
Here is my code:
let urlString = Helpers.getUrlFromGiphyText(text: message.body!)
if let url = URL(string: urlString) {
KingfisherManager.shared.retrieveImage(with: url, options:
[.processor(DefaultImageProcessor.default),
.cacheSerializer(FormatIndicatedCacheSerializer.gif)],
progressBlock:
{ receivedSize, totalSize in
print("(indexPath.row + 1): (receivedSize)/(totalSize)")
}, completionHandler: { image, error, cacheType, imageURL in
if let gifImage = image {
let mediaItem = JSQPhotoMediaItem(image: gifImage)
cell.mediaView = mediaItem?.mediaView()
}
})
}
I wrote this media fetcher method for my app, and it works fine with gif files.
NOTE: Just notice that I use DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) after the kingfisher manager downloads each file, since without the delay - the gifs don't play at the first download.
I wrecked my mind on this one. I figure this is probably some cache syncing issue of Kingfisher.
mediaFetcher.downloadGifFile(url) { imageUrl in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// Use whatever u want to load the gif file
}
}
class MediaFetcher {
func downloadGifFile(_ url: URL, completion: #escaping (_ imageURL: URL) -> () = { (imageURL) in} ) {
KingfisherManager.shared.retrieveImage(with: url, options: .none, progressBlock: nil) { (image, error, cacheType, imageUrl) in
guard error == nil else {
return
}
guard let imageUrl = imageUrl else {
return
}
completion(imageUrl)
}
}
}
Related
I'm trying to download image and store that image in cache. From next attempt, pull that image from cache if exists otherwise download that image
I've gone thru similar posts but nothing helps me to fix.
Please advice
let imageCache = NSCache<NSString, UIImage>()
func getImage(from url: URL, completion: #escaping ((UIImage?, Error?)->(Void))) {
if let cachedImage = imageCache.object(forKey: url.absoluteString as NSString) {
print("Image from cache")
completion(cachedImage, nil)
}
NetworkOperations().downloadImage(from: url) { (data, error) in
if let error = error {
completion(nil, error)
} else if let imgData = data, let image = UIImage(data: imgData) {
self.imageCache.setObject(image, forKey: url.absoluteString as NSString)
print("Image from cache")
completion(image, nil)
}
}
}
When i call this method, it always picks by downloading image. I'm struck here
Create a shared cache at the application level and add/retrieve/remove wherever you want as below,
class ImageCache {
private init() {}
static let shared = NSCache<NSString, UIImage>()
}
Usage
func getImage(from url: URL, completion: #escaping ((UIImage?, Error?)->(Void))) {
if let cachedImage = ImageCache.shared.object(forKey: url.absoluteString as NSString) {
print("Image from cache")
completion(cachedImage, nil)
}
NetworkOperations().downloadImage(from: url) { (data, error) in
if let error = error {
completion(nil, error)
} else if let imgData = data, let image = UIImage(data: imgData) {
ImageCache.shared.setObject(image, forKey: url.absoluteString as NSString)
print("Image from cache")
completion(image, nil)
}
}
}
I hope, this will fix the issue and it should also give you an insight of what is being wrong with your implementation. Most probably your cache has the same scope as the object where it is declared and used so once the object is de-initialised, the cache will also be released.
In the above implemtation, cache is declared as a static so that it is never de-initialized but you can clear the cache anytime with the following,
ImageCache.shared.removeAllObjects()
I am new to swift. I am loading image with url
mainImageView.setImageWith(URL(string: ("https:" + (content?.imagePath)!)), placeholderImage: nil)
print("dimensions after loading \(String(describing: mainImageView.image?.size))")
In case, I print the dimensions of the image as shown above then dimensions come out to be 21*6.5. However, if I wait for sometime and then print the dimensions are 188*109. How do I add a completion block to setImageWith so that I can come to know when image has finished loading?
You can use Sdwebimage for loading the image with completion block https://github.com/rs/SDWebImage
imageView.sd_setImageWithURL(NSURL(string: urlString), completed: {
(image, error, cacheType, url) in
// you can get the image width here...
})
It is happening because URL will always take a time to load image thats why first you got 21*6.5 dimensions and then got real dimensions 188*109.
As best way to prefer 3rd party library SDWebImage that will manage all the thing, You just need to set image URL.
There is method name is
open func sd_setImage(with url: URL!, placeholderImage placeholder: UIImage!, options: SDWebImageOptions = [], completed completedBlock: SDWebImage.SDWebImageCompletionBlock!)
that has completion block so you can manage whatever you want.
Convert image URL into data then Data into UIIamge, Here is a function:
func getImageFromUrl(_ strUrl: String, completionHandler handler: #escaping (_ img: UIImage) -> Void) {
DispatchQueue.global(qos: .background).async {
let url = URL(string: strUrl)
let dataFromUrl = Data(contentsOf: url!)
if dataFromUrl == nil {
return
}
DispatchQueue.main.async(execute: {() -> Void in
handler(UIImage(data: dataFromUrl!))
})
})
}
Use This: -
let imageCache = NSCache<AnyObject, AnyObject>()
typealias CompletionHandler = (_ success:Bool, _ image:UIImage?) -> Void
func loadImageUsingCacheWithUrlString(_ urlString:
String,completionHandler: #escaping CompletionHandler) {
let image = UIImage()
//check cache for image first
if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
image = cachedImage
completionHandler(true, image!)
return
}
if urlString.characters.count == 0 {
completionHandler(false, image)
return
}
//otherwise fire off a new download
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
//download hit an error so lets return out
if error != nil {
print(error ?? "")
completionHandler(false,nil)
return
}
DispatchQueue.main.async(execute: {
if let downloadedImage = UIImage(data: data!) {
image = downloadedImage
imageCache.setObject(downloadedImage, forKey: urlString as AnyObject)
completionHandler(true,image)
}
})
}).resume()
}
appName[8121:97068] 8121: CFNetwork internal error (0xc01a:/BuildRoot/Library/Caches/com.apple.xbs/Sources/CFNetwork_Sim/CFNetwork-808.2.16/Loading/URLConnectionLoader.cpp:304)
I have a chat app, and I want to send an image to others.
I: IPhone ; others: simulator
I take a photo by camera, and press "use this photo",then I'll send an url to others and upload image to server.
Others receive url message immediately, and I use sdWebimage to show this image.
But,when I receive the image, the image request print log error.
log say this image don't exist.
I dont know make this image download error,whether the image is uploading and the server don't have this image or not.
How can prevent this situation or have any function to set download again after 10 seconds of downloading error ?
this is my sd_image func:
cell.photoImageView.sd_setImage(with: url, placeholderImage: nil, options: .progressiveDownload, progress: nil
, completed: { (image, error, cacheType, url) in
guard image != nil else{
print("Image not exist!")
cell.photoImageView.image = resizeImage(image:#imageLiteral(resourceName: "img_refresh"), newWidth: 125)
return
}
print("image here!!!")
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
if data != nil
{
if let image = UIImage(data: data!)
{
if !FileManager.default.fileExists(atPath: fileURL.path) {
if let jpegData = UIImageJPEGRepresentation(image, 0.001)
{
do {
try jpegData.write(to: fileURL, options: .atomic)
print("image save local done!!!")
} catch {
debug(object: error)
}
}
} else {
print("image already esist")
}
DispatchQueue.main.async {
cell.photoImageView.image = resizeImage(image: image, newWidth: 175)
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}
}
}
}
})
uploading
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let uuid = NSUUID().uuidString
let imageName:String = chatroomId + "_" + uuid + ".jpg"
let documentsPath = NSHomeDirectory().appending("/Documents/\(chatroomId)/")
let imagePath = documentsPath.appending(imageName)
let imageUrl = URL(fileURLWithPath: imagePath)
print("imageUrl is here:\(imageUrl)")
photoImage = info[UIImagePickerControllerOriginalImage] as? UIImage
if picker.sourceType == .camera {
photoImage = info[UIImagePickerControllerOriginalImage] as? UIImage
UIImageWriteToSavedPhotosAlbum(photoImage!, nil, nil, nil)
}
let imageData:Data = UIImageJPEGRepresentation(photoImage!, 0.001)!
do {
try imageData.write(to: imageUrl,options: .atomic)
} catch let error {
print(error)
}
//uploading
let objectKey:String = "chatroom/" + imageName
server.uploadObjectAsync(imageUrl, objectKey: objectKey)
let message = server.url + imageName
self.room.send(message: message)
self.tableView.scrollToBottom()
self.tableView.reloadData()
self.dismiss(animated: true, completion: nil)
}
try to add .allowInvalidSSLCertificates to your sd_setImage function :
cell.photoImageView.sd_setImage(with: url, placeholderImage: nil, options: .allowInvalidSSLCertificates, progress: nil
, completed: { (image, error, cacheType, url) in
I need to fetch thumbnail from a video URL to display in ImageView that is in a UITableViewCell.
I can get thumbnail using this method but it takes lots of resource and hence the tableview scrolling lags and stops for the time cellForRowAtIndexPath is called.
This is the method.
func createThumbnailOfVideoFromFileURL(videoURL: String) -> UIImage? {
let asset = AVAsset(url: URL(string: videoURL)!)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(Float64(1), 100)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
return thumbnail
} catch {
return UIImage(named: "ico_placeholder")
}
}
Can anyone please suggest me an alternative to use SDWebImage so that the images go to the cache and are not fetched everytime UITableView delegates are called.
Kindly suggest any other approach if i m wrong.
I used the above method as it is and cached the images in an array of UIImage and made a check in cellForRowAtIndexPath that if image exists at that particular index then use that else fetch the image in background.
Not using SDWebImage but this code works like a charm
func getThumbnailFromUrl(_ url: String?, _ completion: #escaping ((_ image: UIImage?)->Void)) {
guard let url = URL(string: (url ?? "")) else { return }
DispatchQueue.main.async {
let asset = AVAsset(url: url)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMake(value: 2, timescale: 1)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
completion(thumbnail)
} catch let error{
print("Error :: ", error)
completion(nil)
}
}
}
Usage
self.getThumbnailFromUrl(video.url.description) { (image) in
//Use image where you want to use
}
I am new to iOS i want download image to display it is working code but here lot of code duplication
let url = URL(string: iteminfo.imageUrl!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error != nil {
print(error)
}
if let data = data {
print(data)
self.imageViewItemPic.image = UIImage(data: data)
}
}
task.resume()
let url2 = URL(string: iteminfo.cookerProfilePicUrl!)
let urlRequest2 = URLRequest(url: url2!)
let task2 = URLSession.shared.dataTask(with: urlRequest2) { (data, response, error) in
if error != nil {
print(error)
}
if let data = data {
print(data)
self.imageViewCookerProfilePic.image = UIImage(data: data)
}
}
task2.resume()
So I want to reuse my code but i unfortunately i can not reach my goal. there have no error and url is correct . every time goes else statement . i am missing something but what is that ?
if let image = downlaodImage(urlImage: iteminfo.imageUrl){
print("first \(image)")
imageViewItemPic.image = image
}else{
print("first wrong......")
}
if let image = downlaodImage(urlImage: iteminfo.cookerProfilePicUrl){
print("second \(image)")
imageViewCookerProfilePic.image = image
}
else{
print("second wrong......")
}
Here is my method :
func downlaodImage(urlImage : String?) -> UIImage?{
var image : UIImage?
let url = URL(string: urlImage!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
// print(data)
image = UIImage(data: data)
}
}
task.resume()
return image
}
note: I am not sure is it best way or not . if it is not best practice feel free to guide me .
There's no need of so much hassle. You have the URL of the image so you can simply download the image from the URL. For example:
func downloadImage(imageURL: String) {
DispatchQueue.global().async {
let data = NSData.init(contentsOf: NSURL.init(string: imageURL) as! URL)
DispatchQueue.main.async {
let image = UIImage.init(data: data as! Data)
imageView.image = image
}
}
}
Edit: To reuse this code I would suggest to use extension of UIImageView. Here's an example:
extension UIImageView {
func setImageFromURL(url: String) {
DispatchQueue.global().async {
let data = NSData.init(contentsOf: NSURL.init(string: url) as! URL)
DispatchQueue.main.async {
let image = UIImage.init(data: data as! Data)
self.image = image
}
}
}
}
Use this method whenever you want to set the image of an imageView from a url like this:
self.imageViewCookerProfilePic.setImageFromURL(url: "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQNpKmjx1w3DRDZ9IXN81-uhSUA6qL6obkOthoUkb9RZkXf5pJ8")
Dude. You should learn some staff about async and sync code.
Here is the thing. Code in you downloadImage works synchronically, so it pass you URLTask and go straight to return, there you return you image variable, that is nil.
One of the solutions in to use callback block like this:
func downloadImage(urlImage : String?, complete: ((UIImage?)->Void)? = nil){
let url = URL(string: urlImage!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
complete?(UIImage(data: data))
}
}
task.resume()
}
And then use it like this:
{ ...
downloadImage(urlImage: "", complete: { image in
if let image = image{
self.imageViewItemPic.image = image
}else{
print("no image")
}
})
...
}
You should read some tutorials about async code and web in swift. You could start with this site
downlaodImage() downloads an image asynchronously so
if let image = downlaodImage(...) { ... }
is always going to fail because program execution has continued before your response data has come back.
It would be easier just to set your images in the callback function closure of downlaodImage() as below by adding a UIImageView parameter to downlaodImage(). This way you can reduce the repetition of if else blocks by moving them to the downlaodImage function.
func downlaodImage(urlImage : String?, imageView: UIImageView) -> UIImage?{
var image : UIImage?
let url = URL(string: urlImage!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
// print(data)
if let image = UIImage(data: data) {
imageView.image = image
} else {
print("failed to load image")
}
}
}
task.resume()
return image
}
Simplified code without if/else blocks
downlaodImage(urlImage: "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQNpKmjx1w3DRDZ9IXN81-uhSUA6qL6obkOthoUkb9RZkXf5pJ8", imageView: imageViewItemPic)
downlaodImage(urlImage: "https://www.dominos.co.nz/ManagedAssets/OLO/eStore/all/Product/NZ/P015/P015_ProductImage_Small_en_Default_20140203_105245.png", imageView: imageViewCookerProfilePic)