Convert url to image [duplicate] - ios

This question already has answers here:
Loading/Downloading image from URL on Swift
(39 answers)
Closed 4 years ago.
still learning programming and have a question.
im trying to download image from url and put it in cells, ive successfully done it with text but not with images.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier:"searchCell", for: indexPath)
as! CustomTableViewCell
cell.titleField?.text = posts[indexPath.row].caption
cell.descriptionField?.text = posts[indexPath.row].description
cell.tagsField?.text = posts[indexPath.row].tags
let photoUrl = posts[indexPath.row].photoUrl
cell.SearchImage.image = ...
//url is stored in photoUrl
return cell
}
}

Use a pod to make it easy
use SDWebImage
then use it like this :
import SDWebImage
cell.SearchImage.sd_setImage(with: photoUrl, placeholderImage: nil)

I recommend you to use this third party library.
Kingfisher handles everything for you.
A lightweight, pure-Swift library for downloading and caching images from the web.
If you don't know how to install it, have a look at Cocoapods, it's pretty straightforward.
Anyway, here is the only line of code needed to set an image from your URL with Kingfisher :
cell.SearchImage.kf.setImage(with: photoUrl)
By the way, you should follow the Swift convention described here in order to make your code easier to read and "universal".
Have a look : Naming Convention

On swift3 You can use Alamofire (https://cocoapods.org/) for that.
Step 1:
Integrate using pods.
pod 'Alamofire', '~> 4.7'
pod 'AlamofireImage', '~> 3.3'
Step 2:
import AlamofireImage
import Alamofire
Step 3:
Alamofire.request("https:// YOUR URL").responseImage { response in
if let image = response.result.value {
print("image downloaded: \(image)")
self.myImageview.image = image
}
}
Or you can try this -
let url = URL(string: "https:// YOUR URL")
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async()
{
self.imageView.image = UIImage(data: data)
}
}
task.resume()

Related

PDFKIT , Swift How to remove pdf (PDFDocument) from uitableview

i have this code for download PDF file :
var documents = [PDFDocument]()
DispatchQueue.global(qos: .default).async(execute: {
//All stuff here
print("Download PDF");
let url=NSURL(string: urlString);
let urlData=NSData(contentsOf: url! as URL);
if((urlData) != nil)
{
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let fileName = urlString as NSString;
let filePath="\(documentsPath)/\(fileName.lastPathComponent)";
let fileExists = FileManager().fileExists(atPath: filePath)
if(fileExists){
// File is already downloaded
print("PDF Already Downloaded");
}
else{
//download
DispatchQueue.main.async(execute: { () -> Void in
print(filePath)
urlData?.write(toFile: filePath, atomically: true);
print("PDF Saved");
self.refreshData()
})
}
}
})
Now I want to remove this file from uitableview in table and in documentdirecotry how to use index path row and how to find file name for removing
i know i will remove the file here but i don't know how to exactly remove the PDF in documentDirectory and Table
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
// handle delete (by removing the data from your array and updating the tableview)
}
}
here is my table view cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BookshelfCell
let document = documents[indexPath.row]
if let documentAttributes = document.documentAttributes {
if let title = documentAttributes["Title"] as? String {
cell.title = title
}
if let author = documentAttributes["Author"] as? String {
cell.author = author
}
here is my refresh data part
let fileManager = FileManager.default
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let contents = try! fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
documents = contents.flatMap { PDFDocument(url: $0) }
You will need to complete three steps to properly delete the document and update your table view:
Use FileManager's removeItem(at: URL) or removeItem(atPath: String) to delete the file from disk. (note that both of these methods throw so you need to use a do-catch block along with try and only proceed if the method didn't throw an error.) Update: If you look at the documentation for PDFDocument you will find that in addition to the documentAttributes that you are already using there is another optional property, documentURL that should give you exactly what you need to remove it.
Remove the document from documents (you could just refresh the whole array using your existing code but removing a single item is faster). documents.remove(at: indexPath.row)
Finally, you need to tell the table view to remove the row in question (you could of course just reload the whole table view but removing the single cell is cleaner) tableView.deleteRows(at: [indexPath], with .fade)
In case you are unfamiliar with do-catch blocks here is a bit of code from Apple's book on Swift (see below for link) simplified a bit:
do {
try makeASandwich()
eatASandwich() // This only gets called if the line above worked
} catch {
dealWithTheError() // This only gets called if makeASandwich() throws an error
}
Side Note
Apple has a fantastic guide on the Swift Language if you haven't done so yet I suggest reading, at least, The Basics. This will give you a base understanding of the language. If you are also new to programming I would suggest going through Apple's Learn to Code series that is free on iPads in the Swift Playgrounds app. The series will guide you through all the basics of programming giving you the tools to search through the documentation that Apple provides and find answers to your questions.
We all started at the beginning at some point, and we all had to crawl before we could walk and well before we could run.

Download and cache images in UITableViewCell

Note: Please no libraries. This is important for me to learn. Also, there are a variety of answers on this but none that I found solves the issue nicely. Please don't mark as duplicate. Thanks in advance!
The problem I have is that if you scroll really fast in the table, you will see old images and flickering.
The solution from the questions I read is to cancel the URLSession
data request. But I do not know how to do that at the correct place
and time. There might be other solutions but not sure.
This is what I have so far:
Image cache class
class Cache {
static let shared = Cache()
private let cache = NSCache<NSString, UIImage>()
var task = URLSessionDataTask()
var session = URLSession.shared
func imageFor(url: URL, completionHandler: #escaping (image: Image? error: Error?) -> Void) {
if let imageInCache = self.cache.object(forKey: url.absoluteString as NSString) {
completionHandler(image: imageInCache, error: nil)
return
}
self.task = self.session.dataTask(with: url) { data, response, error in
if let error = error {
completionHandler(image: nil, error: Error)
return
}
let image = UIImage(data: data!)
self.cache.setObject(image, forKey: url.absoluteString as NSString)
completionHandler(image: image, error: nil)
}
self.task.resume()
}
}
Usage
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let myImage = images[indexPath.row]
if let imageURL = URL(string: myImage.urlString) {
photoImageView.setImage(from: imageURL)
}
return cell
}
Any thoughts?
Swift 3:
Flickering can be avoided by this way:
Use the following code in public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
cell.photoImageView.image = nil //or keep any placeholder here
cell.tag = indexPath.row
let task = URLSession.shared.dataTask(with: imageURL!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() {
if cell.tag == indexPath.row{
cell.photoImageView.image = UIImage(data: data)
}
}
}
task.resume()
By checking cell.tag == indexPath.row, we are assuring that the imageview whose image we are changing, is the same row for which the image is meant to be. Hope it helps!
A couple of issues:
One possible source of flickering is that while you're updating the image asynchronously, you really want to clear the image view first, so you don't see images for prior row of reused/dequeued table view cell. Make sure to set the image view's image to nil before initiating the asynchronous image retrieval. Or, perhaps combine that with "placeholder" logic that you'll see in lots of UIImageView sync image retrieval categories.
For example:
extension UIImageView {
func setImage(from url: URL, placeholder: UIImage? = nil) {
image = placeholder // use placeholder (or if `nil`, remove any old image, before initiating asynchronous retrieval
ImageCache.shared.image(for: url) { [weak self] result in
switch result {
case .success(let image):
self?.image = image
case .failure:
break
}
}
}
}
The other issue is that if you scroll very quickly, the reused image view may have an old image retrieval request still in progress. You really should, when you call your UIImageView category's async retrieval method, you should cancel and prior request associated with that cell.
The trick here is that if you're doing this in a UIImageView extension, you can't just create new stored property to keep track of the old request. So you'd often use "associated values" to keep track of prior requests.

UITableView is not smoothly when using downloading images

I am new in IOS development using Swift. I created 1 UITableView and displaying images after downloading data. But it is not smooth and some time images are displaying in wrong place when i am scrolling.
I am using AlamofireImage library for image downloading and displaying. Is there any fast library?
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:HomePageCell = tableView.dequeueReusableCell(withIdentifier: "HomePage", for: indexPath) as! HomePageCell
cell.configure( homeData[0], row: indexPath, screenSize: screenSize,
hometableview: self.homeTableView);
return cell
}
import UIKit
import Alamofire
import AlamofireImage
class HomePageCell: UITableViewCell {
#IBOutlet weak var bannerImage: UIImageView!
func configure(_ homeData: HomeRequest, row: IndexPath, screenSize: CGRect, hometableview: UITableView) {
let callData = homeData.banner_lead_stories[(row as NSIndexPath).row]
let url = Constants.TEMP_IMAGE_API_URL + callData.lead_story[0].bg_image_mobile;
if( !callData.lead_story[0].bg_image_mobile.isEmpty ) {
if bannerImage?.image == nil {
let range = url.range(of: "?", options: .backwards)?.lowerBound
let u = url.substring(to: range!)
Alamofire.request(u).responseImage { response in
debugPrint(response)
//print(response.request)
// print(response.response)
// debugPrint(response.result)
if let image = response.result.value {
// print("image downloaded: \(image)")
self.bannerImage.image = image;
self.bannerImage.frame = CGRect(x: 0, y: 0, width: Int(screenSize.width), height: Int(screenSize.width/1.4))
}
}
}
} else {
self.bannerImage.image = nil;
}
}
}
It can be not smooth, because you need to cache your images and make a downloading process not in main thread(read about GCD).
For caching you can go two ways (atleast):
1) Make your own array of images where they will be cached
2) Use KingFisher for example click. It will cache your images.
For example:
yourImageView.kf.setImage(with: URL) // next time, when you will use image with this URL, it will be taken from cache.
Hope it helps
You can use SDWebImage for downloading the image array and add a placeholder image for the time being in imageView. this is function
public func sd_setImageWithURL(url: NSURL!, placeholderImage placeholder: UIImage!)
and it is as easy to use as
myImageView.sd_setImageWithURL(NSURL(string:image), placeholderImage:UIImage(named:"qwerty"))
make sure to reset you imageView in tableView delegate cellforRowAtIndexpath method by setting imageview image to nil
myImageView.image = nil
//now set image in imageView
myImageView.sd_setImageWithURL(NSURL(string:image), placeholderImage:UIImage(named:"qwerty"))
this avoids the image duplicating and weird behave of images as imageview of every cell is being reset before reusing.
Github link -> https://github.com/rs/SDWebImage
You have to use multithreading.Only UI is set in main thread, downloading image in background is in another thread.By this way you can solve your problem.
Try SDWebImage library it will save images in catch automatically and your tableView will work smoothly.
Github link -> https://github.com/rs/SDWebImage
Install pod:
platform :ios, '7.0'
pod 'SDWebImage', '~>3.8'
Just import SDWebImage like:
#import SDWebImage
And use like this:
imageView.sd_setImage(with: URL(string: "http://www.example.com/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
I used it in many live projects and it works like a charm :)
Use this extension to cache your images, and also don't forget to update any UI on the main thread.
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
func loadImageUsingCacheWithURLString(_ URLString: String, placeHolder: UIImage?) {
self.image = nil
if let cachedImage = imageCache.object(forKey: NSString(string: URLString)) {
self.image = cachedImage
return
}
if let url = URL(string: URLString) {
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
//print("RESPONSE FROM API: \(response)")
if error != nil {
print("ERROR LOADING IMAGES FROM URL: \(error)")
DispatchQueue.main.async {
self.image = placeHolder
}
return
}
DispatchQueue.main.async {
if let data = data {
if let downloadedImage = UIImage(data: data) {
imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
self.image = downloadedImage
}
}
}
}).resume()
}
}
}

SDWebImage - image not showing

I want to get an image from a url. When using Apple's api's it works but when I use SDWebImage (version 3.8) I get an error.
Using Apple's api (this works):
if let url = NSURL(string: testURL) {
if let data = NSData(contentsOfURL: url) {
let testImage = UIImage(data: data)
cell.personAvatar.image = testImage
}
}
Using SDWebImage (does not work):
cell.personAvatar.sd_setImageWithURL(NSURL(string: testURL)!, placeholderImage: UIImage(named: "testImage")!) { (image, error, SDImageCacheType, url) in
cell.personAvatar.image = image
}
I am making these calls inside of:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell.
The error I get when using SDWebImage is:
Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=
Any ideas why this is not working?

load image from url in tableview cell from alamofire swift

I am trying to load image from json using Alomofire and swiftyJSON. Json is dictionary:
{"name": {"image": "https://...",}}
Alamorefire and SwiftyJSON
var imageLoad: String!
Alamofire.request(.GET, url).responseJSON { (response) -> Void in
if let value = response.result.value {
let json = JSON(value)
if let jsonDict = json.dictionary {
let image = jsonDict["name"]!["image"].stringValue
print(image) // https://....
self.imageLoad = image
}
self.tableView.reloadData()
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TVCell
// Configure the cell...
cell.imageTableView.image = UIImage(named: imageLoad) // not working "fatal error: unexpectedly found nil while unwrapping an Optional value"
If anyone can help? If there is another way feel free to write.
I share a code implemented using Swift 3 and AlamofireImage:
import UIKit
import AlamofireImage
class MovieViewCell: UICollectionViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var overviewLabel: UILabel!
#IBOutlet weak var posterView: UIImageView!
func configure(for movie: Movie) {
titleLabel.text = movie.title
overviewLabel.text = movie.overview
let url = URL(string: movie.poster_path!)!
posterView.af_setImage(withURL: url)
}
override func prepareForReuse() {
super.prepareForReuse()
posterView.af_cancelImageRequest()
posterView.image = nil
}
}
Also, you will need Cocoapods:
platform :ios, '10.0'
inhibit_all_warnings!
use_frameworks!
target 'MovieApp' do
pod 'Alamofire', '~> 4.5'
pod 'AlamofireImage', '~> 3.3'
end
Official documentation AlamofireImage and an ImageCell.swift example
If you are using Alamofire, try AlamofireImage. But use the af_ shortcuts directly on uiimageview. Automatically you will get caching, placeholder images, and you can cancel outstanding requests if you are using a table view which could recycle the cells.
By specifying a placeholder image, the image view uses the placeholder image until the remote image is downloaded.
let imageView = UIImageView(frame: frame)
let url = URL(string: "https://httpbin.org/image/png")!
let placeholderImage = UIImage(named: "placeholder")!
imageView.af_setImage(withURL: url, placeholderImage: placeholderImage)
https://github.com/Alamofire/AlamofireImage
load image from url in tableview cell from alamofire swift3 : -
//you need install pod 'AlamofireImage', '~> 3.0’ pod
import Alamofire
import AlamofireImage
// put into cellForRowAt
Alamofire.request(self.profileImgArr[indexPath.row]).responseData { (response) in
if response.error == nil {
print(response.result)
// Show the downloaded image:
if let data = response.data {
cell.profileImg.image = UIImage(data: data)
}
}
}
I recommend using a different library for loading the images. We always do this in our projects.
There is an easy and smart library which handles pretty much everything from caching to placeholder image.
It's name is Kingfisher
https://github.com/onevcat/Kingfisher
You can use it directly on the ImageView with the URL from JSON
Example:
let url = URL(string: "url_of_your_image")
imageView.kf.setImage(with: url)
This is the most basic Asynchronous image loading method,
Take a look for yourself :)
If you're already using Alamofire, you can try AlamofireImage which is an image component library for Alamofire.
Then, fetching image is done like this:
import AlamofireImage
Alamofire.request(.GET, "https://httpbin.org/image/png")
.responseImage { response in
if let image = response.result.value {
print("image downloaded: \(image)")
}
}
As Bear with me said you can use AlamofireImage,
https://github.com/Alamofire/AlamofireImage
I made this for Swift 3, hope it can help:
In the controller implementing the tableViewDataSource
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Cell
NetworkService.shared.requestImage(path: path, completionHandler: {image in
cell.photo?.image = image
})
}
return cell
}
In my NetworkService (I use the shared to implement singleton, it's like getInstance) I implemented the requestImage function calling AlamofireImage:
func requestImage(path: String, completionHandler: #escaping (Image) -> Void){
Alamofire.request("\(path)").responseImage(imageScale: 1.5, inflateResponseImage: false, completionHandler: {response in
guard let image = response.result.value else{
print(response.result)
return
}
DispatchQueue.main.async {
completionHandler(image)
}
})
}
As you can see I use the GCD for main queue to manage the completionHandler just to be sure that it is the main queue which handles the view modification.

Resources