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

时间:2010-12-04 22:49:05

标签: cocoa-touch ios

这是How to get notified when a tableViewController finishes animating the push onto a nav stack.

的后续行动

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

这是事情的顺序:

  1. 推送视图控制器
  2. viewWillAppear中,我选择了某一行。
  3. viewDidAppearscrollToRowAtIndexPath(到选定的行)。
  4. 然后,当完成滚动时,我想deselectRowAtIndexPath: animated:YES
  5. 这样,用户就会知道为什么会在那里滚动它们,但是我可以逐渐淡出选择 第4步是我尚未想到的部分。如果我在viewDidAppear中调用它,那么当tableView在那里滚动时,该行已被取消选择,这是不行的。

6 个答案:

答案 0 :(得分:68)

您可以使用表格视图委托的scrollViewDidEndScrollingAnimation:方法。这是因为UITableViewUIScrollView的子类,UITableViewDelegate符合UIScrollViewDelegate。换句话说,表视图是滚动视图,表视图委托也是滚动视图委托。

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

答案 1 :(得分:42)

试试这个

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

不要忘记将动画设置为NO,否则UIView animateWithDuration将覆盖scrollToRow的动画。

希望这有帮助!

答案 2 :(得分:12)

要解决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];
}

答案 3 :(得分:9)

您可以在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];
    }];

答案 4 :(得分:1)

快捷键5

scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView)委托方法的确是在滚动到行动画上执行补全的最佳方法,但有两点值得注意:

首先,文档错误地指出仅响应setContentOffsetscrollRectToVisible才调用此方法;响应scrollToRowhttps://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619379-scrollviewdidendscrollinganimati)也被调用。

第二,尽管实际上是在主线程上调用了该方法,但如果您在此处运行后续动画(滚动完成后一个动画),它仍然会出现故障(这可能是错误,也可能不是错误) UIKit)。因此,只需将所有后续动画重新分发到主队列中,就可以确保这些动画将在当前主任务(似乎包括滚动到行动画)结束之后开始。这样做将使您看起来很完整。

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
    DispatchQueue.main.async {
        // execute subsequent animation here
    }
}

答案 5 :(得分:0)

在Swift扩展中实现它。

//strong ref required
private var lastDelegate : UITableViewScrollCompletionDelegate! = nil

private class UITableViewScrollCompletionDelegate : NSObject, UITableViewDelegate {
    let completion: () -> ()
    let oldDelegate : UITableViewDelegate?
    let targetOffset: CGPoint
    @objc private func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
        scrollView.delegate = oldDelegate
        completion()
        lastDelegate = nil
    }

    init(completion: () -> (), oldDelegate: UITableViewDelegate?, targetOffset: CGPoint) {
        self.completion = completion
        self.oldDelegate = oldDelegate
        self.targetOffset = targetOffset
        super.init()
        lastDelegate = self
    }
}

extension UITableView {
    func scrollToRowAtIndexPath(indexPath: NSIndexPath, atScrollPosition scrollPosition: UITableViewScrollPosition, animated: Bool, completion: () -> ()) {
        assert(lastDelegate == nil, "You're already scrolling.  Wait for the last completion before doing another one.")
        let originalOffset = self.contentOffset
        self.scrollToRowAtIndexPath(indexPath, atScrollPosition: scrollPosition, animated: false)

        if originalOffset.y == self.contentOffset.y { //already at the right position
            completion()
            return
        }
        else {
            let targetOffset = self.contentOffset
            self.setContentOffset(originalOffset, animated: false)
            self.delegate = UITableViewScrollCompletionDelegate(completion: completion, oldDelegate: self.delegate, targetOffset:targetOffset)
            self.scrollToRowAtIndexPath(indexPath, atScrollPosition: scrollPosition, animated: true)
        }
    }
}

这适用于大多数情况,尽管在滚动期间更改了TableView委托,在某些情况下这可能是不受欢迎的。