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

Swift开发:如何将TableView单元格中设置过属性的字符串重置为原文本

问题描述

我有一个TableView,其每个单元格中都有一个UISwitch控件,开启该控件后会修改当前行的label内容。
我的修改逻辑如下:

@IBAction func completedTask(_ sender: UISwitch) {
    
    //Getting original taskLabel
    let initalLabel = taskLabel.text
    
    //Modifying the string to have a line through it. Storing it in variable attributeString
    let attributeString: NSMutableAttributedString =  NSMutableAttributedString(string: taskLabel.text!)
    
        attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 2, range: NSMakeRange(0, attributeString.length))
    
    
    if sender.isOn{
        print("attributed Label --> ",attributeString)
        taskLabel.textColor = UIColor.red
        taskLabel.attributedText = attributeString
    }else{
        print("initial Label --> ",initalLabel!)
        taskLabel.text = initalLabel
        taskLabel.textColor = UIColor.black
        
    }
}

目前我遇到的问题是无法将label重置回原始字符串状态。我做了演示,同时添加了多条打印语句辅助调试,可以看到initialLabel变量存储了正确的单元格值,但不知道为什么赋值不生效。
演示效果如下:
演示动图

请问为什么taskLabel无法正确显示原始字符串?

原因与解决方案

直接原因

你在重置样式时没有先清空UILabel的attributedText属性。当你给UILabel设置过富文本之后,直接赋值text属性不会自动清除之前的富文本属性,残留的删除线属性会导致文本样式不刷新。

临时修复方案

在else分支中先将attributedText置为nil,再赋值普通文本即可解决当前点击不生效的问题:

@IBAction func completedTask(_ sender: UISwitch) {
    let originalText = taskLabel.text ?? ""
    let attributeString: NSMutableAttributedString =  NSMutableAttributedString(string: originalText)
    attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 2, range: NSMakeRange(0, attributeString.length))
    
    if sender.isOn {
        taskLabel.textColor = .red
        taskLabel.attributedText = attributeString
    } else {
        // 先清空富文本,清除残留属性
        taskLabel.attributedText = nil
        taskLabel.text = originalText
        taskLabel.textColor = .black
    }
}

彻底解决方案

你当前的逻辑存在本质缺陷:TableView的单元格是复用的,你把状态和原始文本存在UI控件上,单元格滚动出屏幕再滚回时状态会错乱。你需要把状态存储在数据源中,具体实现如下:

  1. 定义任务模型,存储原始文本和完成状态
struct Task {
    let title: String
    var isCompleted: Bool
}
// 控制器内维护数据源数组
var tasks: [Task] = []
  1. 在cellForRowAt方法中根据数据源状态设置UI
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
    let task = tasks[indexPath.row]
    // 给单元格绑定当前行索引,方便点击开关时定位数据源
    cell.currentIndex = indexPath.row
    cell.taskSwitch.isOn = task.isCompleted
    
    if task.isCompleted {
        let attributeString = NSMutableAttributedString(string: task.title)
        attributeString.addAttribute(.strikethroughStyle, value: 2, range: NSRange(location: 0, length: attributeString.length))
        cell.taskLabel.attributedText = attributeString
        cell.taskLabel.textColor = .red
    } else {
        cell.taskLabel.attributedText = nil
        cell.taskLabel.text = task.title
        cell.taskLabel.textColor = .black
    }
    return cell
}
  1. 开关点击时先修改数据源,再刷新对应行
    在单元格子类中定义回调闭包,点击开关时通知控制器修改数据源:
// 单元格子类代码
class TaskCell: UITableViewCell {
    @IBOutlet weak var taskLabel: UILabel!
    @IBOutlet weak var taskSwitch: UISwitch!
    var currentIndex: Int = 0
    var switchStateChanged: ((Bool, Int) -> Void)?
    
    @IBAction func completedTask(_ sender: UISwitch) {
        switchStateChanged?(sender.isOn, currentIndex)
    }
}

控制器中绑定回调,刷新对应行:

// cellForRowAt中添加回调绑定
cell.switchStateChanged = { [weak self] isOn, index in
    self?.tasks[index].isCompleted = isOn
    self?.tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 14:45:04