UITableView populated but not displaying data or rows - ios

I know this particular question has been asked and answered previously in SO but cross checked those answers and still not able to fix this issue. Can be a silly mistake but unable to nail it.
Cross checked :
Cell Identifierid
datasource and delegate added through Interface builder
Code :
var sensorFields = [Sensor]()
override func viewDidLoad() {
super.viewDidLoad()
readParseDataFromCSV(file: "csvFile")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(self.sensorFields.count)// has count 120
return self.sensorFields.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SensorTableCell") as! SensorTableViewCell
cell.lblSensorName.text = sensorFields[indexPath.row].labelName
cell.lblSensorValue.text = sensorFields[indexPath.row].labelValue
print(sensorFields[indexPath.row].labelName) // doesn't seem to enter to this code block
print(sensorFields[indexPath.row].labelValue)
return cell
}
func readParseDataFromCSV(file:String){
let filepath = Bundle.main.path(forResource: file, ofType: "csv")!
do {
let csv = try CSV(contentsOfURL: filepath)
let rows = csv.rows
for row in rows{
let sensorlblName = row["data column 1"]
let sensorlblValue = row["data column 2"]
let sensor = Sensor(labelName: sensorlblName!, labelValue: sensorlblValue!)
sensorFields.append(sensor)
}
} catch {
print(error.localizedDescription)
}
}
class SensorTableViewCell: UITableViewCell {
#IBOutlet weak var lblSensorName: UILabel!
#IBOutlet weak var lblSensorValue: 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
}
}
Is there anything missed out , using other tableviews in the app already which is working perfectly apart from this one.
Using Xcode 8.1 and Swift 3.0
Edit :
Added reloadData()
Seems like
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}
is not firing too.
Please help.
Thanks in advance.

Just appending data to your sensorFields array has no impact on the cells displayed in your tableView.
You need to call reloadData on the tableView so it knows the data did change and repopulates the cells with the new data.
for row in rows {
let sensorlblName = row["data column 1"]
let sensorlblValue = row["data column 2"]
let sensor = Sensor(labelName: sensorlblName!, labelValue: sensorlblValue!)
sensorFields.append(sensor)
}
tableView.reloadData()
I would also prefer
// newer dequeue method guarantees a cell is returned and resized properly, assuming identifier is registered
// Swift 2
func dequeueReusableCellWithIdentifier(identifier: String, forIndexPath indexPath: NSIndexPath) -> UITableViewCell
// Swift 3
func dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell
to
// Used by the delegate to acquire an already allocated cell, in lieu of allocating a new one.
// Swift 2
func dequeueReusableCellWithIdentifier(identifier: String) -> UITableViewCell?
// Swift 3
func dequeueReusableCell(withIdentifier identifier: String) -> UITableViewCell?
because dequeueReusableCellWithIdentifier(identifier: String) does not guarantee returning a UITableViewCell when the cell with identifier was not yet allocated. ("[...] acquire an already allocated cell, in lieu of allocating a new one.")
And make sure your IBOutlets are connected! (lblSensorName and lblSensorValue)

check this
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell = tableView.dequeueReusableCellWithIdentifier("SensorTableCell") as UITableViewCell!
if !cell {
cell = UITableViewCell(style:.Default, reuseIdentifier: "SensorTableCell")
}
}

Related

Sorting custom table view cells in Swift

I am working on an small project where I have an app that takes in tvshow information entered by the user and displays it in a custom tableview cell. I would like to sort the shows as they are entered based on which current episode the user is on. I know this code works because I tested it with print statements and it sorts the array but it does not sort on the simulator. So I just was curious where I should place this so that it sorts on the app side.
func sortShows() {
let sortedShows = tvShows.sorted { $0.currentEpisode > $1.currentEpisode}
TVShowTableView.reloadData()
print(sortedShows)
}
Here is where I am currently placing it inside my view controller
extension TVShowListViewController: AddTVShowDelegate {
func tvShowWasCreated(tvShow: TVShow) {
tvShows.append(tvShow)
dismiss(animated: true, completion: nil)
TVShowTableView.reloadData()
sortShows()
}
}
In this part of your code:
func sortShows() {
// here you are creating a NEW array
let sortedShows = tvShows.sorted { $0.currentEpisode > $1.currentEpisode}
// here you tell the table view to reload with the OLD array
TVShowTableView.reloadData()
print(sortedShows)
}
In your controller class, you probably have something like:
var tvShows: [TVShow] = [TVShow]()
and then you populate it with shows, like you do with a new show:
tvShows.append(tvShow)
Then your controller is doing something like:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tvShowCell", for: indexPath) as! TVShowCell
cell.tvShow = tvShows[indexPath.row]
return cell
}
What you want to do is add another var to your class:
var sortedShows: [TVShow] = [TVShow]()
then change your sort func to use that array:
func sortShows() {
// use the existing class-level array
sortedShows = tvShows.sorted { $0.currentEpisode > $1.currentEpisode}
// here you tell the table view to reload
TVShowTableView.reloadData()
print(sortedShows)
}
and change your other funcs to use the sortedShows array:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// use sortedShows array
return sortedShows.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tvShowCell", for: indexPath) as! TVShowCell
// use sortedShows array
cell.tvShow = sortedShows[indexPath.row]
return cell
}
and you'll want to call sortShows() at the end of viewDidLoad() (or wherever you are getting your initial list of shows).
Edit
Another way you might use cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tvShowCell", for: indexPath) as! TVShowCell
// use sortedShows array
let tvShow = sortedShows[indexPath.row]
cell.showTitleLable.text = tvShow.title
cell.showDecriptionLable.text = tvShow.description
return cell
}

UITableViewCell dequeuereusablecellwithidentifier returns the same cell

I am creating a UITableView that enables the user to add a variable amount of data. Table looks like this initially:
When the user clicks on the "+" button, i would like to add a new cell with a UITextField for entering data. This new cell is a Custom UITableViewCell called "RecordValueCell". Here's what is looks like:
//Custom UITableViewCell
class RecordValueCell : UITableViewCell {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var deleteButton: UIButton!
var onButtonTapped : ((_ sender : UIButton)->Void)?
#IBAction func deleteButtonTouched(_ sender: Any) {
guard let senderButton = sender as? UIButton else {
return
}
onButtonTapped?(senderButton)
}
}
However when i try to add another cell, using the tableView.dequeueReusableCell(withIdentifier: ) function, it seems to return the same cell. And here is what my UI looks like:
Empty space at the top of the section where my new cell should be. Here is the code to add the cell:
func addNewValueCell() {
guard let reusableValueCell = self.tableView.dequeueReusableCell(withIdentifier: "valueCell") as? RecordValueCell else {
fatalError("failed to get reusable cell valueCell")
}
var cell = Cell() //some custom cell Object
//add the gray horizontal line you see in the pictures
reusableValueCell.textField.addBorder(toSide: .Bottom, withColor: UIColor.gray.cgColor, andThickness: 0.5)
reusableValueCell.onButtonTapped = { (sender) in
self.removeValue(sender: sender)
}
cell.cell = reusableValueCell
self.sections[self.sections.count - 1].cells.insert(cell, at: 0)
//When i put a break point at this spot, i find that reusableValueCell is the same object as the cell that is already being used.
tableView.reloadData()
reusableValueCell.prepareForReuse()
}
When i debug it, i find that dequeueReusableCell(withIdentifier: ) returns the exact same RecordValueCell multiple times.
Here is my cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = self.sections[indexPath.section].cells[indexPath.row].cell else {
fatalError("error getting cell")
}
return cell
}
numberOfRowsInSection
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.sections[section].cells.count
}
First of all, you will need to set the View Controller Class that this table is contained in as the table's UITableViewDataSource
tableView.dataSource = self // view controller that contains the tableView
Create an array of strings as member of your View Controller class which contains the data for each cell:
var strings = [String]()
Then you will need to implement the following method for the UITableViewDataSource protocol:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return strings.count
}
You should also be dequeueing the cells in your cellForRowAt method like so:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: yourIdentifier) as! YourCellClass
cell.textLabel = strings[indexPath.row]
return cell
}
Then whenever the user enters into the textField, their input will be appended to this array:
let input = textField.text
strings.append(input)
tableView.reloadData()
Once the data is reloaded, the cell will be added to the table automatically since the number of rows are defined by the String array's length and the label is set in the cellForRowAt method.
This feature is very easy to implement if you will do in a good way.
First, you have to create two TableCell. First to give the option to add a record with plus button and second for entering a value with textfield. Now always return first cell (AddRecordTableCell) in the last row in tableView, and return the number of rows according to entered values like
return totalValues.count + 1

UITableView.reloadData() is not refreshing tableView. No error message

I've already looked at the post UITableView.reloadData() is not working. I'm not sure that it applies to my situation, but let me know if I'm wrong.
My app has a tableView. From the main viewController I am opening another viewController, creating a new object, and then passing that object back to the original viewController, where it is added to an array called timers. All of that is working fine. However, when I call tableView.reloadData() in didUnwindFromNewTimerVC() to display the updated contents of the timers array, nothing happens.
NOTE: I have verified that the timers array is updated with the new object. Its count increments, and I can access its members. Everything else in didUnwindFromNewTimerVC() executes normally. The tableView just isn't updating to reflect it.
Here is my code:
import UIKit
class TimerListScreen: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tabelView: UITableView!
var timers = [Timer]()
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
tabelView.delegate = self
tabelView.dataSource = self
let tempTimer = Timer(timerLabel: "temp timer")
timers.append(tempTimer)
}
#IBAction func didUnwindFromNewTimerVC(_sender:UIStoryboardSegue){
guard let newTimerVC = _sender.source as? newTimerVC else{return}
newTimerVC.timer.setTimerLabel(timerLabel: newTimerVC.timerLabel.text!)
timers.append(newTimerVC.timer)
tableView.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tabelView.dequeueReusableCell(withIdentifier: "TimerCell", for: indexPath) as? TimerCell{
let timer = timers[indexPath.row]
cell.updateUI(Timer: timer)
return cell
}else{
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return timers.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 78
}
}
Thank you
Please note the spelling. There are two table view instances: the outlet tabelView and a (pointless) instance tableView.
Reload the data of the outlet
tabelView.reloadData()
and delete the declaration line of the second instance let tableView ....
However I'd recommend to rename the outlet to correctly spelled tableView (you might need to reconnect the outlet in Interface Builder).
And force unwrap the cell
let cell = tabelView.dequeueReusableCell(withIdentifier: "TimerCell", for: indexPath) as! TimerCell
and remove the if - else part. The code must not crash if everything is hooked up correctly in IB.

cellForRowAtIndexPath is not being called from custom class

I'm using Xcode 7.0, Swift 2
I'm basically trying to create a custom class that will build a UITable, then in the ViewController I make a new object of the table and load it into self.view;
The problem I'm having is that the function func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell isn't being called at all from within the custom class. I've been looking for a solution for 3 days now and I've tried rebuilding the App and code several times with no luck.
Please note, if I use the same code (that is everything required to build the table; excluding init functions, etc) in the ViewController.swift file, it works fine.
I know the problem is with the cellForRowAtIndexPath function because it will not print out the statement I set in that block of code when it runs. All other functions are called, but for some reason this isn't being called. Not sure if I overlooked something here. Any help would be appreciated. Thanks in advance.
class sideTest: NSObject, UITableViewDelegate, UITableViewDataSource {
let tesTable: UITableView = UITableView()
var items: [String]?
var mView: UIView = UIView()
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("The number of rows is: \(self.items!.count)")
return self.items!.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("\nLets create some cells.")
let sCell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
sCell.textLabel?.text = self.items![indexPath.row]
sCell.textLabel?.textColor = UIColor.darkTextColor()
return sCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
}
func tblSetup() {
self.tesTable.frame = CGRectMake(0, 0, 320, mView.bounds.height)
self.tesTable.delegate = self
self.tesTable.dataSource = self
self.tesTable.backgroundColor = UIColor.cyanColor()
// load cells
self.tesTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.tesTable.reloadData()
print("Currenlty in tblSetup.\nCurrent rows is: \(self.items!.count)")
}
//Init
override init() {
super.init()
self.items = nil
self.tblSetup()
}
init(sourceView: UIView , itemListAsArrayString: [String]) {
super.init()
self.items = itemListAsArrayString
self.mView = sourceView
self.tblSetup()
}
}
Here is the code from ViewController.swift; Please do note that the table gets built, but the cells do not populate, even if I manually enter cell info by doing: sCell.textLabel?.text = "test cell"
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let myTable: sideTest = sideTest(sourceView: self.view, itemListAsArrayString: ["Cell 1", "Cell 2", "Cell 3"])
self.view.addSubview(myTable.tesTable)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Again, any help is greatly appreciated. Thanks.
Your view controller don't have a strong reference to your sideTest var.
Once your view did load finished,your sideTest is nil.Although you have a tableview(by add subview), but you no longer have a data source.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {}
is called after view did load. That cause the problem.
change your view controller to:
var tb :sideTest?
override func viewDidLoad() {
super.viewDidLoad()
let myTable: sideTest = sideTest(sourceView: self.view, itemListAsArrayString: ["Cell 1", "Cell 2", "Cell 3"])
print(myTable.tesTable.frame)
tb=myTable
self.view.addSubview(myTable.tesTable)
}
change your cellforrowatindexpath to:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("create cells")
var cell :UITableViewCell?
if let sCell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell"){
cell=sCell
}else{
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
cell!.textLabel?.text = self.items![indexPath.row]
cell!.textLabel?.textColor = UIColor.darkTextColor()
return cell!
}
this will fix most of the problems.
Your code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let myTable: sideTest = sideTest(sourceView: self.view, itemListAsArrayString: ["Cell 1", "Cell 2", "Cell 3"])
self.view.addSubview(myTable.tesTable)
}
I would think that the myTable variable goes out of scope and is released when viewDidLoad finishes, so there is no data source or delegate after that. Did you verify that the self.view.addSubview(myTable.tesTable) retains it? Try moving declaration of myTable outside of the function level (to property level) or add a diagnostic print to deinit..

Swift - Array not being populated with values

I am trying to build a list within a table view controller and I have the proper setup, but for some reason my simulator crashes at the line where I set my array. It is not an error, but a Thread issue. I'm still learning the XCode warning system so I'm not sure what that means, but I noticed that in the Thread notifications that cityArray = ([String]) 0 values. Can anyone help?
import UIKit
class ListTableViewController: UITableViewController {
var cityArray: [String] = ["Portland","San Francisco","Cupertino"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cityArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellIdentifier", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = self.cityArray[indexPath.row]
return cell
}
}
UPDATE:
Images of the Thread message:
try this
let cityArray: [NSArray] = ["Portland","San Francisco","Cupertino"] as NSArray
Can you also provide the error you're receiving? Is it an issue with the array being mutated while being enumerated or something?
Also,
var cityArray: [String] = ["Portland","San Francisco","Cupertino"]
can change to
var cityArray = ["Portland", "San Francisco", "Cupertino"]
Since all your objects are of the same type, the Swift's type inference takes care of this for you.

Resources