The UITableViewDelegate has a convenient function, willDisplay.
I need to add a handler for when a cell hides, which is essentially the opposite of willDisplay. How can I detect this event?
The method you'll want to use is
func tableView(_ tableView: UITableView,
didEndDisplaying cell: UITableViewCell,
forRowAt indexPath: IndexPath)
Documentation
Related
In my project I have a UITableView displaying custom cells of a fixed height.
When I segue to the ViewController, I want to run an animation in the tableViewCells. when i scroll down, and then back to the first cell I do not want to run the animation again.
To achieve this I am using tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath). I then use an array to remember which cells have been on the screen already.
My function looks like this:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let myCell = cell as! myTableViewCell
if !alreadyShown.contains(indexPath) {
alreadyShown.append(indexPath)
myCell.animate()
} else {
myCell.dontAnimate()
}
}
However, this seems to ignore the height of my cell. Therefore it runs for every indexPath that would have been displayed for standard cells (e.g. 17 times on an iPhone X), even though 14 of those 17 cells are not visible.
When i scroll down later, the animation is already finished for those 14 next cells.
Is there any way of telling the willDisplay cell function the cell height so that it knows which cells are actually going to be displayed? Or is there any other way of keeping score of which cells have actually been displayed already?
The solution was setting the estimatedRowHeight for the tableView to a number equal to or greater than the actual hight of the cells in viewDidLoad. This made sure that tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) is only called on the actually visible cells.
I have a ViewController that contains a collectionview and a tableview. I would like to be able to press a row in my tableView and update the collectionView with new images. I'm not sure how I can reference the collectionView in this scenario.
I'm assuming I would have to put some code in my "DidSelectRowAt" method in my tableView, and pass data using dictionaries. Anyone have any idea? thanks!
Try this method and pass your latest dictionary to collectionView Dictionary
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
yourCollectionViewDict = arrDict[indexPath.row]
collectionView.reloadData()
}
Have you tried triggering collectionView.reloadData() from your tableView's didSelectRowAtIndexPath?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
collectionView.reloadData()
}
I want to change the state of a cell in a TableView. I tried setting the state in "cellForRowAtIndexPath" method, but it did not work. Then I saw posts that said that in order to change the state of a cell one needs to use the delegate method "willDisplayCell". However, when I implemented the method it is not being called (I put a breakpoint which was not called)
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
cell.selected = true
}
I tried also using "willDisplay" as it appears in Apple documentation, but that did not help. As a matter of fact, I tried any garbage instead of that string and nothing happens - the app compiles with no errors but the method is not being called.
I know that the delegate of the tableView is working because other delegate methods, such as "didSelectRowAtIndexPath", are being called.
Swift 3.0 syntax for willDisplayCell is now:
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
}
I get this error message: failed to obtain a cell from its dataSource
The strange that
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
not even get called, problem is not that returned cell is nil. I put in breakpoints. cellForRowAtnot get called.
numberOfRowsInSection returns value.
I do not know it is relating or not, but now I do not use UITableViewController, I just have a UIViewController, and it is the datasource and delegate for the UITableView. I hope it can not cause interfere. Any idea?
Cross check below check list:-
it's because you are failing to dequeue a reusable cell.
The problem is that your cellForRowAtIndexPath function is embedded in another function
when you forgot to add the UITableViewDataSource and UITableViewDelegate protocols to the ViewController declaration.
class DataViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
I guess this is your problem - Failed to obtain a cell from its DataSource
you can take a look at: https://www.hackingwithswift.com/example-code/uikit/fixing-failed-to-obtain-a-cell-from-its-datasource
try something like this:
cell = tableView.dequeueReusableCellWithIdentifier(getCellIdentifierForType(cellType))
setDataToCell(cell, item: item)
good luck
Please make sure when you are migrating your code from swift older to new versions then your tableview data source and delegates have updated syntax.
For example
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
and
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
Can not auto updated to below:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
I have some questions with Swift 3 function calling. Below is an example.
Old Swift:
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell
Swift 3:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
That's fine with the above syntax. But now Xcode shows me an error and asks me to do like below:
#objc(tableView:cellForRowAtIndexPath:) func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
I do not understand why I have to declare #objc(tableView:cellForRowAtIndexPath:).
This is happening only when I am trying to implement table view datasource methods under an extension. Also this is not happening for numberOfRowsInSection or viewForHeaderInSection.
Can anyone help me to understand why this is happening?
While I am not sure what triggers the #objc, I can suggest the following approach:
Store the tableView variable somewhere in the viewDidLoad:
let tv = tableView!
Then hover over the tableView variable and press the command button in conjunction with a click.
This should take you to the interface of a UITableView.
Then, hover over either UITableViewDelegate or UITableViewDataSource and press the command button in conjunction with a click.
Now you can see the new signatures.
A lot has changed...Happy upgrade!
Swift compiler forced to write Objc(funcName) before function if you are using this function from Objective c. According to app doc
Use the #objc(name) attribute to provide Objective-C names for
properties and methods when necessary. For example, you can mark a
property called enabled to have a getter named isEnabled in
Objective-C like this:
var enabled: Bool {
#objc(isEnabled) get {
// ...
}
}
To void this, use extension to write TableView Datasource and delegate
extension YourViewControllerName:UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell() as SplitAddContactCell
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80.0
}
}