Table View Cell Stays Highlighted No Matter What - swift

My storyboard-based UITableViewController subclass pushes another view controller into the navigation when the user taps one of the table view cells.
I am using a segue to accomplish this, and not doing it programmatically with pushViewController(:animated:).
The problem I'm having is that my table view cell stays highlighted (light gray) all along, even after popping the view controller (again, with an 'unwind' segue). The only way to clear it is to call tableView.reloadData().
I have tried:
Setting:
self.clearsSelectionOnViewWillAppear = true
in viewDidLoad() (Should not be necessary, since it's already the default: The template code has self.clearsSelectionOnViewWillAppear = false commented out, and suggests to uncomment it to override the default functionality).
Also:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let indexPath = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: indexPath, animated: true)
}
}
...to no avail.
EDIT: Turns out viewDidAppear() is not called on navigation pop, only after being added to the view hierarchy (e.g., addSubview()). Placing this code in viewWillAppear() does the trick. But my question at the end of this post still stands.
I have also tried implementing this method:
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
...but it is not being called (perhaps because of the segue?)
The only thing that successfully unhighlights the cell is calling:
tableView.deselectRow(at: indexPath, animated: true)
...from within prepareForSegue(). But that happens on push, and I want to de-highlight my cell on pop.
Do I need to setup a dedicated unwind method for the push (cell tap) segue, and unhighlight there?
Why doesn't it work out-of-the-box, even though I am subclassing UITableViewController?

Like what you mention, using tableView.deselectRow(at: indexPath, animated: true)
However, instead of having it in:
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
You should be putting it in:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// You other cell selected functions here ...
// then add the below at the end of it.
tableView.deselectRow(at: indexPath, animated: true)
}
didDeselectRowAt will not be called until a row is deselected. So in order to de-select the cell, you should be adding it in the didSelectRow. This way whenever a cell is selected and didSelectRow get trigger, you will run your other code and also de-select the cell at the end of it.

The third option should work, I think you just accidentally autocompleted with didDeselectRow, not didSelectRow.

Related

UITableViewCell missing deletion control after successive left-swipes

I use a tableview with editable custom style cells. There's an Edit button in the navigation bar which toggles editing mode. I need the editing mode to delete and reorder rows.
When I left-swipe a cell, the Delete action button shows on the right side of that cell. While it is displayed, I press the Edit button and the Delete action button disappears. Pressing the Edit button again shows the deletion command left and the reordering controls right of ALL(!) cells. So far all as expected.
Now (as an example), when I left-swipe cell 0, then left-swipe cell 1 while Delete action button in cell 0 is still visible, activating editing mode with Edit button now shows deletion command left and reordering control right of all cells, EXCEPT(!) cell 0.
I found out that as soon as 2 or more cells are left-wiped, the first cell which is not right-swiped back out of editing mode, misses the deletion control (left) when editing mode for all cells is enabled using Edit button. Even more weird ... on subject cell missing deletion control (left), the reordering control (right) shows correctly!
I followed and compared many tutorials but didn't catch the error.
What do I miss?
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
...
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
...
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
...
}
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
...
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
...
}
// not necessary = didn't change anything, but found in many tutorials
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
}
#IBAction func editAction(_ sender: Any) { // that's the Edit button action
self.setEditing(!isEditing, animated: true)
self.navigationItem.leftBarButtonItem?.title = isEditing ? "Done" : "Edit"
}
Above all involved methods. Irrelevant method content is omitted to keep the post compact. I will add/edit if deemed required.
View after left-swipe of cell 1 (disregard Rename and Share actions).
View after left-swipe of cell 2, then cell 1, then entering editing mode using Edit button.
I found the reason for the misbehaviour. Since I use animation in the setEditing() method, I need to to tell the view that there are updates using beginUpdates() and endUpdates(). That did the job! No badly displayed cells anymore neither simulator, nor device!
#IBAction func editAction(_ sender: Any) {
tableView.beginUpdates()
self.setEditing(!isEditing, animated: true)
tableView.endUpdates()
self.navigationItem.leftBarButtonItem?.title = isEditing ? "Done" : "Edit"
}
EDIT 1
Above (beginUpdates() and endUpdates()) works as desired, but below (performBatchUpdates {}) is more modern (iOS 11.0) and should be preferred.
#IBAction func editAction(_ sender: Any) {
tableView.performBatchUpdates {
self.setEditing(!isEditing, animated: true)
}
self.navigationItem.leftBarButtonItem?.title = isEditing ? "Done" : "Edit"
}
EDIT 2
Meanwhile I believe that the OP misbehaviour is an iOS bug.
By chance, I found that the Safari Bookmarks tableview behaves the same way. Left-swipe cell 0, then left-swipe cell 1, next press Done/Edit button (lower right corner) twice. You can observe that cell 0 doesn't show the deletion control icon (one-way-sign) left side of cell 0. Consequently I believe this is a iOS bug.
Besides this, other apps produce the same misbehaviour on editing tableviews.
Hi I believe your problem can be fixed by using UITableView's preset editButtonItem.
navigationItem.leftBarButtonItem = self.editButtonItem
this sets state of the button into edit mode when a swipe action is being performed, therefore should fix the issue you are having.

awakeFromNimb doesn't get called on TableView reloadData Swift

I have an iPad TabBar navigation app and in OrderViewController I have a tableview on left side and outlets on the right side of the view as in a sort of split view controller. When you click a tableView's cell it shows details on the other half screen. As I'm introducing two different color schemes for A/B testing, I use a switch to perform the change in color. The color switching dough doesn't succeed with tableView's cells. Adding a reloadData() in viewWillAppeardidn't solve the problem.
Everything gets updated except for the cells, they maintain the colors of when they got created the first time.
I added prints throughout the phases and I detected that cell's awakeFromNimbgets called only the first time. How do I get it to be called on tableView.reloadData()? As always many thanks.
awakeFromNib():
override func awakeFromNib() {
super.awakeFromNib()
configureUi()
}
viewWillAppear():
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.configureFetchedResultsController()
orderTableView.delegate = self
orderTableView.dataSource = self
configureUI()
orderTableView.reloadData()
}
Use the tableView delegate willDisplayCell
https://developer.apple.com/documentation/uikit/uitableviewdelegate/1614883-tableview
class MyCustomVC: UITableViewDelegate {
override func tableView(_ tableView: UITableView,
willDisplay cell: UITableViewCell,
forRowAt indexPath: IndexPath) {
guard var cell = tableView.dequeueReusableCell(withIdentifier: "yourReuseIdentifier", for: indexPath) as? YourCustomCell else {
return UITableViewCell()
}
cell.configureUi()
}
}

Swift 3 : Tableview select row goes to different navigation views

Trying to create a simple app with Swift 3, using Xcode 8.2. Would like to have a hierarchy as follows:
Tab-bar View
-->TableView
----->TableView
--------->View
--------->TableView
--------->etc.
----->TableView
----->View
-->TableView
For example, main tab bar should have a tableView controller embedded inside a navigation controller. Clicking on a certain cell (index.path) should lead to a separate tableview or viewcontroller that itself can branch off into its own series of 'child' tables. The tables should be dynamic, and all have navigation control that leads back 'pop' to the most previous table/view controller.
Currently, for the main tableview, I have:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == List_Database_Indexes.employees {
let myWebView = self.storyboard!.instantiateViewController(withIdentifier: "Employees") as! UITableViewController
//self.present(myWebView, animated: true, completion: nil)
self.performSegue(withIdentifier: "Employees", sender: nil)
}
This works to bring up the Employees UITableViewController but does not have the navigationbar controller at the top. For example, from apple, if you click on in the image below 'General' row, you are lead to a navigation tableview that takes you to Passlock code, which takes you to Auto-lock, all with navigation.
Change your code to:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == List_Database_Indexes.employees {
let myWebView = self.storyboard!.instantiateViewController(withIdentifier: "Employees") as! UITableViewController
//self.present(myWebView, animated: true, completion: nil)
//self.performSegue(withIdentifier: "Employees", sender: nil)
self.navigationController?.pushViewController(myWebView, animated: true)
}
}
This method is delegate method. so it will apply on all of the cell for table because we have bind delegate to tableview. cell is subview of table view.
you have to give condition for wether the cell is selected or not..
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == //index of that cell {
let Employee = self.storyboard!.instantiateViewController(withIdentifier: "Employees") as! UITableViewController
self.navigationController?.pushViewController(Employee, animated: true)
}
}

Swift leave a row selected after changing viewcontroller

i have a two view application. If i select a row a segue load the second view Controller. I want that when i return back to my first controller the row is is still selected. I have tried with this:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)!
self.tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: UITableViewScrollPosition.None)
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
but when returning back the row not remain selected. What's is the problem??
P.S.: I want no multiple selection, only one row at time.
If you are using a UITableViewController subclass as your source view controller, what I presume because you are overriding the table view delegate methods, you can use the flag clearsSelectionOnViewWillAppear on your view controller to get this behavior.
override func viewDidLoad() {
self.clearsSelectionOnViewWillAppear = false
}
Or you can set this in your Storyboard as well, when selecting the attributes selector of your source table view controller, you have to untick the Clear on Appearance checkbox.

Why can't I select TableView cells?

This is the flow of my app:
First, the TableView is set to hidden. There is a UITextField in the center of the screen. When the user types something and hits Go, this code is run:
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.textFieldConstraint.constant = -230
self.tableView.hidden = false
self.goButton.hidden = true
self.view.layoutIfNeeded()
}, completion: nil)
At this point, the tableview is populated. When a row is selected, I need to manipulate the data that is populating it.
However, absolutely nothing happens when I tap a cell.
What am I doing wrong?
My TableView code is here:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: SearchResultsTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! SearchResultsTableViewCell
cell.label.text = searchResultsNames[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResultsUrls.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("HELLO")
}
And, I have set the dataSource and delegate properly.
I also want to clarify that the tableView populates and scrolls properly; it just won't do anything when I tap a cell.
Update:
I've discovered that for some reason, I can select the cells when I press and hold them. It is not what I want, so does anybody know how to fix this?
I have just used your code to create a simple table, selection is working fine and logging out HELLO as expected. Can you check the values of Selection in the attributes inspector? Here is mine, which has Selection set to Single Selection.
And here is the code I used for my simple table
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var searchResults = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
searchResults.append("Testing 1")
searchResults.append("Testing 2")
searchResults.append("Testing 3")
searchResults.append("Testing 4")
searchResults.append("Testing 5")
tableView.dataSource = self
tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath)
cell.textLabel?.text = searchResults[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("HELLO")
}
}
I also tried hiding and showing the tableView which made no difference on selection.
EDIT & SOLUTION:
In the comments below, we discovered that the issue is related to a tapGestureRecogniser on the view, This was identified by the op only being able to make a selection by holding a tap on the cell. The gesture has to fail before the selection can be made, the op managed to solve the problem by referring to this other SO Answer
if you use "tap" gesture, you can't select table cell. (but, if you click and drag to right a cell, you can select it.)
Check gesture first.
And if your code has self.tableView.allowsSelection = false, replace false to true or delete this line.
My problem has been caused by the tap gesture recognizer on the view controller itself (I had BaseTableViewController I was extending from). Probably it was interfering with the gesture recognizers of UITableView.
In your viewDidLoad, or wherever you set up your view, make certain that your table view even allows selections. That can be controlled with the allowSelection property.
Something like:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.allowsSelection = true
}
If you have a gesture recognizer, just type gestureRecognizer.cancelsTouchesInView = false
For me I was implementing another did select row method, so I erased it and typed "didSelect..." and selected the first one in the suggested methods, which is this for swift 3:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Row #: \(indexPath)")
}
Try disabling and then enabling the user interaction Enabled property in the attribute inspector of your tableView
Looks something like this
I met the same problem as you and solved it by removing the below code
self.view.layoutIfNeeded()
you can try it.