这是How to get notified when a tableViewController finishes animating the push onto a nav stack.
的后续行动在tableView
中我想取消选择带动画的行,但只有在tableView 完成之后才能动画滚动到所选行。如何在发生这种情况时通知我,或者在完成的那一刻调用什么方法。
这是事情的顺序:
viewWillAppear
中,我选择了某一行。viewDidAppear
我scrollToRowAtIndexPath
(到选定的行)。deselectRowAtIndexPath: animated:YES
这样,用户就会知道为什么会在那里滚动它们,但是我可以逐渐淡出选择
第4步是我尚未想到的部分。如果我在viewDidAppear
中调用它,那么当tableView在那里滚动时,该行已被取消选择,这是不行的。
答案 0 :(得分:68)
您可以使用表格视图委托的scrollViewDidEndScrollingAnimation:
方法。这是因为UITableView
是UIScrollView
的子类,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)
委托方法的确是在滚动到行动画上执行补全的最佳方法,但有两点值得注意:
首先,文档错误地指出仅响应setContentOffset
和scrollRectToVisible
才调用此方法;响应scrollToRow
(https://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委托,在某些情况下这可能是不受欢迎的。