I have a table view controller that needs to be updated through a delegate call. I have set the datasource and delegate and on initial load of the tableview, all works as expected. I have a delegate method that gets called after a datasource update. The delegate calls a refresh method in the table view controller class which calls .reloadData()
When reloadData is called, numberOfRowsInSection is called and accurately returns the number of rows, however cellForRowAtIndexPath never gets called.
In this particular case, numberOfRowsInSection returns 2, therefore cellForRowAtIndexPath should be called twice but it's called zero times.
On initial load everything is fine. It's only when reloadData is called taht cellForRowAtIndexPath is ignored. I have done this same thing many times in Obj-C without any weirdness. Are there any known issues with this in Swift?
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(LayerMenuCell.reuseId) as! LayerMenuCell
// ....
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(layerEntries?.count)
return (layerEntries?.count)!
}
func refresh() {
self.layersTableView.reloadData()
}
Thanks!
Try setting the delegate and dataSource of your UITableView:
myTable.delegate = self
myTable.dataSource = self
As specified in the Documentation:
A functioning table view requires three table view data source
methods.
func numberOfSectionsInTableView(tableView: UITableView) -> Int
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
MAake sure you implement the above three delegate methods and they return some values other than nil or 0.
There is a chance that cell height could be 0/ table height is 0 in both the cases cell for row method will not get called.
Also make sure you set the delegate properly and call the reloadData method on main thread. More on here
Things you need to check when Tableview is not working as expected:
1. Setting the delegate and datasource through storyboard or by code.
self.tableView.delegate = self
self.tableView.dataSource = self
2.check if tableView in storyboard is connected to tableView outlet.
3.check numberOfRowsInSection and numberOfSectionsInTableView returning the correct values.
4.check if the methods is written properly.
5.add the delegate and datasource after UIViewController.
<UITableViewDelegate , UITableViewDataSource>
this will help you if you are missing any thing.
Related
I have a tableview and I have 20 prototype cells and tableview's Content parameter is Dynamic Prototypes
At a time I show only one protoype cell but for some weird reason cellForRowAtIndexPath is getting called twice.
I have gone through my code and I call reloaddata just once.
I couldn't figure out what could have caused tableview to call cellForRowAtIndexPath twice !
so i generally would like to know what and all could cause tableview to call cellForRowAtIndexPath more than once for the same row
Update:
In one of the prototype cells if a button is clicked then probably I would reload the data so that I can show some other prototype cell but then also its called twice
but still number of rows count is one
If you call reloadData() explicitly yourself, that would explain it. The tableView loads data by default in UITableViewController, so explicitly calling it again would trigger cellForRowAt the second time. You can easily confirm this by removing an explicit call to reloadData() - then the tableView should look correctly and the cellForRowAt should be called only once.
I tested a following minimal working example in Playgrounds, and it got called only once:
import UIKit
import PlaygroundSupport
class A: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Reload", style: .plain, target: self, action: #selector(buttonPressed))
}
#objc func buttonPressed() {
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(">>>> calling numberOfRowsInSection")
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(">>>> \(indexPath)")
return UITableViewCell()
}
}
PlaygroundPage.current.liveView = UINavigationController(rootViewController: A())
So unless you do something else, the cellForRowAt should be called really just once per reloadData().
By default there is a call when view controller is initialized to reload the tableView when you add another one then it's 2
I'm starting to work with UITableViews and can't seem to find out how to change the position of a cell with code. Changing the position in the storyboard is straightforward enough but I need to be able to do it in swift.
TLDR;
Update your data. i.e. swap(&arr[2], &arr[3]).
Call the tableView's reloadData() method to reflect the changes to your data.
Long answer
An instance of UITableView works by checking its data source (UITableViewDataSource) for the information it needs. This includes the number of sections and rows, as well as the instance of UITableViewCell that the table view is to use. These are defined by the following UITableViewDataSource delegate methods:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int;
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int;
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell;
Usually, you would base the former two on some data you have, likely an Array or similar container. For example, if your tableView displayed data from an Array named fruitArray (which contained names of different fruit - a list of strings), then you might have something like the following:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Our array is one dimensional, so only need one section.
// If you have an array of arrays for example, you could set this using the number of elements of your child arrays
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Number of fruits in our array
return fruitArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("yourCellId") // Set this in Interface Builder
cell.textLabel?.text = fruitArray[indexPath.row]
return cell
}
Then, you can see that the answer to your question becomes simple! Since the contents of a given cell are based upon fruitArray, all you need to do is update your array. But how do you get the tableView to "recheck" its dataSource? Well, you use the reloadData method, like so:
swap(&fruitArray[2], &fruitArray[3])
tableView.reloadData()
This then triggers the tableView to "recheck" its dataSource, hence causing your data swap to appear on the screen!
If you'd like the user to be able to swap the positions of the cells, you can use the following UITableViewDelegate (not UITableViewDataSource) delegate method:
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool
Have a look at this article for more info. You can also view Apple's documentation on UITableView, UITableViewDataSource, and UITableViewDelegate for further detail.
Hope this helps!
Using UITableView with one Prototype cell inside a ContainerView. The table will need to provide cells so that at run time, the code will add different views in different cells for user information and interaction.
The connection is made between the tableView and the containerView.swift to be its dataSource and delegate.
In the containerView.swift, the UIViewController has been extended.
extension myContainerView: UITableViewDataSource, UITableViewDelegate {...}
And in that block of code, there are required func which return numberOfRowsInSection and cellForRowAtIndexPath
But these infos are available at run time and thus my code is not complying now, I get this following error when return parent.mainTbRowCount() is reached, and if I return just a dummy int for now, I get the same error when let view = parent.cellsViewsDesider.views![indexPath.row] is reached.
fatal error: unexpectedly found nil while unwrapping an Optional value
extension myContainer: UITableViewDataSource, UITableViewDelegate {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return parent.mainTbRowCount() // <------ fatal error
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("topCellId", forIndexPath: indexPath)
let view = parent.cellsViewsDesider.views![indexPath.row] // <---- fatal error
cell.contentView.addSubview(view!)
return cell
}
If I delete the connection which I made between the tableView and the containerView.swift to be its dataSource and delegate, the code compiles but the functions for numberOfRowsInSection and cellForRowAtIndexPath never get called.
What needs to be done in this case?
Thank you
I'm trying to call cellForRowAtIndexPath from within heightForRowAtIndexPath in order to assign a height based on the cell's type (I'm subclassing UITableViewCell). Trivial, right? Well, calling it there causes a loop. I can't quite seem to figure out why that would be. Placing breakpoints in both methods doesn't yield anything—the delegate method cellForRowAtIndexPath never actually gets called. Take a look:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
return SubclassCellTypeOne()
default:
return SubclassCellTypeTwo()
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// Calling cellForRowAtIndexPath here causes a loop
let cell = tableView.cellForRowAtIndexPath(indexPath)!
if cell is SubclassCellTypeOne {
return UITableViewAutomaticDimension
} else {
return 100
}
}
Any idea why that's happening? And any suggestions on how to get around it? Thanks!
When a reference to a cell is made via a UITableView, (usually by iOS, when loading your view), iOS calls the methods in its lifecycle - e.g., heightForRowAtIndexPath, editingStyleForRowAtIndexPath to work out how to display it etc.
So your source of an infinite loop is that you make a reference to a cell, inside a method that is called when a reference to a cell is made ;)
To fix this, you should reference back to your data source, instead of asking the cell directly about itself. If you have a class set up as a data collection, this is easy.
Yep, you shouldn't call cellForRow inside heightForRow.
In heightForRow you have the indexPath variable. You can use indexPath.row to determine the class of the cell inside heightForRow, just like you do in cellForRow.
You could also have forgotten to set the delegate and datasource properties of the tableview. Or you are returning 0 from numberOfRowsInTable...
That could also be why you are not hitting the breakpoint inside cellForRow.
I have a UITableView placed on a view controller in storyboard. I set the datasource and delegate for the table view to self and I specified the UITableViewDelegate and UITableViewDatasource in the view controller class declaration. All of the datasource functions work properly. In other words, when cellForRowAtIndexPath is called, it executes fine.
The problem is that the delegate methods for the tableView are not executing. I verified this by placing a breakpoint at the beginning of
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
I also noticed that the keyword optional is specified to be prefixed for the above delegate method as such:
optional func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
However, if I add the optional keyword to the beginning of the function, I get the error optional can only be applied to protocol members.
I have no idea why my heightForRowAtIndexPath is not getting called. Can anyone help me on this? Thanks in advance.