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
相关产品推荐
相关产品推荐

