Something 检查 UITableViewCell 中选定的行

我是一个 iOS 开发的新手。我想添加一个选择标记到我的 UITableViewCell当它被选中。选中另一行时,应删除复选标记。我要怎么做?

98118 次浏览

To set a checkmark:

UITableViewCell *cell = ...;
cell.accessoryType = UITableViewCellAccessoryCheckmark;

To select/deselect a cell:

[cell setSelected:TRUE animated:TRUE]; // select
[cell setSelected:FALSE animated:TRUE]; // deselect

To deselect the previous cell use a NSIndexPath *lastSelected ivar to track the last selected cell:

- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
if (self.lastSelected==indexPath) return; // nothing to do


// deselect old
UITableViewCell *old = [self.tableView cellForRowAtIndexPath:self.lastSelected];
old.accessoryType = UITableViewCellAccessoryNone;
[old setSelected:FALSE animated:TRUE];


// select new
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[cell setSelected:TRUE animated:TRUE];


// keep track of the last selected cell
self.lastSelected = indexPath;
}

In your UITableViewDatasource method:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];


if(cell == nil )
{
cell =[[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
if ([indexPath compare:self.lastIndexPath] == NSOrderedSame)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}


// UITableView Delegate Method
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.lastIndexPath = indexPath;


[tableView reloadData];
}

And lastIndexPath is a property(strong) NSIndexPath* lastIndexPath;

small typo

// deselect old
UITableViewCell *old = [self.tableView cellForRowAtIndexPath:self.lastSelected];
cell.accessoryType = UITableViewCellAccessoryNone;
[cell setSelected:FALSE animated:TRUE];

should read

// deselect old
UITableViewCell *old = [self.tableView cellForRowAtIndexPath:self.lastSelected];
old.accessoryType = UITableViewCellAccessoryNone;
[old setSelected:FALSE animated:TRUE];

and also in the

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == [previouslySelected intValue])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
selectedIndex = indexPath;
[cell setSelected:YES animated:YES];
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
[cell setSelected:NO animated:YES];
}
}

where previouslySelected is your local ivar, etc. that way if you reload with a selected index it also gets deselected when you flick through the possible selections.

Do not use [tableview reloadData]; // its a hammer.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath   *)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
}


-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
}

I found that reloading the data interrupts the deselect animation in an ugly way.

This Swift implementation cleanly adds/removes checkmarks and deselects the row:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if self.lastSelection != nil {
self.myTableView.cellForRowAtIndexPath(self.lastSelection)?.accessoryType = .None
}


self.myTableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark


self.lastSelection = indexPath


self.myTableView.deselectRowAtIndexPath(indexPath, animated: true)
}

where lastSelection is declared as var lastSelection: NSIndexPath!

No extra activity in cellForRowAtIndexPath needed. Shouldn't be hard to replicate in Obj-C.

The upper mentioned code only works with the single selection.This following code will surely works for multiple selections.

- (void)viewDidLoad {
arrSelectionStatus =[NSMutableArray array]; //arrSelectionStatus holds the cell selection status
for (int i=0; i<arrElements.count; i++) { //arrElements holds those elements which will be populated in tableview
[arrSelectionStatus addObject:[NSNumber numberWithBool:NO]];
}
}


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


UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];


if (cell==nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
}


cell.textLabel.text=[arrElements objectAtIndex:indexPath.row];


if ([[arrSelectionStatus objectAtIndex:indexPath.row] boolValue] == YES)
cell.accessoryType = UITableViewCellAccessoryCheckmark;
else
cell.accessoryType = UITableViewCellAccessoryNone;


return cell;
}


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


UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;


[arrSelectionStatus replaceObjectAtIndex:indexPath.row withObject:[NSNumber numberWithBool:YES]];
}


-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;


[arrSelectionStatus replaceObjectAtIndex:indexPath.row withObject:[NSNumber numberWithBool:NO]];
}

Assuming you are in a class that inherits from UITableViewController, this does the trick in Swift 3:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Add a visual cue to indicate that the cell was selected.
self.tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}


override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
// Invoked so we can prepare for a change in selection.
// Remove previous selection, if any.
if let selectedIndex = self.tableView.indexPathForSelectedRow {
// Note: Programmatically deslecting does NOT invoke tableView(:didSelectRowAt:), so no risk of infinite loop.
self.tableView.deselectRow(at: selectedIndex, animated: false)
// Remove the visual selection indication.
self.tableView.cellForRow(at: selectedIndex)?.accessoryType = .none
}
return indexPath
}

Just Call didSelectRowAtIndexPath Method when select any row to Show CheckMark and Select checkmark row for hide CheckMark.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:true];
NSLog(@"touch");


UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}

When a selected cell(with the check mark is selected again), just remove the selection.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
BOOL isSelected = ([tableView cellForRowAtIndexPath:indexPath].accessoryType ==  UITableViewCellAccessoryCheckmark);
if(isSelected){
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
[tableView deselectRowAtIndexPath:indexPath animated:YES]; //this won't trigger the didDeselectRowAtIndexPath, but it's always a good idea to remove the selection
}else{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
}
}


- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath*)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
}

Bonus:

Use self.tableView.indexPathForSelectedRow for detecting indexPath for the selected cell

The above answers not work if you reused the cell for large number of data. On scroll you can see repeated checkmark. To avoid use below steps:

  1. declare on variable : var indexNumber:NSInteger = -1

  2. Add below code in cellforRowAtIndexPath:

     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
    if indexNumber == indexPath.row{
    cell.accessoryType = .checkmark
    }else{
    cell.accessoryType = .none
    }
    }
    
  3. And in didselectAtIndexpath add below code:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath as IndexPath)?.accessoryType = .checkmark
indexNumber = indexPath.row
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath as IndexPath)?.accessoryType = .none
}

It's better to face this problem from other direction. Put all work on internal UIKit mechanisms and move implementation to UITableViewCell:

@implementation MYTableViewCell


- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];


self.accessoryType = selected ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
}


- (void)prepareForReuse {
[super prepareForReuse];
self.accessoryType = UITableViewCellAccessoryNone;
}


@end
extension ViewController : UITableViewDelegate,UITableViewDataSource {


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataArray.count
}


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = dataArray[indexPath.row]
if selectedData.contains(dataArray[indexPath.row]) {
cell.accessoryType = .checkmark
}else{
cell.accessoryType = .none
}
return cell
}




func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


if selectedData.contains(dataArray[indexPath.row]) {
selectedData.removeLast()
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}else {
selectedData.removeAll()
selectedData.append(dataArray[indexPath.row])
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
print(selectedData)
}


func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}


}

based on dataArray table view formed.. similarly, I took an empty array, and whenever the user taps on a cell, based on indexValue of from dataArray I stored that object in selectedDataArray

As for the question its like... A question has multiple options(Answers), But at finally only one or none answer will be a result

enter image description here

Likewise, only one cell should show checkmark and remaining cells should be in unselected... it some case u can deselect your answer... I hope this is the best answer to this question

enter image description here

Update Swift 4

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}


func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}

swift 4 in case you need.

var lastSelection: NSIndexPath!
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {




//CHECK MARK THE CELL
if self.lastSelection != nil {
self.tableView.cellForRow(at: self.lastSelection as IndexPath)?.accessoryType = .none
}


self.tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark


self.lastSelection = indexPath as NSIndexPath


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


}

Using Swift 4.2 and swift 5 Working code of check mark for only selected row in TableView

   func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
self.tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//print(self.coloursArray[indexPath.row])


self.tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}

I think it's cleaner to set the accessory within your custom UITableViewCell implementation. In swift, I have used:

override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)


// Configure the view for the selected state
accessoryType = selected ? .checkmark : .none
}

There are two ways you could do it. one is without multiple selections and another is with multiple selections.

// Table View Controller -- without Multiple Selection


// Step 1


override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


if(tableView.cellForRow(at: indexPath)?.imageView?.image == UIImage(systemName:"checkmark.circle")) {
tableView.cellForRow(at: indexPath)?.imageView?.image = UIImage(systemName:"circle")
} else {
tableView.cellForRow(at: indexPath)?.imageView?.image = UIImage(systemName:"checkmark.circle")
}
}


//Step 2


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


let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = employeeValues[indexPath.row]
cell.imageView?.image = UIImage(systemName:"circle")


return cell
}


//  Table View Controller -- with Multiple Selection


@IBOutlet var myTableView: UITableView!


override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.allowsMultipleSelection = true
}


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


let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = employeeValues[indexPath.row]
cell.imageView?.image = UIImage(systemName:"circle")


return cell
}


override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


//   let cell = tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCell.AccessoryType.checkmark


tableView.cellForRow(at: indexPath)?.imageView?.image = UIImage(systemName:"checkmark.circle")


}


override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {


tableView.cellForRow(at: indexPath)?.imageView?.image = UIImage(systemName:"circle")
}
// Check or Uncheck a cell
if let cell = tableView.cellForRow(at: indexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
} else {
cell.accessoryType = .checkmark
}
}


Swift 5

    var selectedIndex: IndexPath!


func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedIndex != nil {
tableView.cellForRow(at: selectedIndex)?.accessoryType = .none
}
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
selectedIndex = indexPath
tableView.deselectRow(at: indexPath, animated: true)
}