UITableView not showing images from URLs [duplicate] - json

This question already has answers here:
Display images in UITableview
(3 answers)
Closed 5 years ago.
I have this code that is supposed to show images from URLS. I think everything is good because when I print the count of images and images I get an array of 7 images.
Please look at my code and correct me where I did a mistake.
import UIKit
import SwiftyJSON
import Haneke
class SlideViewController: UIViewController , UITableViewDelegate , UITableViewDataSource {
#IBOutlet weak var tableview : UITableView!
var images = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableview.delegate = self
tableview.dataSource = self
getJSON()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return images.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath) as! ImageTableViewCell
let remote = images[indexPath.row]
let imageurl = URL(string: remote)
cell.images.sd_setImage(with: imageurl)
return cell
}
func getJSON() {
let url = "http://localhost:8000/api/hello"
let myuel = URL(string: url)
let resquest = NSMutableURLRequest(url: myuel!)
resquest.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: resquest as URLRequest, completionHandler: { data,response,error in
if error != nil{
print(error!.localizedDescription)
return
}
let json = JSON(data)
self.images = json["pic"].arrayObject! as! [String]
print(self.images.count)
print(self.images)
DispatchQueue.main.async {
self.tableview.reloadData()
}
})
task.resume()
}
}

make sure to return a section from the following method and
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
use this extension to load and view images from url asynchronously
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()
}}
use above extension to load image to the imageView like this.
cell.imageview.imageFromServerURL(urlString: urlString)

Related

Type 'HeroStruct.Type' cannot conform to 'Decodable', how can i solve that?

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var hero = [HeroStruct]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
navigationController?.navigationBar.barTintColor = UIColor.systemYellow
navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.systemYellow ]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCell.CellStyle.subtitle, reuseIdentifier: nil)
cell.textLabel?.text = ""
cell.backgroundColor = .systemYellow
return cell
}
func getJsonData(completion: #escaping () -> () ) {
let url = URL(string: "https://api.opendota.com/api/heroStats")
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
if error != nil {
print(error?.localizedDescription)
}else {
do {
let result = try JSONDecoder().decode(HeroStruct.Type, from: data!)
}catch {
print(error)
}
}
}
}
}
import Foundation
struct HeroStruct : Decodable {
let localized_name : String
let primary_attr : String
let attack_type : String
let legs : Int
let img : String
}
First code block is my ViewController.swift page,
second code block is my HeroStruct.swift page,
I tried to get data from Json but i got error like this:
Type 'HeroStruct.Type' cannot conform to 'Decodable'
How can i solve this?
let result = try JSONDecoder().decode([HeroStruct].Type, from: data!)`
I tried write like this but doesn't work. Need help ,thanks.
Replace [HeroStruct].Type with [HeroStruct].self. Whenever you want to decode something, always use .self & not .Type.

UITableView returning nil when trying to call reloadData()

I am trying to get my table view to show json data from the news api. I've been able to parse the data and display it to the console but a nil value was caught in the self.tableview.reload(). I need help in resolving the issue
let urlRequest = "https://newsapi.org/v2/everythingq=Coronavirus&sortBy=publishedAt&apiKey"
var articles: [Articles]? = []
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
retriveData()
}
func retriveData(){
guard let aritcleUrl = URL(string: urlRequest) else {
return
}
let request = URLRequest(url: aritcleUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error ?? 0)
return
}
if let data = data {
self.articles = self.parseData(data: data)
// Reload table view
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
})
task.resume()
}
func parseData(data:Data)-> [Articles] {
var articles: [Articles]? = []
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
let jsonArticles = jsonResult?["articles"] as? [AnyObject] ?? []
for jsonArticle in jsonArticles{
let article = Articles()
article.author = jsonArticle["author"] as? String
article.title = jsonArticle["title"] as? String
article.publishedAt = jsonArticle["publishedAt"] as? String
articles?.append(article)
}
print(jsonArticles)
} catch {
print(error)
}
return articles ?? []
}
It seems that your class doesn't conform to table view's protocols.
You should edit your declaration to:
class <YourViewController>: UIViewController, UITableViewDelegate, UITableViewDataSource {
then in the viewDidLoad you should set the delegate and datasource to your tableview
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
retriveData()
}
Then you have to add the protocol stubs as suggested by xcode
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.articles?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// YOUR CELL CUSTOMIZATION GOES HERE
}
However I suggest you to look at any TableView tutorial on Internet, such as the Apple's official one
here

Display callback array in UITableView, currently showing as empty

I have a function that connects to an endpoint and returns a JSON result which I save into an array and using a callback function. Everything seems to be configured correctly. But no data is showing in the tables when the simulator runs. I'm not sure why? I was under the impression I would just need to run tableView.reloadData() but this doesn't do anything
class ViewConversionsTableViewController: UITableViewController {
/* codesToConvert: The Country code and Country sent from the previous view used to connect to the endpoint
* arraysToDisplay: The total list of countries and their corresponding values to be displayed
*/
var codesToConvert = [String]()
var arraysToDisplay = [String]()
override func viewDidLoad() {
super.viewDidLoad()
calculateRate(value: codesToConvert) {(structuredArray) in
self.arraysToDisplay.append(contentsOf: structuredArray)
}
tableView.reloadData()
}
func calculateRate(value: [String], completionHandler: #escaping (_ structuredArray: [String])->()){
var structuredArray = [String]()
let url = URL(string: "domain.com")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let postString = "pairs=\(value[0] + value[2])&pairs=\(value[2]+value[0])"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request) {(data, response, error) in
do {
var arrayToSend = [String]()
let jsonResult = try JSONSerialization.jsonObject(with: data!)
let results = jsonResult as! [String: Any]
for values in results{
arrayToSend.append(values.key)
arrayToSend.append("\(values.value)")
}
arrayToSend.append(value[1])
arrayToSend.append(value[3])
// Structure the array to the required format
structuredArray.append(arrayToSend[1] + " " + String(arrayToSend[0].prefix(3)))
structuredArray.append(arrayToSend[4])
structuredArray.append(arrayToSend[3])
structuredArray.append(arrayToSend[5] + " ยท " + String(arrayToSend[2].prefix(3)))
completionHandler(structuredArray)
} catch {
print(error)
}
}
task.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arraysToDisplay.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ConversionsCell
cell.Amount1.text = arraysToDisplay[0]
cell.currencyName1.text = arraysToDisplay[1]
cell.Amount2.text = arraysToDisplay[2]
cell.currencyName2.text = arraysToDisplay[3]
return cell
}
}
You should've reloaded the tableview inside the completion block.
override func viewDidLoad() {
super.viewDidLoad()
calculateRate(value: codesToConvert) {(structuredArray) in
self.arraysToDisplay.append(contentsOf: structuredArray)
DispatchQueue.main.async {
tableView.reloadData()
}
}
}
This is occurred because your tableView load the data when the data network call process haven't completed yet.
put your tableView.reloadData inside the closure and it must be on main thread. So it should be like this:
override func viewDidLoad() {
super.viewDidLoad()
calculateRate(value: codesToConvert) {(structuredArray) in
self.arraysToDisplay.append(contentsOf: structuredArray)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}

Swift 4 saving data from json to an array to show it in TableView

I'm trying to save the data from func getCoinData to an array sympolsCoin and array sympolsCoin to use it in my TableView
I create this class in the same ViewController.swift file :
struct Coin: Decodable {
let symbol : String
let price_usd : String }
And this in my View controller class :
var coins = [Coin]()
var sympolsCoin = [String]()
var priceUSDcoin = [String]()
func getCoinData(completion: #escaping () -> ()) {
let jsonURL = "https://api.coinmarketcap.com/v1/ticker/"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data!)
for info in self.coins {
self.sympolsCoin.append(info.symbol)
self.priceUSDcoin.append(info.price_usd)
print("\(self.sympolsCoin) : \(self.priceUSDcoin)")
completion()
}
}
catch {
print("Error is : \n\(error)")
}
}.resume()
}
And when i use the array in my TableView i got blank table !
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BitcoinTableViewCell", for: indexPath) as! BitcoinTableViewCell
cell.coinNameLable.text = sympolsCoin[indexPath.row]
cell.priceLable.text = priceUSDcoin[indexPath.row]
return cell
}
Since you are using JSONDecoder the entire logic to create and populate sympolsCoin and priceUSDcoin is pointless and redundant.
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case symbol, priceUSD = "price_usd"
}
let symbol : String
let priceUSD : String
}
var coins = [Coin]()
The completion handler is redundant, too. Just reload the table view on the main thread after receiving the data:
func getCoinData() {
let jsonURL = "https://api.coinmarketcap.com/v1/ticker/"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
In viewDidLoad load the data
override func viewDidLoad() {
super.viewDidLoad()
getCoinData()
}
In cellForRow update the UI
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BitcoinTableViewCell", for: indexPath) as! BitcoinTableViewCell
let coin = coins[indexPath.row]
cell.coinNameLable.text = coin.symbol
cell.priceLable.text = coin.priceUSD
return cell
}
Create an Outlet of tableView in ViewController Class and give it name "tableView" then
Try this code: Swift 4
func getCoinData() {
let jsonURL = "https://api.coinmarketcap.com/v1/ticker/"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data!)
for info in self.coins {
self.sympolsCoin.append(info.symbol)
self.priceUSDcoin.append(info.price_usd)
print("\(self.sympolsCoin) : \(self.priceUSDcoin)")
self.tableView.reloadData()
}
}
catch {
print("Error is : \n\(error)")
}
}.resume()
}
Call this function in ViewDidLoad like this
override func viewDidLoad() {
super.viewDidLoad()
getCoinData()
}
You need to update the tableView from the main thread. As a good lesson to learn: Always update the UI from the Main Thread. Always.
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data!)
for info in self.coins {
self.sympolsCoin.append(info.symbol)
self.priceUSDcoin.append(info.price_usd)
DispatchQueue.main.async {
self.tableView.reloadData()
}
print("\(self.sympolsCoin) : \(self.priceUSDcoin)")
completion()
}
}
There is, however another problem with your code the way you have your labels setup won't work. TableViewCells get reused so I'm guessing you have #IBOutlets for them somewhere else. What you should do is declare a label constant in cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
let coinNameLabel = cell.viewWithTag(100) as! UILabel
coinNameLabel.text = sympolsCoin[indexPath.row]
let priceNameLabel = cell.viewWithTag(101) as! UILabel
priceNameLabel.text = priceUSDcoin[indexPath.row]
}
The above code assumes you've setup two labels with the tags 100 and 101 in your storyboard(assuming your using one)
**
// First View Controller
//
//
//
import UIKit
struct Countory : Decodable {
let name: String
let capital: String
let region: String
let alpha2Code: String
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var listArr = [Countory]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
let url = "https://restcountries.eu/rest/v2/all"
let urlObj = URL(string: url)!
URLSession.shared.dataTask(with: urlObj) {(data, responds, Error) in
do {
self.listArr = try JSONDecoder().decode([Countory].self, from: data!)
for country in self.listArr {
print("Country",country.name)
print("###################")
print("Capital",country.capital)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(" not ")
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.listArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
cell.label1.text = "Name: \(listArr[indexPath.row].name)"
cell.lable2.text = listArr[indexPath.row].capital
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let homeView = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
homeView.res = listArr[indexPath.row].region
homeView.alpha = listArr[indexPath.row].alpha2Code
self.navigationController?.pushViewController(homeView, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
// SecondViewController
class SecondViewController: UIViewController {
#IBOutlet weak var label4: UILabel!
#IBOutlet weak var label3: UILabel!
var res = ""
var alpha = ""
override func viewDidLoad() {
super.viewDidLoad()
self.label3.text = res
self.label4.text = alpha
}
}
**

Displaying JSON imageURL into UITableView using Alamofire and SwiftyJSON

I am currently trying to display the JSON data on to my UITableView. I've created a separate class which handles and declares the data being extracted from the API. I also created a custom cell to display the data. However I am not sure how to display the images of the API. I was able to display the title of the API using just a standard TableViewController without custom cells:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "recipeCell", for: indexPath)
cell.textLabel?.text = recipes = [indexPath.row].title
return cell
}
but when trying to create a custom cell the image uses an imageURL which works differently. Other tutorials and demo don't seem to be using Alamofire and SwiftyJSON.
I did read the documentation: https://github.com/SwiftyJSON/SwiftyJSON#integration but still not sure how to solve this issue. Please tell me how you'd get the API data to display on my table.
Class
import Foundation
import SwiftyJSON
class Recipe {
var title: String!
var image: URL?
init(json: JSON) {
self.title = json["title"].stringValue
self.image = json["image"].url
}
}
CustomCell
import UIKit
class RecipeCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var imgView: UIImageView?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
ViewController
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request(searchURL, method: .get, parameters: params, encoding: URLEncoding.default, headers: headers).response { [unowned self] response in
guard let data = response.data else { return }
let json = JSON(data: data)
for recipe in json.arrayValue {
let newRecipe = Recipe(json: recipe)
self.recipes.append(newRecipe)
}
for recipe in self.recipes {
print(recipe.title)
print(recipe.image)
}
}
}
// Display JSON Data into the tableView
func tableView( _ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return recipes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "recipeCell", for: indexPath)
// What to write here?
return cell
}
Thank you in advance! :)
for this you have to make the extension of Imageview as
extension UIImageView {
func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
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() { () -> Void in
self.image = image
}
}.resume()
}
func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloadedFrom(url: url, contentMode: mode)
}
}
then you have to write cellForRowAt method as
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "recipeCell", for: indexPath)
let recipe = recipes[indexPath.row]
let imagePath = recipe.image
cell.titleLabel.text = recipe.title
cell. imgView.downloadedFrom(link: imagePath)
return cell
}