我有一个代表项目清单的UITableView。每个项目都处于完成/撤消状态。
我想将所有已完成的项目保留在列表顶部,因此当用户点击撤消的项目时,我想知道该行应该去哪里(在当前结束时)完成项目列表)并将其移动到那里。
我正在尝试使用-moveRowAtIndexPath:toIndexPath:
为我的UITableView执行此操作。
有时效果很好,其他时候也不太好。
当完成的动作在屏幕上的其他地方启动另一个动画时,它似乎运作良好。出于某种原因,这似乎是-reloadData
电话的延迟。
如果不是这样(即,“发生”的唯一事情是标记为已完成并正在移动的行),动画似乎通过自动调用UITableView的-reloadData
方法而短路。也就是说,动画开始,但大约一半时,调用-reloadData
并且行捕捉到它们的最终位置。从用户的角度来看,这是非常不和谐的。
我已经通过我的代码进行了跟踪,以确认我自己没有打电话给-reloadData
,而且似乎我没有触发此-reloadData
电话。
我可以自动调用-reloadData
,我理解为什么会调用它(尽管你认为它可能没有必要,但这是一个不同的问题),但我真的很喜欢它等到它完成动画。
这是我正在使用的代码:
NSIndexPath *oldPath = [NSIndexPath indexPathForRow:currentIndex inSection:0];
NSIndexPath *newPath = [NSIndexPath indexPathForRow:newIndex inSection:0];
[tableView beginUpdates];
[checklist removeObject:task];
[checklist insertObject:task atIndex:newIndex];
[tableView moveRowAtIndexPath:oldPath toIndexPath:newPath];
[tableView endUpdates];
我搞砸了什么?
答案 0 :(得分:1)
根据titaniumdecoy的要求,以下是我如何修复此问题:
我有一个UIViewController
子类,其NSTimer
每秒执行一次,检查基础数据源UITableView
是否有更改,表明对-reloadData
的调用是必要。所以,对于那个子类我添加了:
@property (BOOL) IsAnimating;
我最初将其设置为NO
,如果isAnimating
属性设置为YES
,则NSTimer
“短路”并跳过其正常处理。
因此,当我想运行此UITableView
动画时,我将isAnimating
属性设置为YES
并运行动画。
然后,我安排选择器在将来运行1秒,将isAnimating
重置为NO
。然后,NSTimer
将继续触发,并会看到isAnimating
NO
(最有可能在第二次调用-codeTimerFired
后),然后找到数据源更新,开始致电reloadData
。
以下是暂停NSTimer
处理和安排UITableView
动画的代码:
// currentIndex and newIndex have already been calculated as the data item's
// original and destination indices (only 1 section in UITableView)
if (currentIndex != newIndex) {
// This item's index has changed, so animate its movement
NSIndexPath *oldPath = [NSIndexPath indexPathForRow:currentIndex inSection:0];
NSIndexPath *newPath = [NSIndexPath indexPathForRow:newIndex inSection:0];
// Set a flag to disable data source processing and calls to [UITableView reloadData]
[[self Owner] setIsAnimating:YES];
// I haven't tested enough, but some documentation leads me to believe
// that this particular call (-moveRowAtIndexPath:toIndexPath:) may NOT need
// to be wrapped in a -beginUpdates/-endUpdates block
[[[self Owner] InstructionsTableView] beginUpdates];
// These lines move the item in my data source
[[[MMAppDelegate singleton] CurrentChecklist] removeObject:[self CellTask]];
[[[MMAppDelegate singleton] CurrentChecklist] insertObject:[self CellTask] atIndex:newIndex];
// This code is the UITableView animation
[[[self Owner] InstructionsTableView] moveRowAtIndexPath:oldPath toIndexPath:newPath];
// Conclude the UITableView animation block
[[[self Owner] InstructionsTableView] endUpdates];
// Schedule a call to re-enable UITableView animation
[[self Owner] performSelector:@selector(setIsAnimating:) withObject:@(NO) afterDelay:1.0];
} else {
// This location hasn't changed, so just tell my owner to reload its data
[[[self Owner] InstructionsTableView] reloadData];
}
以下是NSTimer
方法(注意isAnimating == YES
如何解决问题):
- (void)codeTimerFired {
// This is a subclass of a template subclass...
// super actually has work to do in this method...
[super codeTimerFired];
// If we're in the middle of an animation, don't update!
if ([self IsAnimating]) {
return;
}
// Other data source processing...
// local BOOL to check whether underlying data source has changed
BOOL shouldUpdate = NO;
// code to check if underlying data source has changed...
// ******************************************************
// [CODE REMOVED]
// ******************************************************
// If the underlying data source has changed, update the UITableView
if (shouldUpdate) {
[self reloadTableView]; // <--- This is the main line I wanted to prevent
// since the code that fired to cause the
// UITableView animation will ALWAYS cause
// the underlying data source to change such
// that this line would fire.
}
}
答案 1 :(得分:1)
<强>夫特强>
//---------
extension UITableView {
// Default delay time = 0.5 seconds
// Pass animation time interval, as a parameter argument
func reloadDataAfterDelay(delayTime: TimeInterval = 0.5) -> Void {
self.perform(#selector(self.reloadData), with: nil, afterDelay: delayTime)
}
}