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

Swift中TableView单元格选取消选时selectedNamesArray不匹配问题排查

问题描述

在Swift开发中,通过ViewController内的按钮实现UITableView单元格的选中与取消选中功能:

  • 初始时selectedRows数组已存储选中的单元格ID,页面加载后单元格选中状态显示正常
  • 但selectedNamesArray与selectedRows的索引始终不匹配,初始填充阶段就存在该问题
  • 进行选中/取消选中操作后,selectedRows的值正确,但selectedNamesArray的名称仍对应错误

相关代码及输出示例如下:

class ViewController2: UIViewController {
@IBOutlet weak var tableView: UITableView!

var selectedRows = [32, 38, 89, 11]
var selectedNamesArray: [String] = []

var arrayModel: [SampleModel] = [SampleModel(id: 32, name: "Apple"),
                                 SampleModel(id: 38, name: "Smasung"),
                                 SampleModel(id: 22, name: "Black berry"),
                                 SampleModel(id: 76, name: "Nokia"),
                                 SampleModel(id: 12, name: "Real me"),
                                 SampleModel(id: 75, name: "Lenova"),
                                 SampleModel(id: 36, name: "Pocco"),
                                 SampleModel(id: 11, name: "Motorola"),
                                 SampleModel(id: 37, name: "Infinix"),
                                 SampleModel(id: 88, name: "China"),
                                 SampleModel(id: 89, name: "Jio")
]

override func viewDidLoad() {
    super.viewDidLoad()
    
    if !selectedRows.isEmpty{
        selectedNamesArray = arrayModel
            .filter { selectedRows.contains($0.id) }
            .map { $0.name }
    }
    print("existing names: \(selectedNamesArray) \n and ids: \(selectedRows)")
}
}

extension ViewController2: UITableViewDelegate, UITableViewDataSource{

func numberOfSections(in tableView: UITableView) -> Int {
    2
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    
        return arrayModel.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableviewCell1", for: indexPath) as? TestTableviewCell1
    cell?.selectionStyle = .none
    cell?.titleLbl?.text = arrayModel[indexPath.row].name
    cell?.checkBtn.tag = indexPath.row
    
    cell?.isChecked = selectedRows.contains(arrayModel[indexPath.row].id)
    
    cell?.buttonAction = { [self] sender in
        
        if selectedRows.contains(arrayModel[indexPath.row].id) {
            if let indexToRemove = selectedRows.firstIndex(of: arrayModel[indexPath.row].id) {
                
                selectedRows.remove(at: indexToRemove)
                
                if indexToRemove < selectedNamesArray.count {
                    selectedNamesArray.remove(at: indexToRemove) // Remove corresponding name
                }
                cell?.isChecked = false
            }
        } else {
            selectedRows.append(arrayModel[indexPath.row].id)
            selectedNamesArray.append(arrayModel[indexPath.row].name) // Add the corresponding name
            cell?.isChecked = true
        }
        print("Selected rows: \(selectedRows)")
        print("Selected names: \(selectedNamesArray)")
    }
    return cell!
}
}

class TestTableviewCell1: UITableViewCell{

@IBOutlet weak var titleLbl: UILabel!
@IBOutlet weak var checkBtn: UIButton!
var isChecked = false{
    didSet{
        checkBtn.backgroundColor = isChecked ? .blue : .green
    }
}
var buttonAction: ((_ sender: AnyObject) -> Void)?
@IBAction func buttonPressed(_ sender: UIButton) {
    self.buttonAction?(sender)
}
}

struct SampleModel{
var id: Int
var name: String
}

输出示例

  • 初始输出:
existing names: ["Apple", "Smasung", "Motorola", "Jio"]
and ids: [32, 38, 89, 11] // 两者数组值索引不匹配
  • 操作后输出:
Selected rows: [32, 38, 89, 36]
Selected names: ["Apple", "Smasung", "Motorola", "Pocco"] // 索引再次不匹配
错误原因分析
  1. 初始填充顺序不匹配:
    初始化selectedNamesArray时,arrayModel.filter会按照arrayModel本身的顺序筛选元素,而selectedRows的顺序是[32,38,89,11],但arrayModel中ID为89的元素在最后一位,ID为11的元素在第7位,因此筛选后的名称顺序是["Apple", "Smasung", "Motorola", "Jio"],和selectedRows的ID顺序完全对应不上,直接导致索引不匹配。

  2. 操作时同步逻辑错误:
    取消选中时,通过selectedRows的索引删除selectedNamesArray的元素,但两个数组顺序本就不一致,删除位置自然错误;选中时直接append名称,会延续这种顺序不匹配的问题。

  3. 双数组维护的设计缺陷:
    同时维护selectedRows(ID数组)和selectedNamesArray(名称数组)两个独立数据源属于冗余存储,极易出现同步不一致的问题,这是根本隐患。

解决方法

方案一:移除selectedNamesArray,动态获取名称(推荐)

不需要单独维护名称数组,当需要获取选中名称时,直接通过selectedRows从arrayModel中匹配获取,从根源避免同步问题。

修改后的核心代码:

// 移除selectedNamesArray变量
// var selectedNamesArray: [String] = []

override func viewDidLoad() {
    super.viewDidLoad()
    // 初始化时不再填充名称数组,需要时动态获取
    print("existing names: \(getSelectedNames()) \n and ids: \(selectedRows)")
}

// 添加获取选中名称的方法
func getSelectedNames() -> [String] {
    return selectedRows.compactMap { id in
        arrayModel.first(where: { $0.id == id })?.name
    }
}

// 单元格操作逻辑中移除名称数组的维护
cell?.buttonAction = { [self] sender in
    let currentId = arrayModel[indexPath.row].id
    if let indexToRemove = selectedRows.firstIndex(of: currentId) {
        selectedRows.remove(at: indexToRemove)
        cell?.isChecked = false
    } else {
        selectedRows.append(currentId)
        cell?.isChecked = true
    }
    print("Selected rows: \(selectedRows)")
    print("Selected names: \(getSelectedNames())")
}

方案二:强制同步两个数组的顺序(如需保留名称数组)

如果必须保留selectedNamesArray,需要确保它和selectedRows的顺序完全一致:

  1. 初始化时按selectedRows的顺序匹配名称:
override func viewDidLoad() {
    super.viewDidLoad()
    if !selectedRows.isEmpty{
        // 按selectedRows的顺序逐个查找对应名称
        selectedNamesArray = selectedRows.compactMap { id in
            arrayModel.first(where: { $0.id == id })?.name
        }
    }
    print("existing names: \(selectedNamesArray) \n and ids: \(selectedRows)")
}
  1. 操作时严格同步两个数组的增删:
cell?.buttonAction = { [self] sender in
    let currentId = arrayModel[indexPath.row].id
    let currentName = arrayModel[indexPath.row].name
    if let indexToRemove = selectedRows.firstIndex(of: currentId) {
        selectedRows.remove(at: indexToRemove)
        // 同步删除对应索引的名称
        if indexToRemove < selectedNamesArray.count {
            selectedNamesArray.remove(at: indexToRemove)
        }
        cell?.isChecked = false
    } else {
        selectedRows.append(currentId)
        // 同步添加名称到对应位置
        selectedNamesArray.append(currentName)
        cell?.isChecked = true
    }
    print("Selected rows: \(selectedRows)")
    print("Selected names: \(selectedNamesArray)")
}

修改后效果

初始输出会变成:

existing names: ["Apple", "Smasung", "Jio", "Motorola"]
and ids: [32, 38, 89, 11]

此时两个数组的索引完全匹配,后续操作也会保持同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 21:56:04