Cocoa touch 如何在ScrollToRowatineXpath完成动画制作时获得通知

Cocoa touch 如何在ScrollToRowatineXpath完成动画制作时获得通知,cocoa-touch,ios,Cocoa Touch,Ios,这是一项后续行动 在tableView中,我想取消选择带有动画的行,但只有在tableView完成滚动到所选行的动画制作之后。发生这种情况时如何通知我,或者在完成时调用什么方法 事情是这样的: 推式视图控制器 在视图中将出现中,我选择某一行 在视图中显示IscrollToRowatinedexpath(到所选行) 然后,当滚动结束时,我想取消浏览索引路径:动画:是 这样,用户就可以知道为什么会在那里滚动,但我可以淡出选择。 第四步是我还没有弄明白的部分。如果我在视图中调用它,那么当tableVi

这是一项后续行动

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

事情是这样的:

  • 推式视图控制器
  • 视图中将出现
    中,我选择某一行
  • 视图中显示
    I
    scrollToRowatinedexpath
    (到所选行)
  • 然后,当滚动结束时,我想
    取消浏览索引路径:动画:是
  • 这样,用户就可以知道为什么会在那里滚动,但我可以淡出选择。

    第四步是我还没有弄明白的部分。如果我在
    视图中调用它,那么当tableView在那里滚动时,该行已经被取消选择,这是不好的。

    您可以使用table view委托的
    滚动视图显示滚动提示:
    方法。这是因为
    UITableView
    UIScrollView
    的子类,
    UITableViewDelegate
    符合
    UIScrollViewDelegate
    。换句话说,表视图是滚动视图,表视图委托也是滚动视图委托


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

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

    您可以在
    [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];
        }];
    
    试试这个

    [UIView animateWithDuration:0.3 animations:^{
        [yourTableView scrollToRowAtIndexPath:indexPath 
                             atScrollPosition:UITableViewScrollPositionTop 
                                     animated:NO];
    } completion:^(BOOL finished){
        //do something
    }];
    
    不要忘记将动画设置为否,scrollToRow的动画将被UIView animateWithDuration覆盖


    希望这有帮助

    在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委托,但在某些情况下可能是不需要的。

    Swift 5

    ScrollViewDiEndScrollingAnimation(\uScrollView:UIScrollView)
    delegate方法确实是对滚动到行动画执行完成的最佳方法,但有两件事值得注意:

    首先,文档错误地指出,此方法仅在响应
    setContentOffset
    scrollRectToVisible
    时调用;它也被调用以响应
    scrollToRow
    ()

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

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

    如果iOS提供一个带有完成块的版本(如
    UIView animateWithDuration:animations:completion
    ),那么该通知可以是特定于上下文的……如果表视图决定不需要滚动动画(因为行已经在屏幕上),该怎么办?如果滚动动画方法不启动,则永远不会取消选择该行。@BenPackard,您可以事先检查一下
    [myTableView.indexPathsForVisibleRows containsObject:myIndexPath]
    该方法在iOS 7上未调用。1使用
    [self.tableView.indexPathsForVisibleRows containsObject:path]
    如何?如果indexPath不可见,则肯定需要滚动以正确定位,因此,在这种情况下,您可以跳过
    contentOffset
    fiddling。但是,如果indexPath可见,tableView可能无法将其滚动到位,因此您应该检查ContentOffset(如上所示)。聪明:)尽管这会覆盖ScrollToRowatineXpath的默认动画持续时间?正确,它会覆盖默认持续时间,但取决于您设置的值,它可以让你的应用程序表现得清晰/独特(这可能是一件好事)或者,您可以找到一个非常接近默认值的值,用户几乎无法识别。我发现此解决方案在滚动长列表时不幸会导致一些瑕疵:滚动停止时,除单元格外的所有单元格都不显示,并显示为白色,导致闪烁。对自身的弱引用(
    me
    )这也是不合理的。从iOS 9开始,如果使用
    0.00的动画
    duration
    NO
    animated
    ,滚动长列表似乎不会“闪烁”。基于这个建议,我可以做我想做的事情,所以我想我拥有你一个:)小心使用这个解决方案:我使用这个方法有一段时间了,直到我在滚动到的单元格中看到奇怪的行为。隐藏的图像将显示。。。无法解释。视图调试将显示它们不可见。但当它们在设备上运行时。我将问题隔离到ScrollToRowatineXpath,并将动画设置为动画块内部的NO。将其移到动画块外,不会出现问题。但是没有动画。。。因此,这种技术可能会导致问题。我更新了使用James Huddleston的ScrollViewDiEndScrollingAnimation解决方案来解决这个问题。在iOS13中,我看到这样做后cpu使用率超过了100%。还有动画