You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中UITableView滚动时出现多余勾选标记的问题排查与解决

UITableViewCell复选框复用导致UI异常:选中第1个单元格时第5个也显示选中

在Storyboard的UITableViewCell中添加复选框后,表格有8个单元格时出现异常:选中第1个单元格,第5个单元格也会显示blue_tick;选中第2个单元格,第6个单元格同样显示选中状态。调试发现cellForRowAt方法并未进入if isfromDetails分支,即使注释该分支代码,异常仍存在,且选中的id并未错误添加到数组,仅UI显示异常。

问题代码

didSelectRowAt方法

var selectedArray:[Int] = []

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if tableView == self.tableView{
        let cell:PaymentCell = tableView.cellForRow(at: indexPath) as! PaymentCell
        if let indexData = eventListData?.result?.events?[indexPath.row] {
            if let index = selectedArray.firstIndex(of: indexData.id ?? 0) {
                cell.checkImg.image = UIImage(named: "checkbox_inactive")
                selectedArray.remove(at: index)
            } else {
                selectedArray.append(indexData.id ?? 0)
                cell.checkImg.image = UIImage(named: "blue_tick")
            }
        }
    }
}

cellForRowAt方法及Cell类

var isfromDetails = false

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "PaymentCell", for: indexPath) as! PaymentCell
    
    let indexData = eventListData?.result?.events?[indexPath.row]
    cell.titleLbl.text = indexData?.event_title
    cell.addressLbl.text = indexData?.event_venue
    
    var floatVal: Float?
    if let standardRate = indexData?.ticket_price,
       let convValue = eventListData?.result?.conversion_factor {
        let priceVal = Float(standardRate)
        let convVal = Float(convValue)
        
        floatVal = (priceVal * convVal!)
        
        if let floatVal = floatVal {
            let formatter = NumberFormatter()
            formatter.maximumFractionDigits = 2
            formatter.roundingMode = .down
            let roundedValue1: String = formatter.string(from: floatVal as NSNumber) ?? ""
            
            cell.priceLbl.text = "\(eventListData?.result?.user_curreny?.symbol ?? "GBP") \(roundedValue1)"
            
        } else {
            cell.priceLbl.text = "N/A"
        }
    }
    let bannerImage = indexData?.event_thumbnail ?? ""
    cell.bannerImg.getImage(subUrl: eventListImageBaseUrl, withImagePath: bannerImage, placeHolder: #imageLiteral(resourceName: "icon8"))
    cell.bannerImg.contentMode = .scaleAspectFit
    
    if isfromDetails{
        let id = indexData?.id ?? 0
        if selectedArray.contains(id){
            cell.checkImg.image = UIImage(named: "blue_tick")
        }else{
            cell.checkImg.image = UIImage(named: "checkbox_inactive")
        }
    }
    return cell
}

class EventListCell: UITableViewCell {
    @IBOutlet weak var checkImg: UIImageView!
    override func awakeFromNib() {
        selectionStyle = .none
        super.awakeFromNib()
    }
}

问题原因

  1. Cell复用机制未正确处理:UITableView会复用滚出屏幕的Cell,你直接在didSelectRowAt中修改Cell的复选框图片,但未在cellForRowAt中统一根据数据源重置状态。比如第1个Cell滚出后被复用为第5个Cell,此时复选框仍保留之前的blue_tick状态。
  2. isfromDetails限制导致状态未初始化:isfromDetails默认值为false,导致cellForRowAt完全不处理复选框状态,复用的Cell无法重置为默认的未选中状态。

解决方案

1. 移除isfromDetails判断,统一在cellForRowAt中处理复选框状态

确保每个Cell在复用或初始化时,都根据selectedArray的数据源渲染正确的复选框状态:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "PaymentCell", for: indexPath) as! PaymentCell
    
    let indexData = eventListData?.result?.events?[indexPath.row]
    cell.titleLbl.text = indexData?.event_title
    cell.addressLbl.text = indexData?.event_venue
    
    var floatVal: Float?
    if let standardRate = indexData?.ticket_price,
       let convValue = eventListData?.result?.conversion_factor {
        let priceVal = Float(standardRate)
        let convVal = Float(convValue)
        
        floatVal = (priceVal * convVal!)
        
        if let floatVal = floatVal {
            let formatter = NumberFormatter()
            formatter.maximumFractionDigits = 2
            formatter.roundingMode = .down
            let roundedValue1: String = formatter.string(from: floatVal as NSNumber) ?? ""
            
            cell.priceLbl.text = "\(eventListData?.result?.user_curreny?.symbol ?? "GBP") \(roundedValue1)"
            
        } else {
            cell.priceLbl.text = "N/A"
        }
    }
    let bannerImage = indexData?.event_thumbnail ?? ""
    cell.bannerImg.getImage(subUrl: eventListImageBaseUrl, withImagePath: bannerImage, placeHolder: #imageLiteral(resourceName: "icon8"))
    cell.bannerImg.contentMode = .scaleAspectFit
    
    // 移除isfromDetails判断,统一根据数据源设置复选框状态
    let eventId = indexData?.id ?? 0
    cell.checkImg.image = selectedArray.contains(eventId) 
        ? UIImage(named: "blue_tick") 
        : UIImage(named: "checkbox_inactive")
    
    return cell
}

2. 修改didSelectRowAt,操作数据源后刷新对应Cell

不再直接修改Cell的UI,而是通过修改数据源后刷新Cell,确保UI与数据源一致:

var selectedArray:[Int] = []

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    guard tableView == self.tableView,
          let indexData = eventListData?.result?.events?[indexPath.row] else {
        return
    }
    
    let eventId = indexData.id ?? 0
    if let index = selectedArray.firstIndex(of: eventId) {
        selectedArray.remove(at: index)
    } else {
        selectedArray.append(eventId)
    }
    
    // 刷新当前行,触发cellForRowAt重新渲染UI
    tableView.reloadRows(at: [indexPath], with: .automatic)
}

内容的提问来源于stack exchange,提问作者new dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 00:15:21