【问题标题】:How to know when a UITableView animation is completed?如何知道 UITableView 动画何时完成?
【发布时间】:2009-08-27 07:56:38
【问题描述】:

我想做一系列事情来响应某些 UITableView 动画的结束。例如,我希望表格视图单元格在通过 scrollToRowAtIndexPath 滚动到之后突出显示(通过 selectRowAtIndexPath)。

如何做到这一点?

【问题讨论】:

    标签: iphone uikit


    【解决方案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 答案以获得更可靠的方法:stackoverflow.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,你有什么不同?它对我以及其他在这里发表评论的人都有效。请记住,这是一个替代解决方案,而不是全部。例如,请参阅此链接以获取另一种选择 stackoverflow.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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多