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

