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

Swift:如何通过自定义BarButtonItem选中TableView所有单元格并处理灰度化

实现UITableView全选/取消全选图片转灰度功能

没问题,我来帮你搞定这个全选功能!咱们一步步来,解决cell复用导致状态丢失的问题,同时实现BarButtonItem的全选/取消全选逻辑:

第一步:添加状态跟踪数组

UITableView的cell是复用的,直接操作cell的状态会在滚动后丢失,所以咱们需要一个数组来记录每一行的选中状态:

// 假设你原有的图片数组是images: [UIImage]?
var isSelectedArray: [Bool] = []

override func viewDidLoad() {
    super.viewDidLoad()
    // 初始化状态数组,和图片数组长度一致,默认未选中
    isSelectedArray = Array(repeating: false, count: images?.count ?? 0)
    
    // 添加全选BarButtonItem到导航栏
    let selectAllButton = UIBarButtonItem(title: "全选", style: .plain, target: self, action: #selector(selectAllTapped))
    navigationItem.rightBarButtonItem = selectAllButton
}

第二步:封装灰度图片处理函数

把你原来的灰度转换逻辑抽成单独函数,避免重复代码,也方便后续调用:

func grayscaleImage(from image: UIImage) -> UIImage? {
    guard let currentCGImage = image.cgImage else { return nil }
    let currentCIImage = CIImage(cgImage: currentCGImage)
    
    let filter = CIFilter(name: "CIColorMonochrome")
    filter?.setValue(currentCIImage, forKey: kCIInputImageKey)
    filter?.setValue(CIColor(red: 0.6, green: 0.6, blue: 0.6), forKey: "inputColor")
    filter?.setValue(1.0, forKey: "inputIntensity")
    
    guard let outputImage = filter?.outputImage else { return nil }
    let context = CIContext()
    
    if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
        return UIImage(cgImage: cgimg)
    }
    return nil
}

这里把字符串key换成系统定义的kCIInputImageKey,代码更规范哦。

第三步:修改cell点击事件,基于状态数组更新

原来的点击事件要更新状态数组,再刷新对应cell,避免复用导致状态丢失:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    cellLabelSelected.text = "Card Number \(indexPath.row) Selected "
    print(" selected \(indexPath.row)")
    
    // 更新状态数组
    isSelectedArray[indexPath.row] = true
    // 刷新当前cell,确保状态正确
    tableView.reloadRows(at: [indexPath], with: .automatic)
}

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    // 更新状态数组
    isSelectedArray[indexPath.row] = false
    // 刷新当前cell
    tableView.reloadRows(at: [indexPath], with: .automatic)
}

第四步:在cell配置时根据状态设置图片

这一步是核心!cell复用的时候,要根据状态数组来显示对应图片:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "SecondTableViewCell", for: indexPath) as! SecondTableViewCell
    
    guard let originalImage = images?[indexPath.row] else { return cell }
    
    if isSelectedArray[indexPath.row] {
        // 显示灰度图
        cell.cellImage.image = grayscaleImage(from: originalImage)
    } else {
        // 显示原图
        cell.cellImage.image = originalImage
    }
    
    // 同步cell的选中状态(如果需要系统默认的选中样式)
    cell.setSelected(isSelectedArray[indexPath.row], animated: false)
    return cell
}

第五步:实现全选按钮的点击逻辑

让BarButtonItem支持切换全选/取消全选状态,同时更新按钮标题:

@objc func selectAllTapped() {
    // 判断当前是否已经全选
    let allSelected = isSelectedArray.allSatisfy { $0 }
    
    if allSelected {
        // 取消全选
        isSelectedArray = Array(repeating: false, count: images?.count ?? 0)
        navigationItem.rightBarButtonItem?.title = "全选"
    } else {
        // 执行全选
        isSelectedArray = Array(repeating: true, count: images?.count ?? 0)
        navigationItem.rightBarButtonItem?.title = "取消全选"
    }
    
    // 刷新整个表格,应用状态变化
    tableView.reloadData()
}

额外优化:点击ImageView触发状态变化

如果你希望只有点击ImageView才触发灰度转换(而不是点击整个cell),可以给cell里的ImageView添加点击手势:

// 在你的SecondTableViewCell类中
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
    cellImage.addGestureRecognizer(tapGesture)
    cellImage.isUserInteractionEnabled = true
}

// 定义代理通知ViewController处理点击
weak var delegate: SecondTableViewCellDelegate?

@objc func imageTapped() {
    delegate?.imageViewTapped(in: self)
}

// 定义代理协议
protocol SecondTableViewCellDelegate: AnyObject {
    func imageViewTapped(in cell: SecondTableViewCell)
}

然后在ViewController里实现代理:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "SecondTableViewCell", for: indexPath) as! SecondTableViewCell
    cell.delegate = self
    // ... 其他配置代码
    return cell
}

// 实现代理方法
func imageViewTapped(in cell: SecondTableViewCell) {
    guard let indexPath = tableView.indexPath(for: cell) else { return }
    // 切换当前行的选中状态
    isSelectedArray[indexPath.row].toggle()
    tableView.reloadRows(at: [indexPath], with: .automatic)
    
    // 更新选中提示文本
    cellLabelSelected.text = isSelectedArray[indexPath.row] ? "Card Number \(indexPath.row) Selected " : "Card Number \(indexPath.row) Deselected "
}

这样就完美实现了你想要的功能,状态不会因为cell复用丢失,全选/取消全选也能正常工作啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:19:11