Selected UItableViewCell staying blue when selected - iphone

When I push a view after a user has selected a UITableView row, the row gets a blue highlight, and then the new view appears. That's fine. But when I go 'back' the row is still highlighted in blue. Here's my didSelectRowAtIndexPath code.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SettingsViewController *controller = [[SettingsViewController alloc] initWithNibName:#"SettingsView" bundle:nil];
[[self navigationController] pushViewController:controller animated:YES];
[controller release], controller = nil;
}
What am I doing wrong?

As the answers above point out, you need to explicitly deselect the row. You have two options as to how you do this. The first, is to deselect the row immediately after selection:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
...
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
That will work just fine, but there is an alternative, and its the approach taken by UITableViewController which is to leave the row selected then deselect it when the view re-appears (after the controller you're pushing is popped off of the stack).
This has the slight advantage that the user sees a glimpse of their previous selection when they return so they can see what they had selected previously.
To implement this, you just need to override viewWillAppear:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
As I said, this is what the default of implementation of UITableViewController's viewWillAppear: does so if you are using UITableViewController and not seeing this behaviour, you should check that you are calling the super implementation in your class' own viewDidAppear:.
Update (30 Oct 2013): well, this is a popular answer! As Ben rightly points out in the comments, UITableViewController actually does this in viewWillAppear: not viewDidAppear: - this is the correct timing. In addition, you turn this behaviour on and off using the clearsSelectionOnViewWillAppear property of UITableViewController. I've amended my answer above to reflect this.

UITableViewController has a BOOL property called clearsSelectionOnViewWillAppear which does exactly what you want.
By default it is set to YES, but I've noticed that you can (sometimes accidentally) disable this property by implementing your own viewWillAppear: method. I think this is because the deselection happens during [UITableViewController viewWillAppear:] which might never get called if you override it.
The solution is easy, then. Just call super's version of viewWillAppear: somewhere in your version of that method:
- (void)viewWillAppear:(BOOL)animated {
// Your custom code.
[super viewWillAppear:animated];
}
Apple recommends always calling the super version of any of the view{Did,Will}{A,Disa}ppear methods if you override them.
References
UITableViewController Class Reference (for clearsSelectionOnViewWillAppear)
UIViewController Class Reference (for viewWillAppear:)

You need to deselect it:
[tableView deselectRowAtIndexPath:indexPath animated:YES];

If your controller is based on UITableViewController, you get this feature for free. But I often ended up using UIViewController with no. of other controls in addition to UITableView, in that case, you should override your viewWillAppear to
-(void) viewWillAppear:(BOOL)animated{
// Unselect the selected row if any
NSIndexPath* selection = [devListTableview indexPathForSelectedRow];
if (selection){
[tableview deselectRowAtIndexPath:selection animated:YES];
}
}

You just need to call [tableView deselectRowAtIndexPath: indexPath animated: YES].

The default behaviour of UITableViewController deselects the row when the user returns from the detail view. The response given by Luke is fine, but I want to point out the reason for it:
1- If you have your UITableViewController like it was when you created it from scratch, you will have all the default behaviours.
2- If in the situation 1 you add a -viewWillAppear or -viewDidAppear, then you will be overwriting the standard behaviour. Them, if you want that the row deselects on return, you must say the super explicitly that you'd like it to deselect the row by himself as it always did! To achieve this, as Luke says, you must call [super viewWillAppear:animated] or [super viewDidAppear:animated] like this:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Here goes all of your stuff
}

This solution is for swift
To solve this problem just add tableView.deselectRow(at: indexPath, animated: false) in didSelectRowAt indexPath function .
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
Watch this 1 minute video tutorial to visualize .

Another solution is to call UITableView's reloadData in viewWillAppear

Related

UITableView in a UIViewController linked to another ViewController (DetailController)

I have an app on the store that uses a RootViewController linking to a UIViewController (DetailController) and I am working on a new app which basically requires the need for this same feature. But instead, my new app has a UITableView inside a UIViewController linked to a UIViewController. So I thought, i'd copy and paste my RootViewController code into this new UIViewController. So i've linked up the TableView, set delegate and datasource to self and the TableView shows the titles of the items (Hurrah) but when touched, doesn't go to the DetailController? I've used NSLog to determine what part isn't working and of course its the didSelectRowAtIndexPath method… and here is my code
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *theItem = [items objectAtIndex:indexPath.row];
DetailController *nextController = [[DetailController alloc] initWithItem:theItem];
[self.navigationController pushViewController:nextController animated:YES];
[nextController release];
}
The TableViewCell just highlights blue and doesn't link to DetailController.
Thanks in advance!
By "not working" do mean it's not being called, or that it's not pushing the view controller? If the first, then make sure the table view delegate is set correctly. If the second, make sure both nextController and self.navigationController are not nil.

didSelectRowAtIndexPath: not being called

I have a UITableView as a subview of my UIScrollVIew, which is the main view controlled by my MainViewController.
In MainViewController.h
#interface MainViewController : UIViewController <UIGestureRecognizerDelegate, UITableViewDelegate, UITableViewDataSource>
// other stuff here...
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
In MainViewController.m
#synthesize myTableView;
// other stuff here...
- (void)viewDidLoad {
myTableView.delegate = self;
myTableView.datasource = self;
}
// other stuff here...
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"listAttributesSegue" sender:self];
}
I know that didSelectRowAtIndexPath is not being called because I have set breakpoints on both the method itself and the line of code inside it, and neither is being called. I also know that the datasource is working correctly because I have other functions which modify the cells at runtime and they are working perfectly fine. I am using the latest Xcode with iOS 5.0 set as the development target. I have searched and searched for an answer. Anyone have any ideas?
Edit:
I have found the answer. I had a UITapGestureRecognizer set for myTableView's superView. This overrode the selection call. Credit to whoever suggested that that might be it. Your answer was deleted before I could mark it correct.
Edit 2:
A lot of people have been commenting about this, so I though I would share it. If you are experiencing this problem, simply set myGestureRecognizer.cancelsTouchInView to false and everything should work fine.
I have found the answer. I had a UITapGestureRecognizer set for myTableView's superView. This overrode the selection call. Credit to whoever suggested that that might be it. Your answer was deleted before I could mark it correct.
Set the cancelsTouchesInView property to NO on the gesture recogniser to allow the table view to intercept the event.
Updated for Swift 3:
if you are used UITapGestureRecognizer in your code :- # Swift 3
use below lines of code:
extension YourViewController{
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(YourViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
tap.cancelsTouchesInView = false
}
func dismissKeyboard() {
view.endEditing(true)
}
}
How to called:-
In ViewDidLoad()
self.hideKeyboardWhenTappedAround()
Your problem is case-sensitivity. Your code:
- (void)tableVIew:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
should be
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
Have you defined instance variable for tableview with same name.
If not then might be this can be the issue-
_myTableView.delegate = self;
_myTableView.datasource = self;
Or-
self.myTableView.delegate = self;
self.myTableView.datasource = self;
Maybe it is a typo after all. Check that your function is not didDeselectRowAtIndexPath: (de select instead of select).
My solution is :
set cancelsTouchesInView To No of any tapGesture
I found in my custom cell , userInteractionEnable is set to
NO, simply delete userInteractionEnable = No and issue solved.
Cancel the other views touches except required one.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch {
if (touch.view == your view) {
return YES;
}
return NO;
}
Sorry, haven't got enough points to add comments - Garret's answer is great but I would add:
You can still have your gesture recognizer but you will need to set 'Cancels touches in view' to NO - then the gestures will be handed on to the view and your UITableView will work fine.
After trying many, many approaches this seems to be the correct way of doing things: a tap gesture recognizer with 'cancel touches in view' is like having an invisible layer on top of everything that grabs all the events and routes them to the view controller (the proxy). The view controller then looks at the gesture to see if it has an action binding (buttons etc.) and will route those and any remaining will just go to the gesture handler. When using a UITableView it is expecting to receive the tap but the view controller snaffles it when you have 'Cancels touches in view'.
I was having this issue for a while, and I did not see any reference to it here, so for reference, another reason for this could be that:
tableView.editing = YES;
but
tableView.allowsSelectionDuringEditing = NO;
As per documentation for
- tableView:didSelectRowAtIndexPath:
This method isn’t called when the editing property of the table is set to YES (that is, the table view is in editing mode). See "Managing Selections" in Table View Programming Guide for iOS for further information (and code examples) related to this method.
My case is strange. My tableView has 2 sections. 1st section's cells work fine about tableView:didSelectRowAt:, but 2nd section's cells doesn't trigger didSelectRowAt:.
The above problem happens at iPhone 4s, iOS 9.3. But in iPhone 5s, iOS 10.3, there are no problems, those cells works fine. It seems like iOS 9 bugs about UITableView.
After many tests, I found out one line codes produces this bug.
tableView.estimatedSectionHeaderHeight = 60.0
Because the 2nd sections has no header view. I remove this line, and all works fine.
A cell can be selected by the user (tapping on the row), by calling "tableView.selectRowAtIndexPath(..)" or "cell.setSelected(true, ...).
If the cell is selected by calling "cell.setSelected(true)", the user
cannot deselect the cell anymore.
If the cell is selected by calling
"tableView.selectRowAtIndexPath()", the user can deselect the cell as
expected.
I had intermittent failure of didSelectRowAtIndexPath: being called on my custom cell press.
I discovered that if I stopped calling [tableView reloadData] very often (10 Hz), and changed it to update every 2 seconds, almost every press would successfully call didSelectRowAtIndexPath:
It seems like reloading the view blocks presses.
My problem is the cell is a customized cell, and the action does not work on it. In addition, there is a UITapGestureRecognizer defined in the superclass.
Firstly,
Set tapGesture.cancelsTouchesInView = false
override func viewDidLoad() {
super.viewDidLoad()
initUI()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(endEditing))
tapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(tapGesture)
}
Secondly, Instead of setting
isUserInteractionEnabled = true; in the table view, I set the action on the cell.
In the ViewDidLoad()
super.viewDidLoad()
tableView.delegate = self
}
Then in the
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell: UITableView = tableView.dequeueReusableCell(for: indexPath)
cell.isUserInteractionEnabled = true;
You can try this solution if you are creating a customized cell.
It's work for me, can you try!
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)

problem with uitableview when return

i have viewcontroller with table view that when i click on a cell i go in the navigation to another view controller with another tableview.
i try to use the viewDidAppear and viewWillAppear in the first viewcontroller, that when i back to this viewcontroller from the second viewcontroller i will enter one of this method.
the problem is that he didn't enter this method when i return to this viewcontroller.
this is how i enter the second view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
ProfileViewController2 *tmp = [[ProfileViewController2 alloc]initWithType:indexPath.row string:[self.array2 objectAtIndex:indexPath.row]];
[[self navigationController] pushViewController:tmp animated:YES];
[tmp release];
[self.mytableview deselectRowAtIndexPath:indexPath animated:YES];
}
viewWillAppear and viewDidAppear are notoriously shaky on iOS.
If you are working with UITableViews we always put the code we need to run before the table gets loaded into the
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
function. It's a slight hack but works very well as this is the first function out of the UITableViewDataSource protocol called.
Alternatively, you can call
[tmp viewDidAppear];
After pushing the view controller and before releasing tmp to force the function being called.

Deselect row in UITableView which is a subview

I have a UITableView which is added to a view as a subview. When selecting a row a new view will be present with pushViewController: and the user has the option to push a Back-button to go back to the UITableView but then the cell is still selected.
Is there a way to deselect this when the view appears?
I have read that I should use the following code but I can't get it working. No errors, no warnings, no nothing.
[tableProgram deselectRowAtIndexPath:[tableProgram indexPathForSelectedRow] animated:NO];
You should deselect the row in the didSelectRowAtIndextPath method of the delegate of your UITableView. It should look something like this.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
/* initialize your view controller here v and then push it */
SomeViewController *v = [[[SomeViewController alloc] init] autorelease];
[self.navigationController pushViewController:v animated:YES];
}
Dot confuse it with didDeselectRowAtIndextPath method - it happens as people do not pay much attention when selecting methods from intelisense.
Another place you can use this is inside viewDidAppear
- (void)viewDidAppear:(BOOL)animated
and insert the following
NSIndexPath *indexPath = [tableView indexPathForSelectedRow];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
The cell stays selected after clicked and pushed to a different view controller, but deselects when it pops back to the original view controller, so it gives the user a visual cue for the last selected row.
quote SimonBS:
tried that and hoped it would work (to
get the animation) but it seems that
both viewDidAppear:(BOOL)animated and
viewWillAppear:(BOOL)animated aren't
called when in a subview.
the code by honcheng does work
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSIndexPath *indexPath = [self.tableViewOutlet indexPathForSelectedRow];
[self.tableViewOutlet deselectRowAtIndexPath:indexPath animated:YES];
}
you just need to use it on the TableView's superview. (For instance: if you have a xib file with a TableView subview inside the view you just need to go to the corresponding view controller code and override viewWillAppear, i just did it and it works! hello visual cue!)

How to remove the cell selection style color when once again showing the table view in iphone?

I am new to iphone development.I created a table displaying my contents.If i select a row ,it state is highlighted in blue color and navigates to another view and if i click the back button it navigates back to the table showing the clicked cell in blue color,i want to remove the highlighted color on table while navigating back to its view.How can i do that. Thanks.
I think the generally accepted way to do this is to deselect the cell as you're navigating to the new view. Instead of viewWillAppear, use the tableview delegate method didSelectRowAtIndexPath and the same deselectRowAtIndexPath you were using.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath{
[tableView deselectRowAtIndexPath:newIndexPath animated:YES];
}
(and by generally accepted, I mean "stuff I most often see in example code". It depends on what you want it to look like in the end)
I finally got it by implementing this in my table view class.
- (void)viewWillAppear:(BOOL)animated
{
NSIndexPath *tableSelection = [self.tableView indexPathForSelectedRow];
[self.tableView deselectRowAtIndexPath:tableSelection animated:NO];
}
You are doing it wrong, on cellForRowAtIndexPath method. use this
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
On your UITableViewCells
This would be the correct way if you wanted the cell not to be highlighted nor on selection touch neither on coming back to view. If you only want the cell deselected when coming back to view the other solutions are more suitable.
The default implementation of viewDidAppear: should take care of that for you. If you did override that method in your table view controller, don't forget to call [super viewDidAppear:animated] in your override method.
For Swift 3:
override func viewWillDisappear(_ animated: Bool) {
if let indexPath = self.tableview.indexPathForSelectedRow {
tableview.deselectRow(at: indexPath, animated: true)
}
}