ios 当 scrollToRowAtIndexPath 完成动画时如何得到通知

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4356256/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 18:13:46  来源:igfitidea点击:

How to get notified when scrollToRowAtIndexPath finishes animating

cocoa-touchios

提问by Andrew

This is a follow-up to How to get notified when a tableViewController finishes animating the push onto a nav stack.

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

In a tableViewI want to deselect a row with animation, but only after the tableView has finishedanimating the scroll to the selected row. How can I be notified when that happens, or what method gets called the moment that finishes.

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

This is the order of things:

这是事情的顺序:

  1. Push view controller
  2. In viewWillAppearI select a certain row.
  3. In viewDidAppearI scrollToRowAtIndexPath(to the selected row).
  4. Then when that finishes scrolling I want to deselectRowAtIndexPath: animated:YES
  1. 推送视图控制器
  2. viewWillAppear我选择某一行。
  3. viewDidAppearI scrollToRowAtIndexPath(到所选行)。
  4. 然后当完成滚动时我想 deselectRowAtIndexPath: animated:YES

This way, the user will know why they were scrolled there, but then I can fade away the selection.
Step 4 is the part I haven't figured out yet. If I call it in viewDidAppearthen by the time the tableView scrolls there, the row has been deselected already which is no good.

这样,用户就会知道他们为什么在那里滚动,但随后我可以淡出选择。
第 4 步是我还没有弄清楚的部分。如果我调用它,viewDidAppear那么当 tableView 滚动到那里时,该行已经被取消选择,这是不好的。

回答by James Huddleston

You can use the table view delegate's scrollViewDidEndScrollingAnimation:method. This is because a UITableViewis a subclass of UIScrollViewand UITableViewDelegateconforms to UIScrollViewDelegate. In other words, a table view is a scroll view, and a table view delegate is also a scroll view delegate.

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

So, create a scrollViewDidEndScrollingAnimation:method in your table view delegate and deselect the cell in that method. See the reference documentation for UIScrollViewDelegatefor information on the scrollViewDidEndScrollingAnimation:method.

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

回答by Pung Worathiti Manosroi

try this

尝试这个

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

Don't forget to set animated to NO, the animation of scrollToRow will be overridden by UIView animateWithDuration.

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

Hope this help !

希望这有帮助!

回答by erik

To address Ben Packard's comment on the accepted answer, you can do this. Test if the tableView can scroll to the new position. If not, execute your method immediately. If it can scroll, wait until the scrolling is finished to execute your method.

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

回答by Mr. T

You can include the scrollToRowAtIndexPath:inside a [UIView animateWithDuration:...]block which will trigger the completion block after all included animations conclude. So, something like this:

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

回答by Drew

Implementing this in a Swift extension.

在 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)
        }
    }
}

This works for most cases although the TableView delegate is changed during the scroll, which may be undesired in some cases.

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