如何知道 UITableView 动画何时完成?
Posted
技术标签:
【中文标题】如何知道 UITableView 动画何时完成?【英文标题】:How to know when a UITableView animation is completed? 【发布时间】:2009-08-27 07:56:38 【问题描述】:我想做一系列事情来响应某些 UITableView 动画的结束。例如,我希望表格视图单元格在通过 scrollToRowAtIndexPath 滚动到之后突出显示(通过 selectRowAtIndexPath)。
如何做到这一点?
【问题讨论】:
【参考方案1】:基本模板:
[UIView animateWithDuration:0.2 animations:^
//do some animations, call them with animated:NO
completion:^(BOOL finished)
//Do something after animations finished
];
示例:滚动到第 100 行。完成后,获取该行的单元格,并将 tag=1 的单元格内容查看到 firstResponder:
[UIView animateWithDuration:0.2 animations:^
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:100 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
completion:^(BOOL finished)
//Do something after scrollToRowAtIndexPath finished, e.g.:
UITableViewCell *nextCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:100 inSection:0]];
[[nextCell.contentView viewWithTag:1] becomeFirstResponder];
];
【讨论】:
这是迄今为止关于“如何在 scrollToRowAtIndexPath 完成后执行 X?”的最简洁的答案。谢谢! 我不知道这个解决方案在 ios 4/5 上是如何工作的,但现在在 iOS 6.1.4 上,我注意到在 contentOffset 被 scrollToRowAtIndexPath 更改后,这个位置的单元格不可见,因此在几分之一秒内 tableview 完全空白,然后绘制单元格并按预期显示键盘。我猜 UITableView 不应该以这种方式使用,或者至少不会在这个版本的 iOS 中使用。使用 setContentOffset 而不是 scrollToRowAtIndexPath 会产生相同的结果。 我猜测它不再适用于 iOS 6,因为它改变了动画的时间。检查以下 SO 答案以获得更可靠的方法:***.com/a/12516056/908621【参考方案2】:我意识到这是一篇旧帖子,但我遇到了类似的问题,并创建了一个对我来说效果很好的解决方案。我应用了NSCookBook 上使用的技术来创建带有块的 UIAlertViews。我这样做的原因是因为我想使用内置动画而不是 UIView 的 + animateWithDuration:animations:completion:。随着 iOS 7 的变化,这些动画之间的差异更大。
您为 UITableView 创建一个类别,并在实现文件中创建一个内部私有类,该类将通过将块分配为您的 tableview 的委托来回调该块。问题是,在调用块之前,可以说原来的委托将“丢失”,因为新的委托是调用该块的对象。这就是为什么我在调用块以重新分配原始 UITableViewDelegate 时发出通知以发送消息。这段代码已经过测试,正在我这边工作。
// Header file
@interface UITableView (ScrollDelegateBlock)
-(void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(UITableViewScrollPosition)scrollPosition
animated:(BOOL)animated
scrollFinished:(void (^)())scrollFinished;
@end
// Implementation file
#import "UITableView+ScrollDelegateBlock.h"
#import <objc/runtime.h>
NSString *const BLOCK_CALLED_NOTIFICATION = @"BlockCalled";
@interface ScrollDelegateWrapper : NSObject <UITableViewDelegate>
@property (copy) void(^scrollFinishedBlock)();
@end
@implementation ScrollDelegateWrapper
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
if (self.scrollFinishedBlock)
[[NSNotificationCenter defaultCenter] postNotificationName:BLOCK_CALLED_NOTIFICATION object:nil];
self.scrollFinishedBlock();
@end
static const char kScrollDelegateWrapper;
static id<UITableViewDelegate>previousDelegate;
@implementation UITableView (ScrollDelegateBlock)
-(void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath
atScrollPosition:(UITableViewScrollPosition)scrollPosition
animated:(BOOL)animated
scrollFinished:(void (^)())scrollFinished
previousDelegate = self.delegate;
ScrollDelegateWrapper *scrollDelegateWrapper = [[ScrollDelegateWrapper alloc] init];
scrollDelegateWrapper.scrollFinishedBlock = scrollFinished;
self.delegate = scrollDelegateWrapper;
objc_setAssociatedObject(self, &kScrollDelegateWrapper, scrollDelegateWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(blockCalled:)
name:BLOCK_CALLED_NOTIFICATION
object:nil];
/*
* Assigns delegate back to the original delegate
*/
-(void) blockCalled:(NSNotification *)notification
self.delegate = previousDelegate;
[[NSNotificationCenter defaultCenter] removeObserver:self
name:BLOCK_CALLED_NOTIFICATION
object:nil];
@end
然后您可以像其他任何方法一样使用块调用该方法:
[self.tableView scrollToRowAtIndexPath:self.currentPath
atScrollPosition:UITableViewScrollPositionMiddle
animated:YES
scrollFinished:^
NSLog(@"scrollFinished");
];
【讨论】:
感谢您发布此信息。将其转换为我的收藏视图并完美运行。 快速提示:如果滚动视图已经在顶部,则不会触发完成块。需要先检查位置。 这行得通,但是它似乎忽略了滚动期间/之后的 heightForCell 。我在每个部分的底部都有一个较小的单元格,但是这种方法使较小的单元格与所有其他单元格的大小相同。 没有像我想象的那样工作。从未调用完成块。 @JasperPol,你有什么不同?它对我以及其他在这里发表评论的人都有效。请记住,这是一个替代解决方案,而不是全部。例如,请参阅此链接以获取另一种选择 ***.com/questions/7623771/…【参考方案3】:如果您想在触发 scrollToRowAtIndexPath 后执行操作。
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
你需要像这样创建一个 CAAnimation 指针
CAAnimation *myAnimation;
然后将delgate设置为self
myAnimation.delegate = self;
一旦你这样做了,下面这些委托方法应该会激活你可以放置你的代码的地方:
- (void)animationDidStart:(CAAnimation *)theAnimation
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
【讨论】:
以上是关于如何知道 UITableView 动画何时完成?的主要内容,如果未能解决你的问题,请参考以下文章
UITableView/UIScrollView 如何自动知道 ContentSize 何时发生变化?
如何检测 UINavigationController 动画何时完成?