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

如何在UITableView的不同分区之间拖拽移动行?

解决UITableView跨分区拖拽行的崩溃问题

你的崩溃根源在于moveRowAt方法没有区分源分区和目标分区,默认只处理了从分区0到分区1的情况,而且没有考虑双向拖拽或者同一分区内的拖拽兼容,导致数组操作时出现索引越界。下面是完整的修复方案:

第一步:完善移动行的核心逻辑

修改moveRowAt方法,先判断源分区和目标分区,再对应操作不同的数组:

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    // 处理同一分区内的拖拽
    if sourceIndexPath.section == destinationIndexPath.section {
        let movedObject: CarouselOrder
        if sourceIndexPath.section == 0 {
            movedObject = displayedOrderList.remove(at: sourceIndexPath.row)
            displayedOrderList.insert(movedObject, at: destinationIndexPath.row)
        } else {
            movedObject = hiddenOrderList.remove(at: sourceIndexPath.row)
            hiddenOrderList.insert(movedObject, at: destinationIndexPath.row)
        }
    } else {
        // 处理跨分区拖拽:分区0 ↔ 分区1
        let movedObject: CarouselOrder
        if sourceIndexPath.section == 0 {
            // 从显示列表拖到隐藏列表
            movedObject = displayedOrderList.remove(at: sourceIndexPath.row)
            hiddenOrderList.insert(movedObject, at: destinationIndexPath.row)
        } else {
            // 从隐藏列表拖回显示列表
            movedObject = hiddenOrderList.remove(at: sourceIndexPath.row)
            displayedOrderList.insert(movedObject, at: destinationIndexPath.row)
        }
    }
    // 注意:这里不需要手动调用reloadData,UITableView会自动更新UI
}

第二步:补充分区1的单元格配置

你的cellForRowAt只处理了分区0的情况,当拖拽到分区1后,加载该行数据时会直接崩溃,所以必须补充分区1的单元格配置:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: SRCarouselOrderTableViewCell.cellIdentifier, for: indexPath) as! SRCarouselOrderTableViewCell
    
    switch indexPath.section {
    case 0:
        let order = displayedOrderList[indexPath.row]
        cell.configureCell(name: order.name!, image: (categoryEnum(rawValue: order.category!)?.getIconCategory())!)
        // 可以恢复显示分区单元格的默认样式
        cell.textLabel?.textColor = .black
    case 1:
        let order = hiddenOrderList[indexPath.row]
        cell.configureCell(name: order.name!, image: (categoryEnum(rawValue: order.category!)?.getIconCategory())!)
        // 给隐藏分区的单元格加样式区分,比如灰色文字
        cell.textLabel?.textColor = .gray
    default:
        // 处理其他分区的默认情况,避免崩溃
        cell.textLabel?.text = "默认内容"
    }
    return cell
}

第三步:配置拖拽辅助方法(优化体验)

为了让跨分区拖拽更严谨,建议实现以下两个方法:

1. 允许指定分区的行被拖拽

func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
    // 只允许分区0和1的行被拖拽
    return indexPath.section == 0 || indexPath.section == 1
}

2. 限制拖拽的目标分区

避免用户把行拖到无效分区,确保拖拽只在目标两个分区内进行:

func tableView(_ tableView: UITableView, targetIndexPathForMoveRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {
    // 只允许拖到分区0或1,其他分区返回源位置
    if proposedDestinationIndexPath.section != 0 && proposedDestinationIndexPath.section != 1 {
        return sourceIndexPath
    }
    return proposedDestinationIndexPath
}

崩溃原因复盘

你之前的代码默认认为源分区一定是0、目标分区一定是1,但实际拖拽时:

  • 如果尝试从分区1拖回分区0,代码会错误地去displayedOrderList中取分区1的行数据,直接触发索引越界;
  • 即使是从0到1的拖拽,若目标行索引超出hiddenOrderList的当前长度(比如拖到最后一行之后),也可能引发数组操作错误。

通过上面的修改,就能覆盖所有拖拽场景,彻底解决索引越界的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:12:18