如何在scrollToRowAtIndexPath完成动画时收到通知

And*_*rew 45 cocoa-touch ios

这是如何在tableViewController完成动画推送到导航堆栈时获得通知的后续内容.

在一个tableView我想要取消选择动画的行,但只有在tableView 完成动画滚动到所选行后.如何在发生这种情况时通知我,或者在完成的那一刻调用什么方法.

这是事情的顺序:

  1. 推视图控制器
  2. viewWillAppear我选择某一行.
  3. viewDidAppearscrollToRowAtIndexPath(到选定的行).
  4. 然后当我完成滚动时我想 deselectRowAtIndexPath: animated:YES

这样,用户就会知道为什么在那里滚动它们,但是我可以逐渐消失选择.
第4步是我尚未想到的部分.如果我viewDidAppear在tableView滚动时调用它,那么该行已被取消选择,这是不行的.

Jam*_*ton 73

您可以使用表视图委托的scrollViewDidEndScrollingAnimation:方法.这是因为a UITableView是a的子类UIScrollView并且UITableViewDelegate符合UIScrollViewDelegate.换句话说,表视图是滚动视图,表视图委托也是滚动视图委托.

因此,scrollViewDidEndScrollingAnimation:在表视图委托中创建一个方法,并取消选择该方法中的单元格.有关UIScrollViewDelegatescrollViewDidEndScrollingAnimation:方法的信息,请参阅参考文档.

  • 如果iOS提供带有完成块的版本(如`UIView animateWithDuration:animations:completion`),那将是很好的,因此通知可以是特定于上下文的...... (46认同)
  • 如果表视图决定不需要滚动动画(因为该行已经在屏幕上),该怎么办?如果不触发滚动动画方法,则永远不会取消选择该行. (25认同)
  • @BenPackard,你可以事先检查一下......`[myTableView.indexPathsForVisibleRows containsObject:myIndexPath]` (4认同)

Pun*_*roi 45

试试这个

[UIView animateWithDuration:0.3 animations:^{
    [yourTableView scrollToRowAtIndexPath:indexPath 
                         atScrollPosition:UITableViewScrollPositionTop 
                                 animated:NO];
} completion:^(BOOL finished){
    //do something
}];
Run Code Online (Sandbox Code Playgroud)

不要忘记将动画设置为NO,scrollToRow的动画将被UIView animateWithDuration覆盖.

希望这有帮助!

  • 小心使用这个解决方案:我正在使用这种方法一段时间,直到我看到滚动到的单元格中的奇怪行为.隐藏的图像将被显示......无法解释.查看调试将显示它们不可见.但他们是在设备上运行的.我将问题分离到scrollToRowAtIndexPath,动画块内部的动画设置为NO.将它移到动画块之外,没有问题.但是没有动画......所以这种技术会引起问题.我更新了使用James Huddleston的scrollViewDidEndScrollingAnimation解决方案来解决问题. (5认同)
  • 根据这个建议,我能够做我想做的事情,所以我想我拥有你一个:) (2认同)

eri*_*rik 13

要解决Ben Packard对已接受答案的评论,您可以这样做.测试tableView是否可以滚动到新位置.如果没有,请立即执行您的方法.如果它可以滚动,请等到滚动完成后执行您的方法.

- (void)someMethod
{
    CGFloat originalOffset = self.tableView.contentOffset.y;
    [self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
    CGFloat offset = self.tableView.contentOffset.y;

    if (originalOffset == offset)
    {
        // scroll animation not required because it's already scrolled exactly there
        [self doThingAfterAnimation];
    }
    else
    {
        // We know it will scroll to a new position
        // Return to originalOffset. animated:NO is important
        [self.tableView setContentOffset:CGPointMake(0, originalOffset) animated:NO];
        // Do the scroll with animation so `scrollViewDidEndScrollingAnimation:` will execute
        [self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
    }
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
    [self doThingAfterAnimation];
}
Run Code Online (Sandbox Code Playgroud)

  • 确实,如果indexPath*不可见,它肯定需要滚动才能正确定位,所以你可以在这种情况下跳过`contentOffset`摆弄.但是,如果indexPath**是**可见的,则tableView可能会或可能无法将其滚动到位,因此您应该检查contentOffsets(与上面显示的相同). (2认同)

Mr.*_*. T 8

您可以scrollToRowAtIndexPath:在一个[UIView animateWithDuration:...]块中包含一个块,该块将在所有包含的动画结束后触发完成块.所以,像这样:

[UIView
    animateWithDuration:0.3f
    delay:0.0f
    options:UIViewAnimationOptionAllowUserInteraction
    animations:^
    {
        // Scroll to row with animation
        [self.tableView scrollToRowAtIndexPath:indexPath
                            atScrollPosition:UITableViewScrollPositionTop
                                    animated:YES];
    }
    completion:^(BOOL finished)
    {
        // Deselect row
        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
    }];
Run Code Online (Sandbox Code Playgroud)

  • 我发现这个解决方案不幸会在滚动长列表时产生一些瑕疵:除滚动停止时可见的所有细胞都不会被绘制并显示为白色,从而导致闪烁. (3认同)

bso*_*sod 5

雨燕5

委托scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView)方法确实是执行滚动到行动画完成的最佳方法,但有两件事值得注意:

首先,文档错误地指出该方法仅在响应 和 时setContentOffset调用scrollRectToVisible;它也被称为响应scrollToRowhttps://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619379-scrollviewdidendscrollinganimati)。

其次,尽管该方法是在主线程上调用的,但如果您在这里运行后续动画(滚动完成后的动画),它仍然会卡住(这可能是也可能不是 UIKit 中的错误)。因此,只需将任何后续动画分派回主队列即可确保动画将在当前主任务(似乎包括滚动到行动画)结束后开始。这样做会给你一种真正完成的感觉。

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
    DispatchQueue.main.async {
        // execute subsequent animation here
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 还有一个问题,那就是如果该行已经位于该位置,则该方法不会触发。仅供参考。 (4认同)