How to stop UITableView moveRowAtIndexPath from leaving empty rows when reordering

I have a problem where when reordering my UITableViewCells, the tableView does not scroll with the cell. Only an empty line appears, and any subsequent scrolling gets an Array error outside the bounds without my Stack Trace code. Here is a video about the problem.

Here is the relevant code:

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    return indexPath.section == 1;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    BOOL ret = indexPath.section == 1 && indexPath.row < self.count;
    DebugLog(@"canMoveRowAtIndexPath: %d:%d %@", indexPath.section, indexPath.row, (ret ? @"YES" : @"NO"));
    return ret;
}
- (void)delayedUpdateCellBackgroundPositionsForTableView:(UITableView *)tableView {
    [self performSelectorOnMainThread:@selector(updateCellBackgroundPositionsForTableView:) withObject:tableView waitUntilDone:NO];
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
    if (fromIndexPath.row == toIndexPath.row) return;

    DebugLog(@"Moved audio from %d:%d to %d:%d", fromIndexPath.section, fromIndexPath.row, toIndexPath.section, toIndexPath.row);
    NSMutableArray *audio = [self.items objectAtIndex:fromIndexPath.section];
    [audio exchangeObjectAtIndex:fromIndexPath.row withObjectAtIndex:toIndexPath.row];
    [self performSelector:@selector(delayedUpdateCellBackgroundPositionsForTableView:) withObject:tableView afterDelay:kDefaultAnimationDuration/3];
}

And here is the generated trace of the crash stack:

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000002, 0x0000000000000000
Crashed Thread:  0  Dispatch queue: com.apple.main-thread

Application Specific Information:
iPhone Simulator 3.2 (193.3), iPhone OS 3.0 (7A341)
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray removeObjectsInRange:]: index (6) beyond bounds (6)'

Thread 0 Crashed:  Dispatch queue: com.apple.main-thread
0   CoreFoundation                  0x302ac924 ___TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION___ + 4
1   libobjc.A.dylib                 0x93cb2509 objc_exception_throw + 56
2   CoreFoundation                  0x3028e5fb +[NSException raise:format:arguments:] + 155
3   CoreFoundation                  0x3028e55a +[NSException raise:format:] + 58
4   Foundation                      0x305684e9 _NSArrayRaiseBoundException + 121
5   Foundation                      0x30553a6e -[NSCFArray removeObjectsInRange:] + 142
6   UIKit                           0x30950105 -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow] + 862
7   UIKit                           0x30947715 -[UITableView layoutSubviews] + 250
8   QuartzCore                      0x0090bd94 -[CALayer layoutSublayers] + 78
9   QuartzCore                      0x0090bb55 CALayerLayoutIfNeeded + 229
10  QuartzCore                      0x0090b3ae CA::Context::commit_transaction(CA::Transaction*) + 302
11  QuartzCore                      0x0090b022 CA::Transaction::commit() + 292
12  QuartzCore                      0x009132e0 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 84
13  CoreFoundation                  0x30245c32 __CFRunLoopDoObservers + 594
14  CoreFoundation                  0x3024503f CFRunLoopRunSpecific + 2575
15  CoreFoundation                  0x30244628 CFRunLoopRunInMode + 88
16  GraphicsServices                0x32044c31 GSEventRunModal + 217
17  GraphicsServices                0x32044cf6 GSEventRun + 115
18  UIKit                           0x309021ee UIApplicationMain + 1157
19  XXXXXXXX                        0x0000278a main + 104 (main.m:12)
20  XXXXXXXX                        0x000026f6 start + 54

NO, the length of the array is not equal to the length of my elements (I have 9), but always something less.

I am trying to solve this for many hours of days without any ... any ideas?



UPDATE: more code requested
In my delegate:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewCellEditingStyleNone;
}

- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
    int count = [(UAPlaylistEditDataSource *)self.dataSource count];
    if (proposedDestinationIndexPath.section == 0) {
        return [NSIndexPath indexPathForRow:0 inSection:sourceIndexPath.section];
    }else if (proposedDestinationIndexPath.row >= count) {
        return [NSIndexPath indexPathForRow:count-1 inSection:sourceIndexPath.section];
    }
    return proposedDestinationIndexPath;
}

... . three20, . updateCellBackgroundPositionsForTableView:, , .

+3
2

SO... . TTTableView, UITableView, :

///////////////////////////////////////////////////////////////////////////////////////////////////
// UIScrollView

- (void)setContentSize:(CGSize)size {
  if (_contentOrigin) {
    CGFloat minHeight = self.height + _contentOrigin;
    if (size.height < minHeight) {
      size.height = self.height + _contentOrigin;
    }
  }

  CGFloat y = self.contentOffset.y;
  [super setContentSize:size];

  if (_contentOrigin) {
    // As described below in setContentOffset, UITableView insists on messing with the 
    // content offset sometimes when you change the content size or the height of the table
    self.contentOffset = CGPointMake(0, y);
  }
}

- (void)setContentOffset:(CGPoint)point {
  // UITableView (and UIScrollView) are really stupid about resetting the content offset
  // when the table view itself is resized.  There are times when I scroll to a point and then
  // disable scrolling, and I don't want the table view scrolling somewhere else just because
  // it was resized.  
  if (self.scrollEnabled) {
    if (!(_contentOrigin && self.contentOffset.y == _contentOrigin && point.y == 0)) {
      [super setContentOffset:point];
    }
  }
}

, . , - -, , , , .

+1

:

, , , .

, . . , , .

, 1 2 , 1, 2, 2, tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: , , . , , .

, , ( 2), ! tableView:cellForRowAtIndexPath: . - , .

[tableView reloadData] tableView:moveRowAtIndexPath:toIndexPath:. , . : reloadData tableView:moveRowAtIndexPath:toIndexPath: , . , , no-op.

, :

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)pathSrc toIndexPath:(NSIndexPath *)pathDst
{
  // APPLE_BUG: after doing the delayed table reload (see below), we get a bogus
  // request to move a nonexistant cell to its current location
  if (pathSrc.row == pathDst.row && pathSrc.section == pathDst.section)
    return;

  // update your data model to reflect the move...

  // APPLE_BUG: if you move a cell to a row that off-screen (because the destination
  // has been modified), the bogus cell gets created and eventually will cause a crash
  [self performSelector:@selector(delayedReloadData:) withObject:tableView afterDelay:0];
}

- (void)delayedReloadData:(UITableView *)tableView
{
  Assert(tableView == self.tableView);
  [tableView reloadData];
}

, UI. . , , , .

. , , , ( ) . , , .

0

Source: https://habr.com/ru/post/1732300/


All Articles