Подтвердить что ты не робот

Как получить уведомление, когда scrollToRowAtIndexPath завершает анимацию

Это продолжение Как получить уведомление, когда tableViewController завершит анимацию нажатия на стек nav.

В tableView я хочу отменить выделение строки с анимацией, но только после того, как tableView завершил анимацию прокрутки до выбранной строки. Как я могу получить уведомление, когда это произойдет, или какой метод вызывается в момент завершения.

Это порядок вещей:

  • Нажмите контроллер просмотра
  • В viewWillAppear я выбираю определенную строку.
  • В viewDidAppear я scrollToRowAtIndexPath (к выбранной строке).
  • Затем, когда заканчивается прокрутка, я хочу deselectRowAtIndexPath: animated:YES

Таким образом, пользователь будет знать, почему они были прокручены туда, но затем я могу угаснуть выбор.
Шаг 4 - это часть, которую я еще не понял. Если я назову его в viewDidAppear, то к тому моменту, когда прокрутится таблицаView, строка уже отменена, что не подходит.

4b9b3361

Ответ 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 изменяется во время прокрутки, что может быть нежелательным в некоторых случаях.