I am trying to populate the data from the external database using a PHP script, and I can see the data has been received in viewdidload(). But I am not able to see the data in the tableview.
I tried using the breakpoint, but no luck as my program does not reach to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { ....}
Can you please suggest what I am not doing correctly.
import UIKit
class UsersViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var data: NSArray = []
#IBOutlet weak var listTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
data = dataOfJson(url: "http://kpstudio18.com/app/select.php")
print("data are as \(data)")
}
func dataOfJson(url: String) -> NSArray {
var data = NSData(contentsOf: NSURL(string: url)! as URL)
var error: NSError?
var jsonArray: NSArray = try! JSONSerialization.jsonObject(with: data! as Data, options: .mutableContainers) as! NSArray
return jsonArray
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if data.count != 0 {
return data.count
} else {
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: additionInfoCell = self.listTableView.dequeueReusableCell(withIdentifier: "customCell") as! additionInfoCell
let maindata = (data[indexPath.row] as! NSDictionary)
cell.name.text = maindata["name"] as? String
cell.info.text = maindata["id"] as? String
print("Cell Name is \(cell.name.text)")
print("Cell Info (id) is \(cell.info.text)")
return cell
}
}
yourtableview_name.registerNib(UINib(nibName: "customCell", bundle: nil), forCellReuseIdentifier: "customCell")
try this in viewdidload.if it works let me know.
You simply forgot to reload your table. In your viewDidLoad() method add the following line yourTableView.reloadData() and hopefully it will work.
Also, check if you registered your "CustomCell" as said above by RB1509
Related
I recieves json data from api and totally confused what should I do next to pass this data to custom cell with imageView and labels in order to update UI in tableView.
Getting JSON
import Foundation
struct Breed: Codable {
let name: String?
let origin: String?
let life_span:String?
let temperament: String?
let description: String?
let wikipedia_url: String?
let image: Image?
}
struct Image: Codable {
let url: String?
}
func getDataFromCatsApi() {
let url = URL(string: "https://api.thecatapi.com/v1/breeds")
let task = URLSession.shared.dataTask(with: url!) { data, _ , error in
let decoder = JSONDecoder()
if let data = data {
let breed = try? decoder.decode([Breed].self, from: data)
print (breed as Any)
} else {
print (error as Any)
}
}
task.resume()
}
All data is printed correctly.
In ViewController I have a tableView with custom cell.
import UIKit
class MainVC: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
title = "Cats"
view.backgroundColor = .systemBackground
getDataFromCatsApi()
}
}
extension MainVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as? CustomTableViewCell
return cell ?? CustomTableViewCell()
}
}
Class for custom cell. Here I have imageView and labels for displaying data from json.
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var catImageView: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var originLabel: UILabel!
#IBOutlet weak var addToFavButton: UIButton!
}
First of all, you are not returning anything from getDataFromCatsApi(). Since it is an asynchronous call, you have to implement a way to get the values either by using a callback or a delegate. In this case callback would suffice.
Then once you receive a value from the api call, set those values in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) in which you can use cell.nameLabel.text = <received value> and etc.
First of all declare only those properties as optional which can be nil
struct Breed: Decodable {
let name, origin, lifeSpan, temperament, description: String
let wikipediaUrl: String?
let image: Image?
}
struct Image: Decodable {
let url: String?
}
In getDataFromCatsApi add a completion handler
func getDataFromCatsApi(completion: #escaping (Result<[Breed],Error>) -> Void ) {
let url = URL(string: "https://api.thecatapi.com/v1/breeds")
let task = URLSession.shared.dataTask(with: url!) { data, _ , error in
if let error = error { completion(.failure(error)); return }
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
completion(Result { try decoder.decode([Breed].self, from: data!) })
}
task.resume()
}
In MainVC declare a data source array
var cats = [Breed]()
Replace viewDidLoad with
override func viewDidLoad() {
super.viewDidLoad()
title = "Cats"
view.backgroundColor = .systemBackground
getDataFromCatsApi {[unowned self] result in
DispatchQueue.main.async {
switch result {
case .success(let breed):
self.cats = breed
self.tableView.reloadData()
case .failure(let error): print(error)
}
}
}
}
and the table view datasources methods with
extension MainVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cats.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as! CustomTableViewCell
let cat = cats[indexPath.row]
cell.nameLabel = cat.name
cell.originLabel = cat.origin
}
}
To load the pictures is beyond the scope of the question. There are libraries like SDWebImage or Kingfisher to load and cache images asynchronously.
I have attached the code please check
extension MainVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Breed.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as? CustomTableViewCell
cell.nameLabel.text = Breed[indexPath.row].name // see here
return cell ?? CustomTableViewCell()
}
}
As shown below I want to sort my TableViewCells by the date. For this I have the time which is also called timestampName.
Right before I reload the data, I tried to sort it, but somehow this has no effect. It also throws me a warning, that I dont use the result of the sorted by. I understand this, but I dont know how to fix that.
import UIKit
import Firebase
class popularViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet var table: UITableView!
// var models = [PhotoPost]()
var texttt = [TextPost]()
override func viewDidLoad() {
super.viewDidLoad()
gettingPosts()
table.register(popularTableViewCell.nib(), forCellReuseIdentifier: popularTableViewCell.identifier)
table.register(featuredTableViewCell.nib(), forCellReuseIdentifier: featuredTableViewCell.identifier)
table.register(textTableViewCell.nib(), forCellReuseIdentifier: textTableViewCell.identifier)
table.delegate = self
table.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return texttt.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: textTableViewCell.identifier, for: indexPath) as! textTableViewCell
cell.configure(with: self.texttt[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 300
}
func gettingPosts(){
let db = Firestore.firestore()
let postsRef = db.collection("posts")
postsRef.addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added){
let data = diff.document.data()
let Name = data["username"] as! String
let text = data["description"] as! String
let likes = data["likes"] as! Int
let typ = data["postType"] as! Int
let pfp = data["profileImage"] as! String
let uid = data["uid"] as! String
let pic = data["picture"]
let time = data["time"] as! String
if typ == 0{ // Text post
let dasDing = TextPost(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text)
self.texttt.append(dasDing)
self.texttt.sorted(by: { $0.timestampName < $1.timestampName }) //WARNING: Result of call to 'sorted(by:)' is unused
self.table.reloadData()
}
struct TextPost {
let numberOfComments: Int
let username: String
let timestampName: String
let userImageName: String
let textName: String
}
Use sort instead of sorted. The sorted method returns a new sorted array, on the other hand, the sort method sorts the array on which it was called.
self.texttt.sort(by: { $0.timestampName < $1.timestampName })
This should also work, using sorted:
self.texttt = self.texttt.sorted(by: { $0.timestampName < $1.timestampName })
I have fixed my earlier problem and have now worked out where the main problem is, I am pulling in a json array with alamofire but am not sure how to properly move the data from one viewcontroller to another. If I hardcode the array with var name = ["Hello", "Goodbye"] I can get it to work but am not sure how to do it with the json. Thank you to any and all help.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let URL_GET_DATA = "http://www.localnewsplus.com.au/ios/service.php"
#IBOutlet weak var tableViewHeroes: UITableView!
var heroes = [Hero]()
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return heroes.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
let hero: Hero
hero = heroes[indexPath.row]
cell.labelName.text = hero.name
cell.labelTeam.text = hero.team
Alamofire.request(hero.imageUrl!).responseImage { response in
if let image = response.result.value {
cell.heroImage.image = image
}
}
//cell.labelName.text = name[indexPath.row]
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(URL_GET_DATA).responseJSON { response in
if let json = response.result.value {
let heroesArray : NSArray = json as! NSArray
for i in 0..<heroesArray.count{
self.heroes.append(Hero(
name: (heroesArray[i] as AnyObject).value(forKey: "st_heading") as? String,
team: (heroesArray[i] as AnyObject).value(forKey: "st_modified") as? String,
imageUrl: (heroesArray[i] as AnyObject).value(forKey: "imageurl") as? String
))
}
self.tableViewHeroes.reloadData()
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "articleViewController") as? articleViewController
vc?.article_st_heading = name[indexPath.row]
self.navigationController?.pushViewController(vc!, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
There are 2 ways to do this
Try to get data from the array which you used in cellForRow to populate data
Let text = someArray[indexPath. Row]
Get the cell instead of the create new one in didSelect method
Let cell = table. CellForRowAt[indexPath ]
Let text = cell.text
I managed to include this API from America FAA website in order to download the NOTAM for a pilot. I manage to send the request of the param "api key, location, state". I get back my JSON data and it works fine. Now my problem is I want to display on a tableView one item of the array that I got back in JSON format, is the item called 'all'.
I created the IBOutlet. I gave the cell identifier, but I'm stuck here
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//Constants
let notamUrl = "https://v4p4sz5ijk.execute-api.us-east-1.amazonaws.com/anbdata/states/notams/notams-realtime-list"
let api_key = "mykey"
let notamModel = ModelloNotam()
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return //????????? i dont know
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath)
// ????????? i dont know
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate=self
tableView.dataSource=self
func getNOTAM (url:String,parameters:[String:String]){
Alamofire.request(url, method: .get, parameters: parameters).responseJSON {
response in
if response.result.isSuccess{
let notamJSON : JSON = JSON (response.result.value!)
self.displayNotam(json: notamJSON)
} else{
print("errore connessione\(response.result.error)")
}
}
}
var locations = "VMMC"
var state = "CHN"
let params : [String : String] = ["locations" : locations, "state" : state, "api_key" : api_key]
getNOTAM(url: notamUrl, parameters: params)
}
func displayNotam (json:JSON) {
let conta = json.count
for var i in 0...conta {
i = i + 1
notamModel.all = json [i]["all"].stringValue
notamModel.type = json [i]["type"].stringValue
// print("The NOTAM type is \(notamModel.type)")
// print(notamModel.all)
// print("************************")
}
}
}
please refer this tutorial https://www.youtube.com/watch?v=sd7d4eoM54U&t=1857s and model data according to JSON file. networking request goes out of main execution queue.
i solve with this code.!!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellid", for: indexPath)
// let text = array[indexPath.row]
// cell.textLabel?.text=text
(cell.contentView.viewWithTag(1) as! UILabel).text = array[indexPath.row]
(cell.contentView.viewWithTag(2) as! UILabel).text = array2[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
thanks for the help.
As i read in the documentation i can access single url in firebase storage like this:
`// Create a reference to the file you want to download
let starsRef = storageRef.child("images/stars.jpg")
// Fetch the download URL starsRef.downloadURL { url, error in
if let error = error {
// Handle any errors }
else {
// Get the download URL for 'images/stars.jpg'
} }`
However, i have many files there, so how can i skip giving direct path and instead iterate through all files in the given directory?
Thanks for tips.
DownloadURL takes single string at a time. In case you want to show all the files inside a folder to a tableview like me, here is the
full code:
import UIKit import Firebase
My very First View Controller-
class FolderList: UIViewController {
var folderList: [StorageReference]?
lazy var storage = Storage.storage()
#IBOutlet weak var tableView : UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.storage.reference().child("TestFolder").listAll(completion: {
(result,error) in
print("result is \(result)")
self.folderList = result.items
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
} }
extension FolderList : UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return folderList?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "FolderListCell", for:
indexPath) as? FolderListCell else {return UITableViewCell()}
cell.itemName.text = folderList?[indexPath.row].name
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 64.0
} }
extension FolderList : UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
guard let downloadVC = storyBoard.instantiateViewController(withIdentifier:
"DownloadedItemView") as? DownloadedItemView else {
return
}
downloadVC.storageRef = folderList?[indexPath.row]
self.navigationController?.pushViewController(downloadVC, animated: true)
}
}
You each cell:
class FolderListCell: UITableViewCell {
#IBOutlet weak var itemName : UILabel!
}