我遇到一个问题,在重新排序我的UITableViewCells时,tableView不会滚动单元格。只显示一个空行,并且任何后续滚动都会在Stack Trace中没有任何代码的情况下获得Array out of bounds错误。 Here is a quick video of the problem.
以下是相关代码:
- (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];
}
这是生成崩溃的堆栈跟踪:
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
NOte表示数组越界长度不是我元素的长度(我有9),但总是更小。
我一直试图解决这个问题很多<罢工>小时天没有用...任何想法?
- (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:
方法中,因为当它被注释掉时仍会崩溃。
答案 0 :(得分:1)
所以......其实问题在于Three20框架。 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 :(得分:0)
将我的回答交叉发布到this related question:
我在我的应用中遇到了我认为相同的问题。
情况是我有两个表格部分。可以在部分内和部分之间拖动项目。用户可以将单元格拖动到第一部分中的任何行,但在第二部分中,项目会进行排序,因此对于任何给定的单元格,只有一行有效。
如果我滚动视图以便第1部分的底部和第2部分的顶部可见,请抓住第1部分中排序到第2部分底部的项目,然后将其拖到第2部分的顶部,调用我的tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:
方法,然后返回正确的目标位置,即屏幕底部下方的几行。在UI中,您可以看到在屏幕底部创建了一个空单元格,这不是正确的目标行。
当你放开细胞时,在屏幕底部(第2节中间)创建的那个假细胞就会停留在那里! tableView:cellForRowAtIndexPath:
甚至从未被要求过。一旦你尝试对那个单元格做任何事情,你就会崩溃。
我的第一个解决方案是在tableView:moveRowAtIndexPath:toIndexPath:
的末尾调用[tableView reloadData]。但这会导致崩溃,所以我会在延迟后间接调用它。但是那时还有另一个错误:在延迟的reloadData调用之后,tableView:moveRowAtIndexPath:toIndexPath:
再次被调用,并且伪造请求将第一个部分结束的项目移动到同一个位置。所以,我不得不添加代码来忽略虚假的无操作请求。
所以,这是代码:
- (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's 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错误。在屏幕上,拖动的单元格被动画化为虚假的空单元格。在动画结束时,使用该行的正确数据重新绘制空单元格,但是观察者会注意到拖动的单元格被动画化到错误的位置,然后立即变形到不同的单元格。
这绝对是一个愚蠢的用户界面。我考虑将正确的目的地行滚动到屏幕上,但如果我这样做,它将填满第二部分的屏幕,然后任何试图拖回第一部分的行为都会被我(现在讨厌的)自动滚动不断挫败。我可能不得不更改UI,但这需要对我的数据模型进行一些复杂而麻烦的更改。