如何知道 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 动画何时完成?的主要内容,如果未能解决你的问题,请参考以下文章

如何知道 UIView 动画何时全部完成

如何知道 UICollectionView 何时完成布局?

UITableView/UIScrollView 如何自动知道 ContentSize 何时发生变化?

如何检测 UINavigationController 动画何时完成?

如何可靠地确定 UICollectionView 布局何时完成动画

如何知道 UITableView 已完成将数据加载到单元格中? [复制]