Downloading Image from Api and populate Image in CollectionViewController using Swift - 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()

Related

Showing UIImageView after finished loading

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
}
})
}

loading images from firebase to uitableviewcell

I am trying to create a UITableView that will load posts and pictures from firebase. The posts load fine, but the images, only the most bottom cell shows the image while the other cells load the image only after I perform tableView.reloadData(). I would like to have all the images loaded properly into each cell when the user opens UITableView.
I am also using a cache method so the user only has to download the images once. Please let me know if you have any ideas!
Here is the cache method I have been trying:
let imageCache = NSCache<AnyObject, AnyObject>()
var imageUrlString: String?
extension UIImageView {
func loadImageUsingCacheWithUrlString(urlString: String) {
imageUrlString = urlString
let url = NSURL(string: urlString)
image = UIImage(named: "addpic")
if let imageFromCache = imageCache.object(forKey: urlString as
AnyObject) as? UIImage {
self.image = imageFromCache
return
}
URLSession.shared.dataTask(with: url! as URL, completionHandler: {
(data, respones, error) in
if error != nil {
print(error)
return
}
DispatchQueue.main.async(execute: {
let imageToCache = UIImage(data: data!)
if imageUrlString == urlString {
self.image = imageToCache
}
imageCache.setObject(imageToCache!, forKey: urlString as
AnyObject)
tableView.reloadData()
})
}).resume()
}
}
In my UITableViewCell swift file I hook up the UIImage:
#IBOutlet weak var posterProfilePic: UIImageView!
In order to load the image from firebase/cache I call the cache method:
func getShoutoutPic() {
if let profileImageUrl = shoutout?.picsImageUrl {
self.posterProfilePic.loadImageUsingCacheWithUrlString(urlString:
profileImageUrl) }
}
Any advice will be greatly appreciated! thanks

download images from url to image array and show in table view swift [duplicate]

This question already has answers here:
Loading/Downloading image from URL on Swift
(39 answers)
Closed 3 years ago.
I am facing trouble getting image from my json url.
this is my json:
"bank_details": [
{
"id": 1,
"logo": "http://mortgagemarket.ae/webApi/public/mortgage_bank_icons/noorebank.png",
"name": abc company
}
]
my swift code to parse the image is this:
import UIKit
class BanksViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
final let BANKS_URL = "http://www.mortgagemarket.ae/webApi/api/manage_interest_rates"
#IBOutlet weak var tableView: UITableView!
var bankicon = [String]()
var bankname = [String]()
var bankid = [Int]()
let stringid: String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.displayFromDb()
tableView.dataSource = self
tableView.delegate = self
}
func displayFromDb()
{
let tokensp = UserDefaults.standard.string(forKey: "tokenKey")
let url = NSURL(string: BANKS_URL+"?token="+tokensp!)
print(url)
URLSession.shared.dataTask(with: (url as?URL)!, completionHandler: {(data,response,error) ->
Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
{
print(jsonObj.value(forKey: "bank_details")!)
if let messageArray = jsonObj.value(forKey: "bank_details") as? NSArray
{
print(jsonObj.value(forKey: "bank_details")!)
for message in messageArray
{
if let messageDict = message as? NSDictionary
{
if let data = data {
if let bankname = messageDict.value(forKey: "bank_name")
{
self.bankname.append(bankname as! String)
print(bankname)
}
if let banklogo = messageDict.value(forKey: "logo")
{
self.bankicon.append(banklogo as! String)
print(banklogo)
}
if let bankid = messageDict.value(forKey: "id")
{
self.bankid.append(bankid as! Int)
print(bankid)
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}
}
}
}
}).resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (bankname.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BanksTableViewCell
cell.bankicon.image = bankicon[indexPath.row] as? UIImage
cell.bankname.text = bankname[indexPath.row]
return (cell)
}
}
now When I run this code it is showing blank table cells. I dont know how to get image from url and display the images in table view cell. Please someone help me.
this is my whole code to get the all the json data into table view cell. Please someone help me
imageicon[indexPath.row] gives a urlStringand not the instance ofUIImage. You need to fetch the image from server using this urlString.
Use URLSession to fetch the image from server like,
if let url = URL(string: imageicon[indexPath.row]) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data {
DispatchQueue.main.async {
cell.imageicon.image = UIImage(data: data)
}
}
}.resume()
}
Your models should be like this:
/// Your response models
struct BankDetails: Codable {
let bank_details: [ImageUrl]
}
struct ImageUrl: Codable {
let logo: String
}
And then in your cell:
class MyCell: UITableViewCell {
/// create dataTask for cancel in prepareForReuse function
private var dataTask: URLSessionDataTask?
/// Like this
override public func prepareForReuse() {
super.prepareForReuse()
dataTask?.cancel()
}
func populate(with model: YourModel) {
/// You should set url in indexPath of your logo array([ImageUrl])
let url = model.url /// It's sample url for explain this is an url of your current index model
if let imageUrl = url {
downloaded(from: imageUrl)
}
}
func downloaded(from url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
contentMode = mode
dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() {
self.yourImageView.image = image
}
}
dataTask?.resume()
}
}
In your Controller's tableView cellForRowAt function:
let model = models[indexPath.row]
cell.populate(with: model)
return cell
You can use the above models, and create displayFromDb like this:
func displayFromDb() {
let tokensp = UserDefaults.standard.string(forKey: "tokenKey")
let url = NSURL(string: BANKS_URL+"?token="+tokensp!)
if let myUrl = url {
URLSession.shared.dataTask(with: myUrl) { (data, response , error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let data = try decoder.decode(BankDetails.self, from: data)
print("my logo array is: \(data.bank_details)")
// TODO: - So you get urls
} catch let err {
print("Err", err)
}
}.resume()
}
}

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
}
}