Как получить уведомление, когда scrollToRowAtIndexPath завершает анимацию
Это продолжение Как получить уведомление, когда tableViewController завершит анимацию нажатия на стек nav.
В tableView
я хочу отменить выделение строки с анимацией, но только после того, как tableView завершил анимацию прокрутки до выбранной строки. Как я могу получить уведомление, когда это произойдет, или какой метод вызывается в момент завершения.
Это порядок вещей:
- Нажмите контроллер просмотра
- В
viewWillAppear
я выбираю определенную строку.
- В
viewDidAppear
я scrollToRowAtIndexPath
(к выбранной строке).
- Затем, когда заканчивается прокрутка, я хочу
deselectRowAtIndexPath: animated:YES
Таким образом, пользователь будет знать, почему они были прокручены туда, но затем я могу угаснуть выбор.
Шаг 4 - это часть, которую я еще не понял. Если я назову его в viewDidAppear
, то к тому моменту, когда прокрутится таблицаView, строка уже отменена, что не подходит.
Ответы
Ответ 1
Вы можете использовать метод делегата таблицы scrollViewDidEndScrollingAnimation:
. Это связано с тем, что UITableView
является подклассом UIScrollView
и UITableViewDelegate
соответствует UIScrollViewDelegate
. Другими словами, представление таблицы представляет собой представление прокрутки, а делегат представления таблицы также является делегатом представления прокрутки.
Итак, создайте метод scrollViewDidEndScrollingAnimation:
в делегате представления таблиц и отмените выбор ячейки в этом методе. Информацию о методе scrollViewDidEndScrollingAnimation:
см. В справочной документации для UIScrollViewDelegate
.
Ответ 2
попробуйте это
[UIView animateWithDuration:0.3 animations:^{
[yourTableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:NO];
} completion:^(BOOL finished){
//do something
}];
Не забудьте установить анимированный NO, анимация scrollToRow будет переопределена UIView animateWithDuration.
Надеюсь, что эта помощь!
Ответ 3
Чтобы обратиться к комментарию 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 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];
}
Ответ 4
Вы можете включить scrollToRowAtIndexPath:
внутри блока [UIView animateWithDuration:...]
, который запустит блок завершения после завершения всех включенных анимаций. Итак, что-то вроде этого:
__weak MYViewController *me = self;
[UIView
animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionAllowUserInteraction
animations:^
{
// Scroll to row with animation
[me.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
}
completion:^(BOOL finished)
{
// Deselect row
[me.tableView deselectRowAtIndexPath:indexPath animated:YES];
}];
Ответ 5
Реализация этого в расширении 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 изменяется во время прокрутки, что может быть нежелательным в некоторых случаях.