Showing UIImageView after finished loading - swift

Currently I have a collection view of thumbnail images, Upon pressing that thumbnail image cell then it should call a function that would retrieve the Image data via API and shows the full image via the hidden ImageView.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
dataManager.downloadAttachment(id: attachments[indexPath.row].attachmentID)
if dataManager.dataHolder != nil {
attachmentImage.image = UIImage.init(data: dataManager.dataHolder!)
attachmentImage.isHidden = false
}
print(attachments[indexPath.row].attachmentID)
}
and
func downloadAttachment(id:Int) {
let finalUrl = "\(urlAttachment)\(id)/data"
if let url = URL(string: finalUrl){
var request = URLRequest(url: url)
request.setValue(apiKey, forHTTPHeaderField: header)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print(error!)
return
}
print("Attachment Downloaded")
self.dataHolder = data
}
task.resume()
}
}
The obvious issue with this is that the image wouldn't show on the first attempt since it would still be retrieving the image and the dataHolder would still be nil, But if I tap on the cell twice then the image will be shown correctly.
Is there a simple way to maybe just tap once and make it shows a place holder until finished downloading and update the place holder with an actual image accordingly? Or any other proper way to handle this?

You can use closures to achieve what you asked for. The updated code looks like this.
func downloadAttachment(id:Int,completionHandler completion: #escaping ((Data)->Void)) {
let finalUrl = "\(urlAttachment)\(id)/data"
if let url = URL(string: finalUrl){
var request = URLRequest(url: url)
request.setValue(apiKey, forHTTPHeaderField: header)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print(error!)
return
}
print("Attachment Downloaded")
self.dataHolder = data
completion()
}
task.resume()
}
}
Now in collectionViewDidSelectItemAt make this changes
func collectionView (_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
dataManager.downloadAttachment(id: attachments[indexPath.row].attachmentID,completionHandler: { data in
if let cell = collectionView.cellForItem(at: indexPath),
let data = dataManager.dataHolder,
let image = UIImage.init(data: data){
attachmentImage.image = image
attachmentImage.isHidden = false
}
})
}

Related

Why URL not working correctly when tried to turn image?

I have question about firebase. I have image's URL in my database and I show them in collectionView cells but in this point I have some problem which is some image not loaded correctly their URLs are different but images are same. I tried lots of things but I can't solve it. My URLs starting with 'https' and App Transport Security Setting, Allow Arbitrary Loads = YES. So these are not solved my problem. Here my code which are firebase and adding this URL to imageViews. Please help me! Thanks!
func firebaseCon() {
let ref = Database.database().reference().child("cells")
ref.observe(.childAdded) { (snapshot) in
if let dict = snapshot.value as? [String: AnyObject] {
let dataCon = ItemCellImage()
dataCon.itemImageName = dict["itemimagename"] as? String
dataCon.itemTitleLabel = dict["itemimagelabel"] as? String
//print(dataCon.itemImageName, dataCon.itemTitleLabel)
self.itemler.append(dataCon)
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
}
And here from data to image:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! itemsCell
let dataGelen = itemler[indexPath.row]
cell.titleLabel.text = dataGelen.itemTitleLabel
if let cellDataImage = dataGelen.itemImageName {
let url = URL(string: cellDataImage)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
cell.itemsimageView.image = UIImage(data: data!)
self.imageDeneme = UIImage(data: data!)!
}
}.resume()
}
return cell
}
When I tried to change image url from firebase some of them working correctly and some of them show just previous image. Is there any way to show exactly correct image in every change?
For this kind of problem due to Reusing concept you can use prepareForReuse() function provide by cells.
override func prepareForReuse() {
yourImageView.image = nil //Or any placeholder image
}
This function is called when OS is about to reuse your cell, thus you can use it as a kind of reset to your cell appearance.

UIImage keeps loading all time when scroll even store in NSCache - swift

I am new in iOS programming. I am creating a simple app which loads image from a particular link ( firestore ). The images are completely downloaded from the server and visible on each cell of collectionview as usual. But the problem is that when when I scroll up or down then those images keeps loading again. I think it starts downloading again because when I turn off internet connection, those images are not being loaded anymore.
Here is how i set images in each cell
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CollectionCell
let explore = dataAppend[indexPath.item]
//cell.imageDisplay.text = explore.title
if let imageUrl = explore.image {
cell.imageDisplay.loadImageWithData(urlString: imageUrl)
}
//print(explore.image)
return cell
}
Here is how loading images look like loadImageWithData(urlString: imageUrl)
let imageCache = NSCache<NSString, UIImage>()
class CustomImageView : UIImageView {
var imageUrlString: String?
func loadImageWithData (urlString: String) {
imageUrlString = urlString
if let imageFromCache = imageCache.object(forKey: urlString as NSString){
self.image = imageFromCache
}
image = nil
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if let err = error {
print(err.localizedDescription)
}
if let data = data {
DispatchQueue.main.async {
let imageToCache = UIImage(data: data)
if self.imageUrlString == urlString {
self.image = imageToCache
}
imageCache.setObject(imageToCache!, forKey: urlString as NSString)
}
}
}).resume()
}
}
var imageCache = NSMutableDictionary()
class CustomImageView: UIImageView {
func loadImageUsingCacheWithUrlString(urlString: String) {
self.image = nil
if let img = imageCache.valueForKey(urlString) as? UIImage{
self.image = img
return
}
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(NSURL(string: urlString)!, completionHandler: { (data, response, error) -> Void in
if(error == nil){
if let img = UIImage(data: data!) {
imageCache.setValue(img, forKey: urlString) // Image saved for cache
DispatchQuee.main.asyn{
self.image = img
}
}
})
task.resume()
}
}
}
You can instead use the Kingfisher library , handles the image caching itself you don't need to worry about it. For implementing see :
https://github.com/onevcat/Kingfisher
with just one line of code you can set the image
imgView.kf.setImage(with: ImageResource(downloadURL: URL(string: imgUrl)!))

URLCache (CS193P Assignment 6)

I'm now on Stanford iOS Swift Assignment 6 where one of the required tasks is to use URLCache to cache the image on the local disk. After days of googling, I still couldn't figure out how to use it. It'd be helpful if anyone could point me to a good guide!
My code is like this now after trying to understand the official documentation. It doesn't help that the official doc doesn't have sample codes I could refer to :(
let urlCache = URLCache.shared
The required task is to set a cache and specify the size limit. I tried initialising URLCache and pass the size in the parameters. It works but storing and getting the cache doesn't seem to work. If URLCache is initialised every time the app (or view controller) is launched, wouldn't it ignore the previous cache that was created and stored?
I think there's no issue with this code? reading data from cache
if let cachedURLResponse = urlCache.cachedResponse(for: URLRequest(url: url)) {
if let fetchedImage = UIImage(data: cachedURLResponse.data) {
image = fetchedImage
}
}
I'm lost on writing data to cache
urlCache.storeCachedResponse(CachedURLResponse(response: URLResponse(), data: imageData), for: URLRequest(url: url))
How to initialise URLResponse properly? I looked at the init method and it also requires url to be passed in as parameter. Found this strange since url is in URLRequest() too. Am I doing it wrong?
Helpful advice much appreciated!
You can use URLCache by making a request for the image data with URLSession then using the data and response available in its completion handler, for example:
import UIKit
class GalleryCollectionViewController: UICollectionViewController, UICollectionViewDragDelegate, UICollectionViewDropDelegate, UICollectionViewDelegateFlowLayout {
// MARK: - Model
var gallery: Gallery?
// MARK: - Properties
private var cache = URLCache.shared
private var session = URLSession(configuration: .default)
override func viewDidLoad() {
super.viewDidLoad()
cache = URLCache(memoryCapacity: 100, diskCapacity: 100, diskPath: nil) // replace capacities with your own values
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GalleryCell", for: indexPath)
if let galleryCell = cell as? GalleryCollectionViewCell {
galleryCell.image = nil
galleryCell.imageView.isHidden = true
if let url = gallery?.images[indexPath.item].url {
let request = URLRequest(url: url.imageURL) // imageURL from Utilities.swift of Stanford iOS course
if let cachedResponse = cache.cachedResponse(for: request), let image = UIImage(data: cachedResponse.data) {
galleryCell.image = image
galleryCell.imageView.isHidden = false
} else {
DispatchQueue.global(qos: .userInitiated).async { [weak self, weak galleryCell] in
let task = self?.session.dataTask(with: request) { (urlData, urlResponse, urlError) in
DispatchQueue.main.async {
if urlError != nil { print("Data request failed with error \(urlError!)") }
if let data = urlData, let image = UIImage(data: data) {
if let response = urlResponse {
self?.cache.storeCachedResponse(CachedURLResponse(response: response, data: data), for: request)
}
galleryCell?.image = image
} else {
galleryCell?.image = UIImage(named: "placeholder")
}
galleryCell?.imageView.isHidden = false
}
}
task?.resume()
}
}
}
}
return cell
}
}

Display Image from Odata in Table View

enter image description hereI am trying to create an app based on shopping cart with swift 4, display product list from oData in a Table View, one of the element of odata contains URL of Image .
with below codes i am trying to generate the URL in picUrl variable
var picUrl = String()
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as! FUIContactCell
let product = self.entities[indexPath.row]
picUrl = ("https://\(self.hurl)\(product.pictureUrl! as String)" )
if let url = URL(string: picUrl){
downloadImage(url: url)
}
cell.detailImage = #imageLiteral(resourceName: "Image.png")
cell.detailImage = self.imageView.glyphImage
func getDataFromUrl(url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
completion(data, response, error)
}.resume()
}
func downloadImage(url: URL) {
//print("Download Started")
getDataFromUrl(url: url) { data, response, error in
// guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
DispatchQueue.main.async() {
self.imageView = (UIImage(data: data!))!
}
}
}
downloadImage(url: url) calls the image from url.
but somehow i am getting attached error enter image description here

Downloading Image from Api and populate Image in CollectionViewController using Swift

I am able to find the response data in array but not able to download and populate in Collection View. I have tried to upload the image from the image container from application but not able to download and upload by API
Code
func get_data_from_url(){
//API calls
let url = NSURL(string: "http://android.eposapi.co.uk/?app_id=A1A2A3A4&app_key=K1K2K3K4&request=gallery")
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
let postString = "gallery"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data,response,error in
if error != nil{
print("error=\(error)")
return
}
//Print out response object
print("response= \(response)")
//print response body
// let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
// print("response data = \(responseString!)")
var json: NSArray!
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as? NSArray
print(json)
print(json[0])
} catch {
print(error)
}
dispatch_async(dispatch_get_main_queue()) {
self.collectionView!.reloadData()
}
}
task.resume()
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url()
self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as UICollectionViewCell
// Configure the cell
let image = cell.viewWithTag(1) as! UIImageView
image.image = images[indexPath.row]
return cell
}
Tell me any one solution how to download images of anytype (either png or jpg) and populate in the UICollectionView using mutable urlRequest/
I have done exactly this recently, it is a Memory game that fetches images from instagram and displays the images in a UICollectionView. Please checkout the SwiftIntro project on Github
I fetch images using Alamofire:
func prefetchImages(urls: [URLRequestConvertible], done: Closure) {
imageDownloader.downloadImages(URLRequests: urls) {
response in
done()
}
}
This is a "prefetch" solution, then I can retrieve the images using this function:
func imageFromCache(url: NSURL) -> UIImage? {
guard let cache = imageCache else { return nil }
let imageFromCache = cache.imageForRequest(NSURLRequest(URL: url), withAdditionalIdentifier: nil)
return imageFromCache
}
Checkout the ImagePrefetcher class.
The MemoryDataSourceClass which implements the UICollectionViewDataSource and UICollectionViewDelegate protocols returns UICollectionViewCells of type CardCVCell which contains an UIImageView created in its .Xib. I set the image on the UIImageView in this method:
func updateWithModel(model: Model) {
guard let card = model as? CardModel else { return }
guard let cachedImage = ImagePrefetcher.sharedInstance.imageFromCache(card.imageUrl) else { return }
cardFrontImageView.image = cachedImage
flipped = card.flipped
}
Sorry about the ImagePrefetcher.sharedInstance Singleton ;), Singletons are bad (as discussed here and here)! I have not yet set up Dependency Injection using amazing Swinject, but will do so soon! :)
Try another way like NSURLSession to download the data
var url: NSURL = NSURL(string:"http://android.eposapi.co.uk/?app_id=A1A2A3A4&app_key=K1K2K3K4&request=gallery")!
var downloadPhotoTask: NSURLSessionDownloadTask =
NSURLSession.sharedSession().downloadTaskWithURL(url, completionHandler: {(location: NSURL, response: NSURLResponse, error: NSError) -> Void in
var downloadedImage: UIImage = UIImage.imageWithData(NSData.dataWithContentsOfURL(location))
})
downloadPhotoTask.resume()