220

I am working on a project on which I have to preselect a particular cell.

I can preselect a cell using -willDisplayCell, but I can't deselect it when the user clicks on any other cell.

- (void)tableView:(UITableView*)tableView 
        willDisplayCell:(UITableViewCell*)cell
        forRowAtIndexPath:(NSIndexPath*)indexPath
{ 
    AppDelegate_iPad *appDelegte = 
      (AppDelegate_iPad *)[[UIApplication sharedApplication] delegate];

    if ([appDelegte.indexPathDelegate row] == [indexPath row])
    {
        [cell setSelected:YES];    
    } 
}

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

    AppDelegate_iPad *appDelegte = 
      (AppDelegate_iPad *)[[UIApplication sharedApplication] delegate];

    NSIndexPath *indexpath1 = appDelegte.indexPathDelegate;
    appDelegte.indexPathDelegate = indexPath;
    [materialTable deselectRowAtIndexPath:indexpath1 animated:NO];
}

Can you help?

Alex Cio
  • 5,752
  • 4
  • 40
  • 73
Ram
  • 2,373
  • 2
  • 15
  • 14

23 Answers23

423

use this code

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
 {
    //Change the selected background view of the cell.
     [tableView deselectRowAtIndexPath:indexPath animated:YES];
 }

Swift 3.0:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    //Change the selected background view of the cell.
    tableView.deselectRow(at: indexPath, animated: true)
}
Aland Kawa
  • 53
  • 1
  • 12
Saikiran Komirishetty
  • 6,345
  • 1
  • 26
  • 36
  • 1
    There is a didDeselectRowAtIndexPath to use to deselect the previous one. – huggie Apr 23 '14 at 04:42
  • 3
    I like to think I can't upvote this post because it would cause an integer overflow :) – Milo Jul 06 '14 at 23:30
  • 1
    hi @ram, you need to select an answer on this question – Fattie Mar 20 '16 at 16:43
  • 4
    I don't understand... this code is never going to let anything get selected. – C. Tewalt Oct 24 '16 at 18:25
  • @matrixugly is right. It's more appropriate to do this inside `willSelectRowAt:`. See a more complete answer here: http://stackoverflow.com/questions/12693402/uitableview-tap-to-deselect-cell – HuaTham Feb 03 '17 at 05:34
119

Use the following method in the table view delegate's didSelectRowAtIndexpath (Or from anywhere)

[myTable deselectRowAtIndexPath:[myTable indexPathForSelectedRow] animated:YES];
Scott Berrevoets
  • 16,581
  • 6
  • 56
  • 79
Shamitha
  • 1,407
  • 1
  • 10
  • 9
40

Try this:

for (NSIndexPath *indexPath in tableView.indexPathsForSelectedRows) {
    [tableView deselectRowAtIndexPath:indexPath animated:NO];
}
Souleiman
  • 3,065
  • 3
  • 17
  • 21
ikosdroid
  • 421
  • 5
  • 8
36

It might be useful to make an extension in Swift for this.

Swift 4 and Swift 5:

Swift extension (e.g. in a UITableViewExtension.swift file):

import UIKit

extension UITableView {

    func deselectSelectedRow(animated: Bool)
    {
        if let indexPathForSelectedRow = self.indexPathForSelectedRow {
            self.deselectRow(at: indexPathForSelectedRow, animated: animated)
        }
    }

}

Use e.g.:

override func viewWillAppear(_ animated: Bool)
{
    super.viewWillAppear(animated)

    self.tableView.deselectSelectedRow(animated: true)
}
Thomas Neuteboom
  • 661
  • 1
  • 6
  • 14
28

Please check with the delegate method whether it is correct or not. For example;

-(void) tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath

for

-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Titouan de Bailleul
  • 12,622
  • 11
  • 60
  • 114
jfalexvijay
  • 3,678
  • 7
  • 37
  • 67
23

Swift 4:

tableView.deselectRow(at: indexPath, animated: true)
LinusGeffarth
  • 21,607
  • 24
  • 100
  • 152
Andrea.Ferrando
  • 928
  • 11
  • 21
18

This is a solution for Swift 4

 in tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)

just add

tableView.deselectRow(at: indexPath, animated: true)

it works like a charm

example:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
//some code
}
tBug
  • 645
  • 8
  • 12
  • until you add selection on editing in your tableview, it will be impossible to select any cell because it will deselect it when you done selecting it :) – Mehdi Chennoufi Aug 27 '18 at 16:47
  • This is certainly the correct approach, when you're not editing, when you're "tapping a row to do something" – Fattie Jul 25 '19 at 19:06
11

In addition to setting the cell as selected, you also need to inform the tableView that the cell is selected. Add a call to -tableView:selectRowAtIndexPath:animated:scrollPosition: to your willDisplayCell: method: and you will be able to deselect it as normal.

- (void)tableView:(UITableView*)tableView 
  willDisplayCell:(UITableViewCell*)cell
forRowAtIndexPath:(NSIndexPath*)indexPath
{ 
    AppDelegate_iPad *appDelegte = (AppDelegate_iPad *)[[UIApplication sharedApplication] delegate];

    if ([appDelegte.indexPathDelegate row] == [indexPath row])
    {
        // SELECT CELL
        [cell setSelected:YES]; 
        // TELL TABLEVIEW THAT ROW IS SELECTED
        [tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];   
    } 
}

Be sure to use UITableViewScrollPositionNone to avoid odd scrolling behavior.

Drew C
  • 6,258
  • 3
  • 36
  • 50
  • 1
    Absolutely correct. I think that `[cell setSelected:YES]` could be omitted: `selectRowAtIndexPath` will take care of the cell's `selected` state – spassas Jan 15 '15 at 17:07
  • This answer works for UITableView's with multiple selections. Thumbs up. – Danny Bravo Oct 23 '18 at 11:19
6

Based on saikirans solution, I have written this, which helped me. On the .m file:

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

    if(selectedRowIndex && indexPath.row == selectedRowIndex.row) {

        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        selectedRowIndex = nil;

    }

    else {  self.selectedRowIndex = [indexPath retain];   }

    [tableView beginUpdates];
    [tableView endUpdates];

}

And on the header file:

@property (retain, nonatomic) NSIndexPath* selectedRowIndex;

I am not very experienced either, so double check for memory leaks etc.

Yunus Nedim Mehel
  • 11,371
  • 4
  • 47
  • 54
6

If you want to select any table cell with the first click and deselect with the second, you should use this code:

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

    if (self.selectedIndexPath && 
        [indexPath compare:self.selectedIndexPath] == NSOrderedSame) {

        [tableView deselectRowAtIndexPath:indexPath animated:YES];
         self.selectedIndexPath = nil;
    } 
    else {
        self.selectedIndexPath = indexPath;
    }
}
CHiP-love-NY
  • 467
  • 6
  • 12
6

This should work:

[tableView deselectRowAtIndexPath:indexpath1 animated:NO];

Just make sure materialTable and tableView are pointing to the same object.

Is materials connected to the tableView in Interface Builder?

dplante
  • 2,387
  • 3
  • 20
  • 26
Jordan
  • 21,598
  • 10
  • 48
  • 63
  • i have tested with NO but that also not solve my problem. but i fixed this issue with another way as reloaded Data when ever the didselect delegate fired. – Ram Oct 20 '10 at 09:46
5

Swift 4:

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    let cell = tableView.cellForRow(at: indexPath)
    if cell?.isSelected == true { // check if cell has been previously selected
        tableView.deselectRow(at: indexPath, animated: true)
        return nil
    } else {
        return indexPath
    }
}
  • 2
    This worked for me in Swift 3. Just had to add `self.tableView(tableView, didDeselectRowAt: indexPath)` after `tableView.deselectRow...` because it was not called automatically – elysch Nov 03 '17 at 03:46
4

Swift 2.0:

tableView.deselectRowAtIndexPath(indexPath, animated: true)
raed
  • 4,061
  • 4
  • 25
  • 39
Anit Kumar
  • 7,239
  • 1
  • 16
  • 25
3

try this

[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
EnasAZ
  • 82
  • 6
3
NSIndexPath * index = [self.menuTableView indexPathForSelectedRow];
[self.menuTableView deselectRowAtIndexPath:index animated:NO];

place this code according to your code and you will get your cell deselected.

rahulchona
  • 582
  • 4
  • 10
3

Can you try this?

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

    AppDelegate_iPad *appDelegte = 
    (AppDelegate_iPad *)[[UIApplication sharedApplication] delegate];
    NSIndexPath *indexpath1 = appDelegte.indexPathDelegate;
    appDelegte.indexPathDelegate = indexPath;

    UITableViewCell *prevSelectedCell = [tableView cellForRowAtIndexPath: indexpath1];
    if([prevSelectedCell isSelected]){
       [prevSelectedCell setSelected:NO];
    }
}

It worked for me.

2

Swift 3.0:

Following the protocol conformance section of the ray wenderlich swift style guide, to keep related methods grouped together, put this extension below your view controller class like that:

// your view controller
class MyViewcontroller: UIViewController {
  // class stuff here
}

// MARK: - UITableViewDelegate
extension MyViewcontroller: UITableViewDelegate {
      // use the UITableViewDelegate method tableView(_:didSelectRowAt:)
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        // your code when user select row at indexPath

        // at the end use deselectRow
        tableView.deselectRow(at: indexPath, animated: true)
    }
}
ronatory
  • 6,311
  • 3
  • 24
  • 41
1

Swift

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    // your code

    // your code

    // and use deselect row to end line of this function

    self.tableView.deselectRowAtIndexPath(indexPath, animated: true)

}
Programer_saeed
  • 115
  • 2
  • 5
1
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
}
Aayushi
  • 709
  • 10
  • 13
1

Swift 3

I've run into this as well - when you navigate or unwind back to the table view it usually doesn't deselect the previously selected row for you. I put this code in the table view controller & it works well:

override func viewDidAppear(_ animated: Bool) {
    if let lastSelectedRow = tableView.indexPathForSelectedRow {
        tableView.deselectRow(at: lastSelectedRow, animated: true)
    }
}
Trev14
  • 2,516
  • 2
  • 22
  • 31
1

Swift 3/4

In ViewController:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
}

In Custom Cell:

override func awakeFromNib() {
    super.awakeFromNib()
    selectionStyle = .none
}
Sevy11
  • 131
  • 2
  • 5
0

swift 3.0

    tableView.deselectRow(at: indexPath, animated: true)
Marcelo Gracietti
  • 2,885
  • 1
  • 11
  • 23
0

To have deselect (DidDeselectRowAt) fire when clicked the first time because of preloaded data; you need inform the tableView that the row is already selected to begin with, so that an initial click then deselects the row:

//Swift 3:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    if tableView[indexPath.row] == "data value"

        tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableViewScrollPosition.none)

    }
}
Arkelyan
  • 250
  • 4
  • 14