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

UITableView行重排序时最后一行消失:是框架Bug还是预期行为?

UITableView Reordering Bug: Unexpected Cell Disappearance When Reverting to Source Index

Great question—this is a super tricky edge case with UITableView's internal reordering logic that’s not well-documented, so your detailed reproduction steps and analysis are spot-on. Let’s break this down:

Confirming the Bug Behavior

First, let’s validate your observations: this is not expected behavior from UITableView. The issues you’re seeing—last cell disappearing without didEndDisplaying being called, scroll-induced blank areas, and eventual state recovery—all point to the table view’s layout and cell reuse system getting confused by the sudden index path reversal.

Root Cause Analysis

Your suspicion about proposedDestinationIndexPath needing to be monotonic is correct. Here’s why:

  • UITableView’s reordering logic relies on continuous, incremental changes to the target index path as the user drags. It maintains internal state to track the drag’s progression, updating cell positions and visibility incrementally.
  • When you first drag a row toward the bottom (returning proposed indices like 5, 6...14), the table view adjusts its layout to anticipate the drop. If you then suddenly revert back to the original source index (instead of a nearby valid index), the table view’s internal state can’t reconcile the jump. It assumes the row is moving in a single direction, so the sudden reversal breaks its visibility calculations—leading to the last cell being marked as off-screen even though it’s still in the bounds.

Official Documentation Context

Apple’s official docs for tableView(_:targetIndexPathForMoveFromRowAt:toProposedIndexPath:) don’t explicitly state that the return value must be monotonic, but the API’s design intent tells the story:

This method lets you modify the proposed target index path for a row being moved. Use it to restrict where rows can be placed.

The key here is "modify"—the framework expects you to adjust the proposed path to a nearby valid position, not jump back to the original source index. The method is meant to enforce boundaries, not abruptly reverse the drag’s direction.

Fix & Best Practice

As you discovered, the correct fix is to return the highest valid index (rows.count - 2) instead of the source index when the user tries to drag to the last row. This keeps the target index path changes incremental and continuous, which UITableView’s reordering system can handle correctly:

func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {
    if proposedDestinationIndexPath.row >= rows.count - 1 {
        // Return the highest valid index instead of sourceIndexPath
        return IndexPath(row: rows.count - 2, section: 0)
    } else {
        return proposedDestinationIndexPath
    }
}

This maintains the drag’s directional flow, so the table view never gets confused about cell positions or visibility.

Additional Notes

  • If you want to give visual feedback that the last row is an invalid drop target, you can combine this with a custom drag placeholder or highlight effect—this avoids confusing the user while keeping the table view’s state consistent.
  • Testing with your SpyCell is a great way to debug this kind of issue, as it lets you track when cells are actually removed from the view hierarchy vs. just marked as off-screen.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:07:44