My app retrieves json from the newsAPI.com . When I look at the json returned from the web service , it shows 2000 values however it returns 20 values loaded into my tableview controller. How do I increase the value so that when the user scrolls down the table view, they are presented with more values loaded into the table view controller?
class LatestNewsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let newsData = Articles() //Model object
let urlRequest = "https://newsapi.org/v2/everything?q=coronavirus&apiKey=d32071cd286c4f6b9c689527fc195b03" //Website API
var urlSelected = ""
var articles: [Articles]? = [] // holds array of Articles data
var indexOfPageToRequest = 1
#IBOutlet weak var table_view: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table_view.cellLayoutMarginsFollowReadableWidth = true
navigationController?.navigationBar.prefersLargeTitles = true
retriveData( )
}
func retriveData( )->Int{
guard let aritcleUrl = URL(string: urlRequest) else { //send a request to the server
return n
}
let request = URLRequest(url: aritcleUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in //collects content from website
if error != nil { // checks if content is available
print(error ?? 0)
return
}
if let data = data { // converts data to an array of Article objects
self.articles = self.parseData(data: data)
}
})
task.resume()
return n
}
func parseData(data:Data)-> [Articles] {
var articles: [Articles]? = [] // holds parsed data
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
let jsonArticles = jsonResult?["articles"] as? [AnyObject] ?? [] // gets first head of json file and converts it to dictionary
for jsonArticle in jsonArticles{ // captures data and stores it in the model object
let article = Articles()
article.author = jsonArticle["author"] as? String
article.title = jsonArticle["description"] as? String
article.publishedAt = jsonArticle["publishedAt"] as? String
article.urlImage = jsonArticle["urlToImage"] as? String
article.urlWebsite = jsonArticle["url"] as? String
articles?.append(article) //put article data in the array
}
print(jsonArticles)
DispatchQueue.main.async {
if(articles!.count > 0)
{
self.table_view.reloadData()
}
}
} catch {
print("Nothing my guy\(error)")
}
return articles ?? [] // returns an array of articles
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return articles?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! NewsTableViewCell
cell.authorName.text = articles?[indexPath.row].author
cell.headLine.text = articles?[indexPath.row].title
cell.newsImage.downloadImage(from:(self.articles?[indexPath.item].urlImage ?? "nill"))
cell.timePublication.text = articles?[indexPath.row].publishedAt
return cell
}
[1]: https://i.stack.imgur.com/OY5G5.png
First, I would check the constraints of the table view, because this is a common issue (usually 0,0,0,0)
And also I would check the 'scrolling enabled' in the Attribute inspector and 'reuse Identifier'
Related
I've already seen: Swift UITableView reloadData in a closure but it still does not work. That's why I'm creating a new thread for this.
I'm trying to insert Firestore data into a custom tableview. But when I print the numbers it returns (In the console):
"MyDogwalk.listTxt"
And no data is showing up on the tableview.
I guess all of this is relevant. (I also have 2 classes, with init etc)
class HistoryViewController: UIViewController {
//Tableview
#IBOutlet weak var tableView: UITableView!
let db = Firestore.firestore()
var list: [listTxt] = []
override func viewDidLoad()
{
super.viewDidLoad()
list = createArray()
tableView.delegate = self
tableView.dataSource = self
}
func createArray() -> [listTxt]
{
var tempTxt: [listTxt] = []
//Authentication
let authentication = Auth.auth().currentUser?.uid
//Choosing collection
db.collection("rastad").document(authentication!).collection("promenad").getDocuments()
{ (QuerySnapshot, err) in
if err != nil
{
print("Error getting documents: \(String(describing: err))");
}
else
{
//For-loop
for _ in QuerySnapshot!.documents
{
self.list.removeAll()
let document = QuerySnapshot!.documents.first
let data = document!.data()
data.forEach { (item) in
let data1 = data["Dog"] as? String
let data2 = data["Person"] as? String
let data3 = data["What"] as? String
let data4 = data["Date"] as? String
let data5 = data["Time"] as? String
let txt = listTxt(dog: data1!, person: data2!, action: data3!, time: data4!, date: data5!)
print(txt)
tempTxt.append(txt)
}
}
self.tableView.reloadData()
}
}
//return tempTxt
return list
}
}
extension HistoryViewController: UITableViewDelegate, UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let listPath = list[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell") as! HistoryCell
cell.setCell(list: listPath)
return cell
}
}
And.. Why is this downvoted? I got an answer that was actually working for this case, and the question is detailed enough for people to understand, isn't it?
createArray() method runs async code, and fills tempTxt. But you are returning tempTxt before async code has been run. So instead returning from createArray method and setting its value to self.list, just do it in the method itself:
self.list = tempTxt
self.tableView.reloadData()
You are iterating over documents but always using data of documents.first. Try this:
self.list.removeAll()
for document in QuerySnapshot!.documents {
let data = document!.data()
data.forEach { (item) in
let data1 = data["Dog"] as? String
let data2 = data["Person"] as? String
let data3 = data["What"] as? String
let data4 = data["Date"] as? String
let data5 = data["Time"] as? String
self.list.append(listTxt(dog: data1!, person: data2!, action: data3!, time: data4!, date: data5!))
}
}
self.tableView.reloadData()
Change self.tableView.reloadData() to
self.list = tempTxt
DispatchQueue.main.async {
self.tableView.reloadData()
}
And skip returning array from that func
I made a news feed from JSON using TableView and Prototype Cell with three Labels but meet a problem. All lines of feed fetch same data although JSON has differents.
JSON give to my app 15 blocks with data. All lines show content which has to be last.
Its seem like array has error and doing not correct. But I can't understand why tableview pull from him only last line data and put in to each cells.
What the reason my problem? What I do wrong? Please help me.
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "newsfeedCell", for: indexPath) as! NewsFeedCell
cell.newsfeed_title.text = self.news?[indexPath.item].headline
//cell.newsfeed_title.text = "Динамо обыграло Шахтер"
cell.newsfeed_topic.text = self.news?[indexPath.item].topic
//cell.newsfeed_topic.text = "Премьер-лига"
cell.newsfeed_time.text = self.news?[indexPath.item].time
//cell.newsfeed_time.text = "17:22"
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.news?.count ?? 0
} //number of rows
#IBOutlet weak var tableview: UITableView!
var news: [Newsfeed]? = []
override func viewDidLoad() {
super.viewDidLoad()
getJSON()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getJSON(){
let urlRequest = URLRequest(url: URL(string: "any_json_url")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
if error != nil {
print(error as Any)
return
}
self.news = [Newsfeed]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSArray
let NF = Newsfeed()
for arrayX in json as! [[String: AnyObject]] {
if let ID = arrayX["id"],
let date = arrayX["date"],
let status = arrayX["status"],
let title0 = arrayX["title"] as? [String: Any],
let title = title0["rendered"] {
NF.headline = title as? String
NF.topic = status as? String
NF.id = ID as? String
NF.time = date as? String
print(ID)
print(title)
print(date)
print(status)
}
self.news?.append(NF)
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
} catch let error {
print(error)
}
}
task.resume()
}
}
You are writing always into the same Newsfeed instance and if it's a class – it obviously is – you are using always the same object.
Change the order. Replace
let NF = Newsfeed()
for arrayX in json as! [[String: AnyObject]] {
with (by the way a JSON dictionary in Swift 3+ is always [String:Any])
for arrayX in json as! [[String: Any]] {
let NF = Newsfeed()
And declare the data source array as non optional
var news = [Newsfeed]()
then you get rid of a lot of question marks and ugly syntax like return self.news?.count ?? 0. Just return self.news.count
Maybe:
cell.newsfeed_title.text = self.news?[indexPath.row].headline
I have spent about three weeks trying to figure this out. I can get the section title to view, but none of the JSON data is showing. When I do a standard "array" contained in the file, it displays.
I have followed every tip and trick out there and I am stuck.
I think this may have something to do with AnyObject and String, but I am missing something. Please see my code below:
import UIKit
import Alamofire
import SwiftyJSON
class UserTableViewCell: UITableViewCell {
#IBOutlet weak var userFirstname: UILabel!
#IBOutlet weak var userLastname: UILabel!
}
class Profile2VC: UITableViewController {
#IBOutlet var userTable: UITableView!
var usertitles = ["First Name", "Last Name", "Email", "Mobile Number"]
var userinfo = [[String:AnyObject]]() //Array of dictionary
override func viewDidLoad() {
super.viewDidLoad()
let defaultValues = UserDefaults.standard
let URL_USER_LOGIN = "https://www.myapp.com/myphp.php"
let userid = "13"
let parameters: Parameters=["id":coolid]
Alamofire.request(URL_USER_LOGIN, method: .get, parameters:
parameters).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
print(swiftyJsonVar)
if let userData = swiftyJsonVar["user"].arrayObject {
self.userinfo = userData as! [[String:AnyObject]]
//debugPrint(userData)
}
if self.userinfo.count > 0 {
self.userTable.reloadData()
}
}
}
self.userTable.reloadData()
// Uncomment the following line to preserve selection between
presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the
navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return userinfo.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection
section: Int) -> String? {
return "Section \(section)"
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell",
for: indexPath) as! UserTableViewCell
//let userTitles = usertitles[indexPath.row]
let userInfo = userinfo[indexPath.row]
cell.userFirstname?.text = userInfo["first_name"] as? String
cell.userLastname?.text = userInfo["last_name"] as? String
//cell.imageView?.image = UIImage(named: fruitName)
//cell.textLabel?.text = usertitles[indexPath.row]
return cell
}
}
First of all you need to reload your table view in main queue. Check below code:
DispatchQueue.main.async {
self.userTable.reloadData()
}
And you are reloading it multiple times which is not good so Remove unwanted reload code and you final code will be:
Alamofire.request(URL_USER_LOGIN, method: .get, parameters: parameters).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
print(swiftyJsonVar)
if let userData = swiftyJsonVar["user"].arrayObject {
self.userinfo = userData as! [[String:AnyObject]]
//debugPrint(userData)
}
if self.userinfo.count > 0 {
DispatchQueue.main.async {
self.userTable.reloadData()
}
}
}
}
//self.userTable.reloadData() //Remove this code
And once your API call done, Make sure debugPrint(userData) is printing some data and then when you are reloading userTable put a breakpoint in cellForRowAt and confirm that it's calling.
Then if its calling and data is there from server, You are good to go.
But if cellForRowAt method didn't called then you need to check your userTable DataSource and Delegate if it's correctly connected or not.
Try this code :
let API = URL(string:"http://www.myapp.com/myphp.php")
let request = URLRequest(url:API!)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
if let data = data {
if String(data: data, encoding: String.Encoding.utf8) != nil {
let data = data
let json = try? JSONSerialization.jsonObject(with: data, options: [])
let jsonData = json as! [[String:Any]]
DispatchQueue.main.sync {
let user = jsonData.flatMap { $0["user"] as? String }
print(user)
self.annocumentTableView.reloadData()
}
}
}
})
task.resume()
Getting a JSON object from a rest web service I get the data from the object and I want to show it in a tableview.
class TableViewController1: UITableViewController {
var nomProduit = ["ok"]
var prixProduit = [""]
var vt1 : String?
var vt2 : String?
var i : Int!
var compteur1:Int!
var resultat1:NSArray?
var x : AnyObject?
override func viewDidLoad() {
super.viewDidLoad()
// \(detectionString)
let str:String = "http://vps43623.ovh.net/yamoinscher/api/products/6194005492077"
let url = NSURL(string: str)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
self.resultat1 = jsonResult["meme_categorie"] as? NSArray
self.compteur1 = self.resultat1!.count
print(self.compteur1!)
//self.value = (compteur1 as? Int)!
for self.i=0 ; self.i < self.compteur1! ; self.i = self.i+1 {
if let aStatus = self.resultat1![self.i] as? NSDictionary{
self.vt1 = aStatus["libelle_prod"]! as? String
self.nomProduit.append(self.vt1!)
self.vt2 = aStatus["prix"]! as? String
self.prixProduit.append(self.vt2!)
//print(self.nomProduit[self.i])
}
}
} catch {
print("JSON serialization failed")
}
}
}
task.resume()
}
Then My problem is that this array stays nil:
self.prixProduit.append(self.vt2!)
here is the rest of my code
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 17
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! customCell1
// cell.PrixSim.text = nomProduit[indexPath.row]
print(self.nomProduit[0])
return cell
}
First of all use a custom struct for the category objects, it makes things so much easier.
At the beginning of TableViewController1
class TableViewController1: UITableViewController {
declare this struct
struct Produit {
var id : String
var prix : String
var title : String
}
and a data source array (forget all your other properties / variables)
var produits = [Produit]()
In viewDidLoad get the data, populate the data source array and reload the table view on the main thread.
This code uses Swift native collection types
override func viewDidLoad() {
super.viewDidLoad()
// \(detectionString)
let str = "http://vps43623.ovh.net/yamoinscher/api/products/6194005492077"
let url = NSURL(string: str)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(urlContent, options: [])
if let jsonResult = jsonObject as? [String:AnyObject] {
if let memeCategorie = jsonResult["meme_categorie"] as? [[String:String]] {
for categorie in memeCategorie {
if let prix = categorie["prix"], title = categorie["libelle_prod"], id = categorie["id"] {
self.produits.append(Produit(id: id, prix: prix, title: title))
}
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
}
} catch {
print("JSON serialization failed", error)
}
} else if let connectionError = error {
print("connection error", connectionError)
}
}
task.resume()
}
In numberOfRowsInSection return the actual number of items rather than a hard-coded number.
You can omit numberOfSectionsInTableView since the default value is 1.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return produits.count
}
In cellForRowAtIndexPath get the item by index path and assign the values to your labels (or whatever). For now the values are just printed out.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! customCell1
let produit = produits[indexPath.row]
print(produit.id, produit.title, produit.prix)
return cell
}
}
A new programmer here. How would I populate my tableView from this JSON?
My first problem is the JSON Serialization and then plugging it in the tableView.
Code
import UIKit
class LegislatorsTableVC: UITableViewController {
// MARK: Variables & Outlets
private let cellIdentifer = "cellReuse"
// MARK: View Did Load
override func viewDidLoad() {
super.viewDidLoad()
// Creating Congfiguration Object // Session Is Created // Getting Info/Data
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration)
let apiKey = "https://congress.api.sunlightfoundation.com/legislators?apikey=xxxxxxxxxxxxxxxxxxxxx&all_legislators=true&per_page=all"
if let url = NSURL(string: apiKey) {
// Spawning Task To Retrieve JSON Data
session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
// Checking For Error
if let error = error {
print("The error is: \(error)")
return
}
// Response
if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200, let data = data {
print("Status Code: \(httpResponse.statusCode)")
// self.JSONSerialization(data)
}
}).resume()
}
} // End Of View Did Load
// JSON Serialization Function With SwiftyJSON.swift
private func JSONSerialization(data: NSData){
// I See this Gets A Status Code 200 And Then I'm Lost.
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as! [String: AnyObject]
} catch {
print("Error Serializing JSON Data: \(error)")
}
} // End Of JSONSerialization
// MARK: - Table view data source
// Number Of Sections
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
} // End Of Number Of Sections
// Number Of Rows In Section
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 15
} // End Of Number Of Rows In Section
// Cell For Row At Index Path
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifer, forIndexPath: indexPath) as! LegislatorTVCell
// Configure the cell...
cell.name.text = "Name"
cell.title.text = "Title"
cell.party.text = "Party"
return cell
} // End Of Cell For Row At Index Path
}
Create a custom class Person outside the view controller
class Person {
var firstName = ""
var lastName = ""
var title = ""
var party = ""
}
Create an array of Person in the view controller
var people = [Person]()
The JSON has a key results which contains an array of dictionaries.
In viewDidLoad parse the JSON and create Person instances. Finally reload the table view.
override func viewDidLoad() {
super.viewDidLoad()
// Creating Congfiguration Object // Session Is Created // Getting Info/Data
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration)
let apiKey = "https://congress.api.sunlightfoundation.com/legislators?apikey=xxxxxxxxxxxxxxxxxx&all_legislators=true&per_page=all"
if let url = NSURL(string: apiKey) {
// Spawning Task To Retrieve JSON Data
session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
// Checking For Error
if error != nil {
print("The error is: \(error!)")
return
} else if let jsonData = data {
do {
let parsedJSON = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! [String:AnyObject]
guard let results = parsedJSON["results"] as? [[String:AnyObject]] else { return }
for result in results {
let person = Person()
person.firstName = result["first_name"] as! String
person.lastName = result["last_name"] as! String
person.party = result["party"] as! String
person.title = result["title"] as! String
self.people.append(person)
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
} catch let error as NSError {
print(error)
}
}
}).resume()
}
} // End Of View Did Load
The table view delegate methods look very clear when using a custom class.
Since cellForRowAtIndexPath is called very often the code is quite effective.
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifer, forIndexPath: indexPath) as! LegislatorTVCell
let person = people[indexPath.row]
cell.name.text = person.firstName + " " + person.lastName
cell.title.text = person.title
cell.party.text = person.party
return cell
} // End
Of course I couldn't test the code but this might be a starting point.
Basically what you want to do is introduce a new variable to your class, for example jsonDict like so:
class LegislatorsTableVC: UITableViewController {
var jsonDict:Dictionary<String,AnyObject>?
// further code
And then - you almost got it right already - save your JSON serialization into that in your JSONSerialization function. (which I would rename to parseJSON or something like that to avoid confusion) like so:
do {
jsonDict = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as! [String: AnyObject]
} catch {
print("Error Serializing JSON Data: \(error)")
}
So then you can return the right values to your tableView data source:
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return jsonDict["your JSON key"].count ?? 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jsonDict["your JSON key"]["items"].count ?? 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifer, forIndexPath: indexPath) as! LegislatorTVCell
let item = jsonDict["your JSON key"][indexPath.row]
// Configure the cell...
cell.name.text = item["name"]
cell.title.text = item["title"]
cell.party.text = item["party"]
return cell
}
Naming is a little confusing, as I don't know the layout of your JSON, but replace your JSON key with your path to the data of course.