UITableView not being populated - ios

I have a method which grabs JSON and appends individual values from the JSON into the array like so:
internal let methods = Methods()
var countryData = [""]
#IBOutlet weak var countryTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
displayCountries()
}
func displayCountries() {
// Wait for task to complete before grabbing JSON
methods.getCountriesData {() -> () in
for (_, value) in self.methods.getJSON() {
self.countryData.append(String(describing: value))
}
DispatchQueue.main.async {
self.countryTableView.reloadData()
print(self.countryData)
}
}
}
I have UITableView delegates declared like so also:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return countryData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")! as UITableViewCell
let row = indexPath.row
cell.textLabel?.text = self.countryData[row]
return cell
}
The print(self.countryData) prints all of the countries within the log (100's of countries) however for some reason the countries aren't being displayed in the UITableView, does anyone understand why?

You need to specify the table view's data source. This can be done in code or in the storyboard.
In code:
countryTableView.dataSource = self
You will also need to have your data source object conform to the UITableViewDataSource protocol:
MyViewController: UIViewController, UITableViewDataSource {
Since the data is an array of strings and a property on the view controller, this should work fine.
In the storyboard:
Simply drag the dataSource outlet to the object which is providing the data (in this example it is the view controller):

Related

Swift: How to access value of selected table view cells

How can I access the value of the cell of the table view that the user selects? I know how to access the value if the user hasn't searched because I can just access the IndexPath's value of my array, but I can't when the user has searched something because the townArray won't line up with the cells that are shown.
To give you a better understanding- just say I have an array of fruits which has [apples, bananas, oranges]. If the user searches for bananas, then the only cell with text showing (results) will say bananas. If I then try to access the IndexPath element of fruits, I will get apples since it is the first element of fruits and bananas is the first element showing. What I want is to get the access the value bananas when the user selects bananas and searches "bananas" instead of apples. I know this may be confusing but please let me know if you have any thoughts on how I can solve this issue.
var searchedResult: [Fruit] = yourSearchingFunction()
tableview.reloadData()
IndexPaths would refresh correctly after TableView reloaded with a new collection of fruits
Explanation is showed with the following code:
class ViewController: UIViewController {
#IBOutlet private weak var tableView: UITableView!
private var originalFruits: [Fruit] = [] // This data is used to cache
private var fruits: [Fruit] = [] // This data is used to display on TableView
// MARK: - View Cycle
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
setupData()
}
private func setupData() {
// Loading all fruits at the first time [Apple, Banana, Orange ]
originalFruits = yourSetupDataFunctionToFetchAllFruitsAtTheFirstTime()
fruits = originalFruits
tableView.reloadData()
}
#IBAction private func actionTapToSearchButton(_ sender: Any) {
let searchingKey = searchTextField.text
if searchingKey.isEmpty {
fruits = originalFruits
} else {
fruits = yourSeacrchingFruitFunction(searchingKey)
}
tableView.reloadData()
}
}
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruits.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: FruitCell.identifier) as? FruitCell {
return cell
}
return UITableViewCell()
}
}
// MARK: - UITableViewDelegate
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}

UITableView cells not showing on first load

My TableView Cells aren't loading, when i go to another screen and come back my data shows?
any way to fix this
my Code:
import UIKit
import FirebaseDatabase
var mynamesArray = [String]()
class Services: UIViewController, UITableViewDelegate, UITableViewDataSource {
var refHandle: UInt!
#IBOutlet var tableView: UITableView!
var videos: [String] = []
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = mynamesArray[indexPath.row]
tableView.reloadData()
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
refHandle = ref.child("Services").child("ServiceA").observe(.value, with : { (snapshot) in
mynamesArray = []
for child in snapshot.children {
mynamesArray.append((child as AnyObject).key)
}
print (mynamesArray)
})
return mynamesArray.count
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCell.EditingStyle.delete {
mynamesArray.remove(at: indexPath.row)
tableView.reloadData()
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myIndex = indexPath.row
performSegue(withIdentifier: "seque", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
tableView.delegate = self
}
I am new so i don't really know how stack overflow works, do let me know if i should update anything or add anything :)
EDIT: I FORGOT TO MENTION THIS BUT I HAVE A SECOND VIEW CONTROLLER IN WHICH THE TEXT APPEARS:
import UIKit
import FirebaseDatabase
class ServicesDisplayViewController: UIViewController {
let ref = Database.database().reference()
#IBOutlet var descLabel: UILabel!
#IBOutlet var titlelabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
ref.child("ServiceC").child("Title").observeSingleEvent(of: .value, with: { DataSnapshot in
print(DataSnapshot) // replace this with textLabel or other item
})
titlelabel?.text = mynamesArray[myIndex]
descLabel?.text = descriptionList[myIndex]
// Do any additional setup after loading the view.
}
You are not using your numberOfRowInSection properly and are confusing sync and async programming.
numberOfRowsInSection is a synchronous method that needs to return "immediately" the number of rows for that section.
This means that you need to know how many cells are in that section in advance.
Inside of it, instead, you are using a firebase method to get that number, and that method is asynchronous. This means that when you return the number of rows it will always be 0 since the block of code you pass to the observe method will execute only after some time (the time of the http request to be made).
Another problem in your code is also the fact that you don't know how many times numberOfRowsInSection is called by the tableView, so you could end up re-making the same http request over and over (maybe not now, but in some future implementation of yours).
You should find a single place, that is maybe called only once and before the table view is shown, to call that method and fill your array before reloading the tableview.
In order to do that, you can add, in the viewDidLoad the code that handles the fetching of the array of items, than (once it's completed) you can reload the table (after you dispatch on main thread).
It would look something like this:
func viewDidLoad() {
super.viewDidLoad()
refHandle = ref.child("Services").child("ServiceA").observe(.value, with : { (snapshot) in
mynamesArray = []
for child in snapshot.children {
mynamesArray.append((child as AnyObject).key)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
And in numberOfRowsInSection you just return the count:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) ->
return mynamesArray.count
}

Label not appearing in Swift table

No data is appearing in my Swift table. I'm fairly new to Swift and not quite sure why this or what I might be missing. I followed the guide here for the most part with some differences:
Apple Table Creation
Here's the tableView definition:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "AccountTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? AccountTableViewCell else {
fatalError("The dequeued cell is not an instance of AccountTableViewCell.")
}
let item = userDataSource[indexPath.row]
// Dummy values just to test this out
cell.leftLabel.text = "test1";
cell.rightLabel.text = "test2";
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) ->Int {
return userDataSource.count;
// This should be an array value, but I have also tried passing a static int here as well to test
}
Here is my class definition with the implemented procotols:
class AccountViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
And here is my table cell definition:
class AccountTableViewCell: UITableViewCell {
//MARK: Properties
#IBOutlet weak var leftLabel: UILabel!
#IBOutlet weak var rightLabel: UILabel!
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
}
}
I've got both rightLabel and leftLabel setup in the Storyboard.
I can go to the account page represented by this view controller and a table display does come up - it just has absolutely no data in it.
What am I missing?
It is not sufficient to simply add a UITableView to your view controller scene. You must set the tableview's dataSource property to your view controller instance in the Storyboard connections inspector for the tableview.

How to Update UITableView With Swift?

I'm trying to populate a table view from a SQlite database. Tickets get printed in the console, but nothings shows up on the table view. What's the proper way to update and refresh? Here is my code. Thanks!
import UIKit
import SQLite
class TicketTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tickets = [String]()
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let dm = DatabaseManager.shared
let db = dm.db!
do {
for row in try db.prepare(dm.tickets) {
let ticket = row[dm.pick]
tickets.append(ticket)
debugPrint(ticket)
}
table.reloadData()
} catch {}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tickets.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ticket")!
cell.detailTextLabel!.text = tickets[indexPath.row]
return cell
}
}
Dynamic table views needs to know their delegate and datasource. If you didn't set the delegate and datasource, you can add them programmatically in your viewDidLoad function. Like this:
override func viewDidLoad() {
super.viewDidLoad()
//Set delegate and datasource
table.delegate = self
table.dataSource = self
let dm = DatabaseManager.shared
let db = dm.db!
do {
for row in try db.prepare(dm.tickets) {
let ticket = row[dm.pick]
tickets.append(ticket)
debugPrint(ticket)
}
table.reloadData()
} catch {}
}

Swift 3 - Custom cells not showing?

After a few hours of trying to figure out why my customs cells aren't showing, I have hit a bit of a bump. Using the MVC pattern:
Model.swift
struct studentProperties {
var _title : String!
var _forename : String!
var _surname : String!
}
class MainModel {
// Singleton instances
static let modelInstance = MainModel()
static var structInstance = studentProperties()
// Array of structs
var studentArray: [studentProperties] = []
// MARK: - Initialization
private init() {}
func studentInput(title: String, forename: String, surname: String) {
MainModel.structInstance._title = title
MainModel.structInstance._forename = forename
MainModel.structInstance._surname = surname
}
}
CreateStudentController.swift
I won't show the whole file, but this code is inside of a save button - user inputs data, and that data gets append to the array in model
#IBAction private func saveNewStudentButton(_ sender: UIBarButtonItem) {
// Put student data into the struct properties in model
MainModel.modelInstance.studentInput(title: studentTitle.text!, forename: studentForename.text!, surname: studentSurname.text!)
// Append the user input
MainModel.modelInstance.studentArray.append(MainModel.structInstance)
dismiss(animated: true, completion: nil)
performSegue(withIdentifier: "returnToStudentsList", sender: sender)
}
MainStudentController.swift
class MainStudentController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//MARK: - #IBOutlets
#IBOutlet weak var myTableView: UITableView!
//MARK: - #IBActions
#IBAction func addStudentButton(_ sender: UIBarButtonItem) {
dismiss(animated: true, completion: nil)
performSegue(withIdentifier: "createStudentSegue", sender: sender)
}
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
myTableView.reloadData()
}
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(MainModel.modelInstance.studentArray.count)
return MainModel.modelInstance.studentArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: MainCell = self.myTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MainCell
cell.studentTitle?.text = MainModel.modelInstance.studentArray[0]._title
cell.studentForename?.text = MainModel.modelInstance.studentArray[0]._forename
cell.studentSurname?.text = MainModel.modelInstance.studentArray[0]._surname
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myTableView.deselectRow(at: indexPath, animated: true)
}
}
There is another file - which is of UITableViewCell, but it literally only has the outlets for my custom cell.
Issue
The issue I am currently experiencing is (I think) around this method?:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(MainModel.modelInstance.studentArray.count)
return MainModel.modelInstance.studentArray.count
}
User inputs data -> it gets append to that array -> thus now I am using those elements from the array to represent the amount of rows there will be.
Sadly, it currently isn't showing any rows? I could add multiple elements and it still wouldn't show any rows. Yet, when I print that same line out, it shows that there are elements inside(dependant on how many times I add new data). I'm a little confused as to why this isn't adding rows?
I'm not sure if it's something I am missing or isn't correct in code. I would appreciate if anyone could see where I have gone wrong?
Hopefully that explains my situation.
Thanks
According to this code, your problem is just that the table view itself is not getting reloaded. Your only reload call is in viewDidLoad, so it only gets loaded when your view loads the first time (that method only gets called once). You can fix this easily by moving myTableView.reloadData() from viewDidLoad into viewWillAppear or viewDidAppear. That should be all you need to do.
Something like this:
override func viewWillAppear() {
super.viewWillAppear()
myTableView.reloadData()
}

Resources