【问题标题】:UITableView Checkmark ONLY ONE Row at a TimeUITableView 一次只勾选一行
【发布时间】:2025-12-27 02:45:16
【问题描述】:

使用此代码,我可以检查表中的多行。

但我想要的是一次只检查一行。这是我的代码:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    UITableViewCell *theCell = [tableView cellForRowAtIndexPath:indexPath];
    
    if (theCell.accessoryType == UITableViewCellAccessoryNone) {
        theCell.accessoryType = UITableViewCellAccessoryCheckmark;
    } else if (theCell.accessoryType == UITableViewCellAccessoryCheckmark) {
        theCell.accessoryType = UITableViewCellAccessoryNone;
    }
}

如果用户选择了不同的行,那么我希望旧行简单地自动取消选中。不知道该怎么做。

【问题讨论】:

  • 对于那些回答正确的问题,您应该点击旁边的复选标记接受正确答案。

标签: ios xcode ios4 ios5 xcode4


【解决方案1】:

目标-C:

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

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

斯威夫特:

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
}

【讨论】:

  • 最佳解决方案!谢谢!
  • 这不考虑可重用性。小心。
  • 同意@p0lAris。当细胞被重用时。它仍然有复选标记
  • 如果您的表格的行数少于会填满屏幕的行数,这很好,但是一旦它们溢出并在滚动过程中开始重复使用(如@p0lAris 所说),您的用户将非常困惑。
  • @HardikAmal - 很简单,只需在配置单元格时添加条件以明确设置附件类型。因此,请务必在 if 之后添加 else,并以任一方式设置值。
【解决方案2】:

您可以创建一个新变量来跟踪在 didSelectRowAtIndex 处选择的索引。

int selectedIndex;

在你的 cellForRowAtIndexPath 中

if(indexPath.row == selectedIndex)
{
   cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
   cell.accessoryType = UITableViewCellAccessoryNone;
}

在你的 didSelectRowAtIndex 中

selectedIndex = indexPath.row;
[tableView reloadData];

【讨论】:

  • 请声明为 NSINTEGER 而不是 int
  • 只要这段代码就够了,谢谢。约翰·保罗·马诺萨
  • 简直是超棒的男人!此解决方案也适用于继续选择单元格中的任何内容。伟大的!你救了我的一天! :)
  • 你拯救了我的一天
【解决方案3】:

最好的方法是在cellForRowAtIndexPath中设置附件类型。

使用didSelectRowAtIndexPath 仅记录应该选择哪个路径,然后调用reloadData

【讨论】:

  • 此解决方案的唯一问题是您将丢失取消选择行动画。只需重新加载其他可见单元格。
  • 我肯定会先选择正确,然后再选择...如果有必要。 :-)
  • 再次感谢您! reloadData 是让事情顺利进行的关键。非常感谢您的洞察力。
  • 在这种情况下可以实现正确和漂亮。所以没有必要诉诸reloadData
  • 您不(也确实不应该)调用 reloadData 来解决此问题。请查看其他一些答案(如我的)以正确处理动画和可重用性。
【解决方案4】:

您不需要(也不应该)在每次选择后重新加载表格。

Apple 在如何管理选择列表方面有很好的documentation。示例见清单 6-3。

这与其他一些答案或多或少相同,但我想我会添加更多细节。

您要做的是将当前选定的 IndexPath 保存到一个变量中,并在 didSelectRowAtIndexPath 中使用它来删除旧的复选标记。这也是您将添加新复选标记的地方。

您需要确保还设置/取消设置 cellForRowAtIndexPath 中的复选标记,否则如果您的列表很大并且单元格被重复使用,它看起来就像选择了多行。这是其他一些答案的问题。

参见下面的 swift 2.0 示例:

// for saving currently seletcted index path
var selectedIndexPath: NSIndexPath? = NSIndexPath(forRow: 0, inSection: 0)  // you wouldn't normally initialize it here, this is just so this code snip works
// likely you would set this during cellForRowAtIndexPath when you dequeue the cell that matches a saved user selection or the default

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    // this gets rid of the grey row selection.  You can add the delegate didDeselectRowAtIndexPath if you want something to happen on deselection
    tableView.deselectRowAtIndexPath(indexPath, animated: true) // animated to true makes the grey fade out, set to false for it to immediately go away

    // if they are selecting the same row again, there is nothing to do, just keep it checked
    if indexPath == selectedIndexPath {
        return
    }

    // toggle old one off and the new one on
    let newCell = tableView.cellForRowAtIndexPath(indexPath)
    if newCell?.accessoryType == UITableViewCellAccessoryType.None {
        newCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
    }
    let oldCell = tableView.cellForRowAtIndexPath(selectedIndexPath!)
    if oldCell?.accessoryType == UITableViewCellAccessoryType.Checkmark {
        oldCell?.accessoryType = UITableViewCellAccessoryType.None
    }

    selectedIndexPath = indexPath  // save the selected index path

    // do whatever else you need to do upon a new selection
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
    // if this is the currently selected indexPath, set the checkmark, otherwise remove it
    if indexPath == selectedIndexPath {
        cell.accessoryType = UITableViewCellAccessoryType.Checkmark
    } else {
        cell.accessoryType = UITableViewCellAccessoryType.None
    }
}

【讨论】:

  • 这个答案很棒,但它假设selectedIndexPath 已初始化,不是吗?我的意思是,如果初始化表格视图时没有默认值,didSelectRowAtIndexPath 应该尝试解开selectedIndexPath 并设置值
  • @matteodv,在我的代码片段中,我在第三行有一条评论,上面写着// likely you would set this during cellForRowAtIndexPath when you dequeue the cell that matches a saved user selection or the default。这能回答你的问题吗?
  • 是的,对不起...我的意思是没有默认值的情况,需要在第一次单击一行时设置它。在这种情况下,cellForRowAtIndexPath 不会设置变量...
  • 调用tableView.cellForRowAtIndexPath( ... ) 与调用tableView.reloadRows(at: ...) 本质上不一样吗?如果您重新加载新旧行(不是整个表格视图),您可以从didSelect 中删除重复的复选标记代码。
【解决方案5】:

您不需要重新加载 tableView。

见以下代码:

为你的类声明一个 NSIndexPath lastSelectedIndexPath 变量

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

    if(lastSelectedIndexPath) {

        UITableViewCell *lastCell = [tableView cellForRowAtIndexPath:lastSelectedIndexPath];
        [lastCell setAccessoryView:nil];
    }

    UITableViewCell *currentCell = [tableView cellForRowAtIndexPath:currentIndexPath];
    [currentCell setAccessoryView:UITableViewCellAccessoryCheckmark];

    lastSelectedIndexPath = indexPath;
}

【讨论】:

  • 有谁知道如何使用 NSUserDeafults 保存所选行的状态?我一直在寻找一种简单的方法来做到这一点。我找到的所有解决方案都相当复杂,而且对于我的项目来说,这只会让我的代码变得混乱。
  • @Borges 你能解释一下你的问题吗?你说的细胞状态到底是什么意思?
【解决方案6】:

对于 Swift 3,以下对我有用:

override func viewDidLoad() {
    super.viewDidLoad()

    // allow cells to be selected
    tableView.allowsSelection = true

    // ensure that deselect is called on all other cells when a cell is selected
    tableView.allowsMultipleSelection = false
}

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

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

【讨论】:

    【解决方案7】:

    我认为https://*.com/a/5960016/928599 会帮助你。
    我用过它,它也适用于 deselectRowAtIndexPath!

    【讨论】:

      【解决方案8】:

      最简单的方法是保存选中的IndexPath并在cellForRowAtIndexPath中勾选:

      1. 初始化选中的索引路径:

        selectedIndexPath = [[NSIndexPath alloc] init];
        
      2. cellForRowAtIndexPath:

        if([selectedIndexPath isEqual:indexPath]){
            [checkMark setHidden:NO];
        } else {
            [checkMark setHidden:YES];
        }
        
      3. didSelectRowAtIndexPath:

        selectedIndexPath = indexPath;
        [tableView reloadData];
        

      它应该可以工作试试这个 Njoy :)

      【讨论】:

        【解决方案9】:

        试试这个:

        func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
            if indexPath.section == 1 {
                // un-select the older row
                if let selected = self.LastSelected {
                    tableView.cellForRowAtIndexPath(selected)?.accessoryType = .None
                }
        
                // select the new row
                tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = UITableViewCellAccessoryType.Checkmark
                self.LastSelected = indexPath
            }
        }
        

        【讨论】:

          【解决方案10】:

          在 Swift 中,以下工作完美:

          lastSelectedIndexPath = NSIndexPath(forRow: 1, inSection: 0)//Row will be the previous selected row
          
          
              func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
          {
              let cell:LabelsSelectionCell = tableView.dequeueReusableCellWithIdentifier("LabelsSelectionCell", forIndexPath: indexPath) as! LabelsSelectionCell
              cell.setCellLael(labelOptionsArray[indexPath.row])
              if lastSelectedIndexPath == indexPath
              {
                  cell.setCellCheckedStatus(true)
              }
              return cell
          }
          func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
          {
              if let _ = lastSelectedIndexPath
              {
                  let lastCell:LabelsSelectionCell = tableView.cellForRowAtIndexPath(lastSelectedIndexPath!) as! LabelsSelectionCell
                  lastCell.setCellCheckedStatus(false)
              }
              let currentCell:LabelsSelectionCell = tableView.cellForRowAtIndexPath(indexPath) as! LabelsSelectionCell
              currentCell.setCellCheckedStatus(true)
          
              lastSelectedIndexPath = indexPath
          
              tableView.deselectRowAtIndexPath(indexPath, animated: true)
          }
          

          【讨论】:

            【解决方案11】:

            在 Swift 2.0 中我使用了这个:

            func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
            
                if((lastSelectedIndexPath) != nil) {
                    let lastCell = tableView.cellForRowAtIndexPath(lastSelectedIndexPath)
                    lastCell?.accessoryType = UITableViewCellAccessoryType.None
                }
            
                let currentCell = tableView.cellForRowAtIndexPath(indexPath)
                currentCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
            
                lastSelectedIndexPath = indexPath
            
            }
            

            【讨论】:

            • lastSelectedIndexPath 是如何初始化和管理的?因为 lastSelectedIndexPath 永远不能为零
            • var lastSelectedIndexPath: NSIndexPath? = NSIndexPath(forRow: 0, inSection: 0)
            • var lastSelectedIndexPath: NSIndexPath! 在开始时为 nil,但之后,我用 if 检查是否为 nil,并在函数结束时执行:lastSelectedIndexPath = indexPath
            【解决方案12】:

            我的方法是在选择过程中取消选择其他单元格:

            func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                let cell = ....
            
                if condition {
                    cell.accessoryType = .checkmark
                    tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableViewScrollPosition.bottom)
                } else {
                    cell.accessoryType = .none
                }
            
                return cell
            }
            
            func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                for row in 0...tableView.numberOfRows(inSection: 0) {
                    if row == indexPath.row {continue}
                    tableView.deselectRow(at: IndexPath(row: row, section: 0), animated: true)
                }
            
                if let cell = tableView.cellForRow(at: indexPath) {
                    cell.accessoryType = .checkmark
                }
            }
            
            func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
                let cell = tableView.cellForRow(at: indexPath)
                cell?.accessoryType = .none
            }
            

            【讨论】:

              【解决方案13】:

              这是我遇到此问题时的想法。
              这段代码是在最新版本的 Swift 中实现的,截至今天...
              如需更多信息和/或扩展文件,请查看此 sn-p 的Github Gist

              override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {       
                  if let sip = selectedIndex {
                      tableView.deselectRow(at: sip, animated: false)
                  }
                  selectedIndex = indexPath
              }
              
              override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
                  if selectedIndex?.row == indexPath.row {
                      selectedIndex = nil
                  }
              }
              

              【讨论】:

                【解决方案14】:

                测试并解决了,试试这个效果很好

                将此添加为全局变量

                var selectedIndexPath = NSIndexPath(row: 0, section: 0)
                

                在 didselect 方法中添加这个

                        // old one check off
                        if indexPath != selectedIndexPath as IndexPath {
                            let oldCell = categorytable.cellForRow(at: selectedIndexPath as IndexPath)
                            if oldCell?.accessoryView != nil {
                                oldCell?.accessoryView = nil
                            }
                            else {
                                let imageName = "checkmark"
                                let image: UIImageView = UIImageView(image: UIImage(named: imageName))
                                cell.accessoryView = image
                            }
                        }
                        // the new one on
                
                        if  cell.accessoryView == nil {
                            let imageName = "checkmark"
                            let image: UIImageView = UIImageView(image: UIImage(named: imageName))
                            cell.accessoryView = image
                        }
                        else {
                            cell.accessoryView = nil
                        }
                

                【讨论】:

                  【解决方案15】:

                  您可以在一行代码中自定义单元格类型。

                  final class CheckableTableViewCell: UITableViewCell {
                  
                    override func setSelected(_ selected: Bool, animated: Bool) {
                      super.setSelected(selected, animated: animated)
                  
                      self.accessoryType = selected ? .checkmark : .none
                    }
                  
                  }
                  

                  要使此方法有效 - 应选择您的单元格。

                  如果您不想看到所选单元格的突出显示,只需在情节提要或代码中将单元格的 selectionStyle 设置为 .none

                  这个方法不行,如果你调用的话

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

                  也适用于多选。

                  【讨论】:

                    【解决方案16】:

                    Swift 5 版本的 jeffjv 响应。

                    // for saving currently selected index path
                    var selectedIndexPath: NSIndexPath? = NSIndexPath(forRow: 0, inSection: 0)  // you wouldn't normally initialize it here, this is just so this code snip works
                    // likely you would set this during cellForRowAtIndexPath when you dequeue the cell that matches a saved user selection or the default
                    
                    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
                        // this gets rid of the grey row selection.  You can add the delegate didDeselectRowAtIndexPath if you want something to happen on deselection
                        tableView.deselectRow(at: indexPath, animated: true) // animated to true makes the grey fade out, set to false for it to immediately go away
                    
                        // if they are selecting the same row again, there is nothing to do, just keep it checked
                        if indexPath == selectedIndexPath! as IndexPath {
                            return
                        }
                    
                        // toggle old one off and the new one on
                        let newCell = tableView.cellForRow(at: indexPath)
                        if newCell?.accessoryType == UITableViewCell.AccessoryType.None {
                            newCell?.accessoryType = UITableViewCell.AccessoryType.Checkmark
                        }
                        let oldCell = tableView.cellForRow(at: selectedIndexPath! as IndexPath)
                        if oldCell?.accessoryType == UITableViewCell.AccessoryType.Checkmark {
                            oldCell?.accessoryType = UITableViewCell.AccessoryType.None
                        }
                    
                        selectedIndexPath = indexPath as NSIndexPath  // save the selected index path
                    
                        // do whatever else you need to do upon a new selection
                    }
                    
                    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
                        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
                        // if this is the currently selected indexPath, set the checkmark, otherwise remove it
                         if indexPath == selectedIndexPath! as IndexPath {
                            cell.accessoryType = UITableView.CellAccessoryType.Checkmark
                        } else {
                            cell.accessoryType = UITableView.CellAccessoryType.None
                        }
                    }
                    

                    【讨论】:

                      【解决方案17】:

                      Swift iOS

                       var checkedIndexPath : NSIndexPath?
                      
                      // table row which row was selected
                      func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
                          tableView.deselectRowAtIndexPath(indexPath, animated: true)
                          
                          println("Section #\(indexPath.section)! You selected cell #\(indexPath.row)!")
                          
                          if (self.checkedIndexPath != nil) {
                              var cell = tableView.cellForRowAtIndexPath(self.checkedIndexPath!)
                              cell?.accessoryType = .None
                          }
                          
                          var cell = tableView.cellForRowAtIndexPath(indexPath)
                          cell?.accessoryType = .Checkmark
                          
                          self.checkedIndexPath = indexPath
                          
                      }// end tableView
                      

                      【讨论】:

                      • 当 'checkedIndexPath' 不在 tableView 的 VisibleCells 中时它会崩溃 if (self.checkedIndexPath != nil) { var cell = tableView.cellForRowAtIndexPath(self.checkedIndexPath!) cell?.accessoryType = .None } 如果 Cell 不在 Visible Cells 中,则为 nil
                      【解决方案18】:

                      存储选定的索引路径,在didSelectRowAt: 中更新它,然后从那里调用reloadRows(at: 以仅重新加载新旧检查的行(而不是整个表视图):

                      var selectedIndexPath: IndexPath?
                      
                      override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                          let previous = selectedIndexPath
                          selectedIndexPath = indexPath
                          
                          let allowDeselection = true // optional behavior
                          if allowDeselection && previous == selectedIndexPath {
                              selectedIndexPath = nil
                          }
                          
                          tableView.reloadRows(at: [previous, selectedIndexPath].compactMap({ $0 }), with: .automatic)
                          tableView.deselectRow(at: indexPath, animated: true)
                      }
                      

                      处理cellForRowAt indexPath:中的复选标记附件视图的设置。这将考虑到单元重用,并且还会由您对reloadRows(at: 的上述调用触发,避免重复设置单元附件的代码:

                      override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                          let cell = tableView.dequeueReusableCell(withIdentifier: "IDENTIFIER", for: indexPath)
                          
                          cell.accessoryType = (indexPath == selectedIndexPath) ? .checkmark : .none
                          
                          // ...
                          
                          return cell
                      }
                      

                      【讨论】: