UITableViewCell is not canceled on quick access

Now I have updated my three applications to iOS 7, but in all three cases, despite the fact that they do not use any code, I have a problem when the user navigates to return to the navigation controller (instead) quickly. the cell will remain in the selected state.

Three applications use custom cells created programmatically, while the other uses custom cells created in the storyboard, while the third uses the default cells in the very basic UITableView subclass, also in the storyboard. In all three cases, the cells do not separate themselves. If the user skips slowly or accesses the "Back" button, they cancel the selection as usual.

This only happens in my iOS 7 apps, my own Apple apps and third-party apps updated for iOS 7 all seem to behave normally (albeit with slight differences in how quickly the cells turn off).

I must be doing something wrong, but I'm not sure what?

+57
ios uitableview ios7
Oct. 15 '13 at 10:57
source share
15 answers

I am dealing with the same problem right now. UICatalog - Apple's example seems to bring a dirty solution.

It really does not make me happy. As mentioned earlier, it uses [self.tableView deselectRowAtIndexPath:tableSelection animated:NO]; to cancel the selected current line.

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // this UIViewController is about to re-appear, make sure we remove the current selection in our table view NSIndexPath *tableSelection = [self.tableView indexPathForSelectedRow]; [self.tableView deselectRowAtIndexPath:tableSelection animated:NO]; // some over view controller could have changed our nav bar tint color, so reset it here self.navigationController.navigationBar.tintColor = [UIColor darkGrayColor]; } 

I should mention that the sample code may not be iOS 7 iOS 8 iOS 9 iOS 10-ready




Something that really confuses me is the Link to the UITableViewController class :

When the table view is displayed on first boot, the table view controller reloads the table view data. It also clears its selection (with or without animation, depending on the request) each time a table view is displayed. UITableViewController class implements this in the superclass method viewWillAppear: You can disable this behavior by changing the value in clearsSelectionOnViewWillAppear .

This is exactly the behavior I expect ... but it doesn't seem to work. Neither for you nor for me. We really need to use a dirty solution and do it ourselves.

+35
Nov 10 '13 at 11:52
source share
— -

This worked better for me:

 - (void)viewDidLoad { [super viewDidLoad]; self.clearsSelectionOnViewWillAppear = NO; } -(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated]; } 

I even got a much better deburring when I slowly came back.

+51
Nov 25 '13 at 17:21
source share

Fabio's answer works well, but does not give the right look if the user goes through a bit and then changes his mind. To qualify for this case, you need to save the selected pointer path and reset if necessary.

 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.savedSelectedIndexPath = nil; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.savedSelectedIndexPath) { [self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.savedSelectedIndexPath = self.tableView.indexPathForSelectedRow; if (self.savedSelectedIndexPath) { [self.tableView deselectRowAtIndexPath:self.savedSelectedIndexPath animated:YES]; } } 

If you are using the UITableViewController, be sure to disable the built-in cleanup:

 self.clearsSelectionOnViewWillAppear = NO; 

and add property for savedSelectedIndexPath:

 @property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath; 

If you need to do this in several different classes, it may make sense to separate it in an assistant, for example, as in this context: https://gist.github.com/rhult/46ee6c4e8a862a8e66d4

+19
Sep 13 '14 at 7:30
source share

This solution animates the row selection along with the transition coordinator (to deviate from the user-managed VC) and reapplies the selection if the user cancels the transition. Adapted from Caleb Davenport solution in Swift. It is checked only on iOS 9. Both the work with the transition with the help of the user (swipe) and with the click of the back button of the old style were tested.

In a subclass of UITableViewController :

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // Workaround. clearsSelectionOnViewWillAppear is unreliable for user-driven (swipe) VC dismiss NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow; if (indexPath && self.transitionCoordinator) { [self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { [self.tableView deselectRowAtIndexPath:indexPath animated:animated]; } completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { if ([context isCancelled]) { [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } }]; } } 
+9
Dec 04 '15 at 16:11
source share

After I started this today, I found out that this is apparently a fairly well-known problem with the UITableView, its support for interactive navigation navigation is slightly broken. The people behind Castro posted an excellent analysis and solution for this: http://blog.supertop.co/post/80781694515/viewmightappear

I decided to use their solution, which also considers canceled transitions:

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; NSIndexPath *selectedRowIndexPath = [self.tableView indexPathForSelectedRow]; if (selectedRowIndexPath) { [self.tableView deselectRowAtIndexPath:selectedRowIndexPath animated:YES]; [[self transitionCoordinator] notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) { if ([context isCancelled]) { [self.tableView selectRowAtIndexPath:selectedRowIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } }]; } } 
+7
Jan 25 '16 at 10:10
source share

You can try to install

 self.clearsSelectionOnViewWillAppear = YES; 

in a UITableViewController or

 [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:NO]; 

in viewWillAppear, possibly before calling [super viewWillAppear: animated]; If your UItableView is not inside the UITableViewController, you must deselect the cell manually:

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; } 
+5
Oct 15 '13 at 11:14
source share

I use

 [tableView deselectRowAtIndexPath:indexPath animated:YES]; 

at the end of the method

 (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 

Like this:

 (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { //doing something according to selected cell... [tableView deselectRowAtIndexPath:indexPath animated:YES]; } 
+2
Mar 27 '14 at 12:50
source share

Swift 3 simple answer:

 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if tableView.indexPathForSelectedRow != nil { self.tableView.deselectRow(at: tableView.indexPathForSelectedRow! as IndexPath, animated: true) } } 
+2
Nov 06 '16 at 18:06
source share

Use

[tableView deselectRowAtIndexPath:indexPath animated:YES];

code in

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method

+1
Oct. 15 '13 at 11:18
source share

I found a very simple solution to this problem that just makes the default behavior work as it should. I was not satisfied with the solutions with deselectRowAtIndexPath , as the resulting visual effect was slightly different.

All you have to do to prevent this strange behavior is to reload the table when displaying the view:

 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self.tableView reloadData]; } 
+1
Dec 20 '14 at 0:39
source share

Codestage answer , in Swift 3. notifyWhenInteractionEnds deprecated.

 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(true) if let indexPath = self.tableView.indexPathForSelectedRow { self.tableView.deselectRow(at: indexPath, animated: true) self.transitionCoordinator?.notifyWhenInteractionChanges { (context) in if context.isCancelled { self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none) } } } } 
+1
Sep 22 '16 at 9:06
source share

Based on the Rhult code , I made a few changes.

This implementation allows the user to undo napkins back and still save the ones selected for later scrolling back to deselect the animation

 @property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath; - (void)viewDidLoad { [super viewDidLoad]; self.clearsSelectionOnViewWillAppear = NO; } -(void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.savedSelectedIndexPath = nil; } -(void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.savedSelectedIndexPath && ![self.tableView indexPathForSelectedRow]) { [self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } else { self.savedSelectedIndexPath = [self.tableView indexPathForSelectedRow]; } } 
0
Mar 19 '15 at 3:07
source share

Rhult's solution works great on iOS 9.2. This is the implementation in Swift:

Declare a variable in MasterViewController to save IndexPath:

 var savedSelectedIndexPath: NSIndexPath? 

You can then put the code in the extension for clarity:

 extension MasterViewController { override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) self.savedSelectedIndexPath = nil } override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) if let indexPath = self.savedSelectedIndexPath { self.tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None) } } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) self.savedSelectedIndexPath = tableView.indexPathForSelectedRow if let indexPath = self.savedSelectedIndexPath { self.tableView.deselectRowAtIndexPath(indexPath, animated: true) } } } 
0
Dec 29 '15 at 10:14
source share

Codestage provided the best answer , so I decided to convert it to Swift 2.

 override func viewWillAppear(animated: Bool) { super.viewWillAppear(true) let selectedRowIndexPath = self.tableView.indexPathForSelectedRow if ((selectedRowIndexPath) != nil) { self.tableView.deselectRowAtIndexPath(selectedRowIndexPath!, animated: true) self.transitionCoordinator()?.notifyWhenInteractionEndsUsingBlock({ context in if (context.isCancelled()) { self.tableView.selectRowAtIndexPath(selectedRowIndexPath, animated: false, scrollPosition: UITableViewScrollPosition.None) } }) } } 
0
May 17 '16 at 9:19
source share

For quick

 override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) guard let indexPath = tableView.indexPathForSelectedRow else{ return } tableView.deselectRowAtIndexPath(indexPath, animated: true) } 
0
Sep 01 '16 at 7:06 on
source share



All Articles