I need to print image in my next view controller after comparing ID of a table containing user details after comparing the ID I am successfully getting the name but the respective image is unable to fetch
if the user has posted anything then I am getting name for particular posted job now what I want is image of from respective user (that image which user uploaded while registration), (which identifies the posted job is posted via which user).
Below is my code:
func getJOBData()
{
let jobUrl = URL(string: "http://172.16.1.22/Get-Job-API/get-jobs/")
URLSession.shared.dataTask(with: jobUrl!) { (data, response, error) in
do
{
if error == nil
{
self.jobArray = try JSONDecoder().decode([JobData].self, from: data!)
for mainJobArr in self.jobArray
{
DispatchQueue.main.async {
self.jobPostTblView.reloadData()
}
}
print("Job Data****\(self.jobArray)")
}
}
catch
{
// print("my data=\(self.mainCellData)")
print("Error in get JSON Data\(error)")
}
}.resume()
}
numberOfRowsInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jobFilteredArray.count
}
cellForRowAtIndexPath Method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("JobTableViewCell", owner: self, options: nil)?.first as! JobTableViewCell
let data = jobArray[indexPath.row]
cell.jobTitle.text = data.job_desig
cell.expDetails.text = data.job_exp_to
cell.locationDetails.text = data.job_location
cell.dateDetails.text = data.job_skills
cell.companyName.text = companyArray.first { $0.company_id == data.company_id }?.company_name
return cell
}
didSelectRowAtIndexPath
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let selectedCell:UITableViewCell = tableView.cellForRow(at: indexPath)!
selectedCell.contentView.backgroundColor = UIColor.white
let rows = indexPath.row
print("Rows=\(rows)")
let jobDetail = WorkerJobDetailsViewController(nibName: "WorkerJobDetailsViewController", bundle: nil)
let jdata = jobFilteredArray[indexPath.row]
jobDetail.gender = jobArray[indexPath.row].job_emp_gender
jobDetail.location = jobArray[indexPath.row].job_location
jobDetail.companyName = (companyArray.first { $0.company_id == jdata.company_id }?.company_name)!
jobDetail.profile = jobImgPath
jobImgPath = (companyArray.first { $0.company_id == jdata.company_id }?.company_logo)!
jobDetail.skills = jobArray[indexPath.row].job_skills
jobDetail.descriptionValue = jobArray[indexPath.row].job_desc
jobDetail.jobDesignation = jobArray[indexPath.row].job_desig
self.present(jobDetail, animated: true, completion: nil)
}
Can anyone please help me to fetch images for respective user of posted job??
just use a pod like SDWebImage and fetch the link with the url that you are mapping the problem that you will face is that the link is a local ip and it will not work from a remote network but if the link changes at the json in the future you will be fine
Without pod you can do this
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.image = UIImage(data: data)
}
}
}
Related
I am looking for a way to smoothen my tableview. Since I have cells that need to download images from the URL, loading tableview cells one by one is very slow and makes the tableview lag. Moreover, the tableview will reload every time when I go back to previous cells. So I hope I could load the cells in batches of 15 with animation while loading. Any ideas of how I can do this? Any help is appreciated.
PS: Is my download function appropriate? Is there any other faster or better download function?
My code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row] // "post" are the firebase documents
if post.posttype == 1{
let cell = tableView.dequeueReusableCell(withIdentifier: TextPostCell.identifier, for: indexPath) as! TextPostCell
cell.titleText.text = post.title
cell.contentLable.text = post.content
cell.userPhoto.downloadImage(from: post.userphoto, placeHolder: UIImage(named: "notfound")!)
cell.username.text = post.sender
return cell
}else{
let cell = tableView.dequeueReusableCell(withIdentifier: ImageTextPostCell.identifier, for: indexPath) as! ImageTextPostCell
cell.titleText.text = post.title
cell.photoImage.downloadImage(from: post.photo![0], placeHolder: UIImage(named: "notfound")!)
print("success")
cell.userphoto.downloadImage(from: post.userphoto, placeHolder: UIImage(named: "notfound")!)
cell.username.text = post.sender
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// tableview selected
}
extension UIImageView{
func downloadImage(from url: String?, placeHolder placeholder: UIImage) {
print("Download Started")
if let safeString = url{
if let safeURL = URL(string: safeString){
getData(from: safeURL) { data, response, error in
if error != nil{
print("Tony's Notes: Problem retrieving data")
print(error!)
self.image = placeholder
}
// always update the UI from the main thread
DispatchQueue.main.async() { [weak self] in
if let safeData = data{
self?.image = UIImage(data: safeData)
return
}
}
}
}else{
self.image = placeholder
}
}else{
self.image = placeholder
}
}
func getData(from url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}
}
You may be looking for preloading: prefetch rows, and precancel rows.
https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching
I'm new into coding, learning how to parse JSON image into table view
able to display the labels but not able to display the image file. How to display it? I used the code given below please check it.
import UIKit
class ViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
var dataArray = [[String:AnyObject]]()
#IBOutlet weak var myTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://jsonplaceholder.typicode.com/photos")! //change the url
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "GET" //set http method as POST
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [[String:Any]] {
self.dataArray = json as [[String : AnyObject]]
DispatchQueue.main.async {
self.myTable.reloadData()
}
print(json)
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "id") as! ModTableViewCell
cell.labout.text = String(describing: dataArray[indexPath.row]["id"]!)
cell.imagethum.image = UIImage(named :dataArray[indexPath.row]["thumbnailUrl"]! as! String)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "story") as? FinalViewController
var selectedindex = indexPath.row
vc?.jarray = dataArray
vc?.selectedindex1 = selectedindex
self.navigationController?.pushViewController(vc!, animated: true)
}
}
You need to download your image at first.
The basic solution is:
if let url = URL(string: "YOUR_URL") {
if let data = try? Data(contentsOf: url) {
cell.imagethum.image = UIImage(data: data)
}
}
For more advanced solution take a look on SDWebImage framework ( for example ) - it's beginner-friendly.
You need to download the image using thumbnailUrl String that you're getting from JSON response.
Replace the implementation of cellForRowAt method with the following code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "id") as! ModTableViewCell
cell.labout.text = String(describing: dataArray[indexPath.row]["id"] ?? "")
if let urlString = dataArray[indexPath.row]["thumbnailUrl"] as? String, let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) { (data, urlResponse, error) in
if let data = data {
cell.imagethum.image = UIImage(data: data)
}
}.resume()
}
return cell
}
Also, don't use forced unwrapping (!) so extensively. It might result in crashing the app unnecessarily.
You can try this
We use this SDWebImage pod to load images from URL. This pod provides an async image downloader with cache support.
Example Of SDWebImage as below
let img1 = savedInspofflineData[indexPath.row].image1
if img1 == ""{
//Error
}else{
let imageUrl = URL(string: img1!)!
cell.img1.sd_setImage(with: imageUrl, placeholderImage: UIImage(named: "logo_grey"), options: .refreshCached, completed: nil)
}
I am trying to get an API call to populate the cells of a list view but cannot seem to get the data to be gathered before the cells are generated. I have tried making a completion handler as like here to no avail as well as a semaphore from the same link. While the completion handler compiled, it still did not get the data in time when executing (admittedly it was my first attempt at a completion handler). Here is the original code w/o the completion handler:
override func viewDidLoad() {
super.viewDidLoad()
RestaurantListView.delegate = self
RestaurantListView.dataSource = self
//API setup
guard let url = URL(string: "https://myURL.com") else {
print("ERROR: Invalid URL")
return
}
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) -> Void in
// URL request is complete
guard let data = data else {
print("ERROR: Unable to access content")
return
}
guard let blog = try? JSONDecoder().decode(Blog.self, from: data) else {
print("Error: Couldn't decode data into Blog")
return
}
self.myData = blog
}
task.resume()
}
The table and cells are initialized below these functions:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = RestaurantListView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = myAPIValue
return cell!
}
I have several print statements to track the program and it goes through viewDidLoad then the first tableView which sets up the table then the second which handles the cells, finally it goes through the URL task.
I have rewritten this dozens of different ways and have no idea how to make this execute in the correct order.
You need to reload the table , as this line URLSession.shared.dataTask(with: url) triggers an asynchronous task that isn't in the same sequential order of your lines of code
guard let blog = try? JSONDecoder().decode(Blog.self, from: data) else {
print("Error: Couldn't decode data into Blog")
return
}
self.myData = blog
DispatchQueue.main.async{
RestaurantListView.reloadData()
}
You have to save your data in a variable as you can see as follow, I used arrayOfData. Look the reloadData() function which is called after saving the data.
func downloadData (){
let url = URL(string: "http://www.url.com")
if let url = url {
let task = URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in
do {
if let dataResponse = data{
let responseDecoded = try JSONDecoder().decode(Response.self, from: dataResponse)
DispatchQueue.main.async {
self.arrayOfData = responseDecoded.results ?? []
self.RestaurantListView.reloadData()
}
}
}
catch {
print("Error: \(error)")
}
})
task.resume()
}
}
You have to conform the protocol with that data
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
/**If you have no elements you should return 0**/
return arrayOfData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = RestaurantListView.dequeueReusableCell(withIdentifier: "cell"){
cell.textLabel?.text = arrayOfData[indexPath.row].name
return cell
}
return UITableViewCell()
}
I have a table view that loads images from s3 bucket and set some data with the images in my cell.
I call my cell at
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellDish:DishTableViewCell = tableView.dequeueReusableCell(withIdentifier: "DishCell", for: indexPath) as! DishTableViewCell
cellDish.setDish(dish: brain.listOfDishes[indexPath.row])
return cellDish }
in my tableviewcell I have a func called setDish :
func setDish (dish: Dish)
{
var StringPrice = dish.DishPrice
StringPrice.append("$")
self.la_name.text = dish.DishName
self.la_price.text = StringPrice
self.la_des.text = dish.DishDes
self.downloadData(dish: dish, completion: { success in
guard success else { return }
DispatchQueue.main.async {
self.dish_image.image = UIImage(data: dish.DishData!)!
}
})
}
func downloadData(dish:Dish,completion: #escaping (Bool) -> Void) {
let transferUtility = AWSS3TransferUtility.default()
let expression = AWSS3TransferUtilityDownloadExpression()
let s3Bucket = "<my bucket name>"
transferUtility.downloadData(fromBucket: s3Bucket, key: dish.DishImage, expression: expression) {(task, url, data, error) in
if error != nil {print(error)
completion(false)
}
else {
dish.DishData = data!
}
completion(true)
}
}
I want it to show me the dish data without the image until the image is downloaded and then show it to me as well (I want it to be not on the main thread of course ).
I'm not sure why but right now all the cells download their images and only then everything loads up together.
Swift 3 Updated Code :
Load url asynchronous will update automatically
extension UIImageView
{
public func imageFromServerURL(urlString: String)
{
URLSession.shared.dataTask(with: NSURL(string: urlString)! as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error)
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
self.image = image
})
}).resume()
}}
Swift 2.2 Code :
extension UIImageView {
public func imageFromServerURL(urlString: String) {
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: urlString)!, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error)
return
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let image = UIImage(data: data!)
self.image = image
})
}).resume()
}}
I'm populating my tableView with JSON data, most of the time the data shows but for some strange reason other times it doesn't. I tested the JSON data in Chrome and the info is there. I also made print statements to print the info after it has downloaded and it appears to download correctly. I can't figure out why 80% of the time the data populates the tableView correctly and 20% of the time it doesn't. Here is a sample of my code, there are many more cells but I shortened it to 2 for this example:
var task : NSURLSessionTask?
var newURL : String?
var bannerArray: [String] = []
var overViewArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
getJSON(newURL!)
}
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.updateDetailShowInfo(data)
}
else {
"Not getting JSON"
}
}
}
task!.resume()
}
func updateDetailShowInfo (data: NSData!) {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
else { return }
_ = ""
print(overview)
bannerArray.append(banner)
overViewArray.append(overview)
}
catch {
print("It ain't working")
}
self.DetailTvTableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: return bannerArray.count
case 1: return overViewArray.count
default: fatalError("Unknown Selection")
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier("bannerCell", forIndexPath: indexPath) as! BannerCell
cell.bannerImage.sd_setImageWithURL(NSURL(string: bannerArray[indexPath.row]))
self.DetailTvTableView.rowHeight = 100
DetailTvTableView.allowsSelection = false
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("overviewCell", forIndexPath: indexPath) as! OverviewCell
let overViewText = overViewArray[indexPath.row]
if overViewText != "" {
cell.overView.text = overViewText
} else {
cell.overView.text = "N/A"
}
self.DetailTvTableView.rowHeight = 200
print(overViewArray[indexPath.row])
return cell
default: ""
}
return cell
}
I'm just doing this off the web. And I think there are some errors. You need to debug them yourself.
Your understanding of fetching the JSON and GCD is totally wrong. I believe these codes you got somewhere off the web. Go read up what is dispatch_async.
Basically, you need to create session to fetch JSON data, which you have done it correctly, however, within the NSJSONSerialization, you need to store them in a variable and append it to your array. This is fetched asynchronously. Your dispatch_async will reload data serially.
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
bannerArray.append(banner)
overViewArray.append(overview)
} dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.DetailTvTableView.reloadData()
}
else {
"Not getting JSON"
}
}
catch {
print("It ain't working")
}
}
}
task!.resume()
}