Установка contentOffset программно запускает scrollViewDidScroll - PullRequest
57 голосов
/ 23 февраля 2012

У меня есть несколько UIScrollView на странице. Вы можете прокручивать их независимо друг от друга или соединять вместе и прокручивать как единое целое. Проблема возникает, когда они заблокированы.

Я использую UIScrollViewDelegate и scrollViewDidScroll: для отслеживания движения. Я запрашиваю contentOffset из UIScrollView, который изменился, и затем отражаю изменение в других представлениях прокрутки, устанавливая их свойство contentOffset для соответствия.

Отлично .... кроме того, что я заметил много дополнительных звонков. Программное изменение contentOffset моих представлений прокрутки вызывает метод делегата scrollViewDidScroll:, который будет вызван. Я попытался использовать setContentOffset:animated: вместо этого, но я все еще получаю триггер на делегате.

Как программно изменить мои contentOffsets, чтобы не вызывать scrollViewDidScroll:?

Замечания по реализации .... Каждый UIScrollView является частью пользовательского UIView, который использует шаблон делегата для обратного вызова к представленному подклассу UIViewController, который обрабатывает координацию различных значений contentOffset.

Ответы [ 6 ]

105 голосов
/ 23 февраля 2012

Можно изменить смещение содержимого UIScrollView, не вызывая обратный вызов делегата scrollViewDidScroll:, установив границы UIScrollView с источником, установленным на желаемое смещение содержимого.

CGRect scrollBounds = scrollView.bounds;
scrollBounds.origin = desiredContentOffset;
scrollView.bounds = scrollBounds;
75 голосов
/ 25 июля 2013

Попробуйте

id scrollDelegate = scrollView.delegate;
scrollView.delegate = nil;
scrollView.contentOffset = point;
scrollView.delegate = scrollDelegate;

работал для меня.

37 голосов
/ 08 ноября 2014

А как насчет использования существующих свойств UIScrollView?

if(scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating) {
    //your code
}
6 голосов
/ 10 марта 2015

Упрощая ответ @ Тарка, вы можете расположить скроллвью без стрельбы scrollViewDidScroll в одну строку следующим образом:

scrollView.bounds.origin = CGPoint(x:0, y:100); // whatever values you'd like
5 голосов
/ 11 сентября 2014

Другой подход заключается в добавлении некоторой логики в ваш делегат scrollViewDidScroll, чтобы определить, было ли изменение смещения содержимого инициировано программно или прикосновением пользователя.

  • Добавить булеву переменную isManualScroll вваш класс.
  • Установите его начальное значение в false.
  • В scrollViewWillBeginDragging установите его в true.
  • В вашей проверке scrollViewDidScroll убедитесь, что это правда, и отвечайте только если оноis.
  • В scrollViewDidEndDecelerating установите для него значение false.
  • В scrollViewWillEndDragging добавьте логику, чтобы установить для него значение false, если скорость равна 0 (так как scrollViewDidEndDecelerating не будет вызываться в этом случае).
4 голосов
/ 01 апреля 2012

Это не прямой ответ на вопрос, но если вы получаете такие сообщения, которые кажутся фальшивыми, ТАКЖЕ ТАКЖЕ, потому что вы меняете границы.Я использую некоторый пример кода Apple с методом tilePages, который удаляет и добавляет подпредставление к просмотру прокрутки.Это нередко приводит к дополнительным scrollViewDidScroll: сообщениям, вызываемым немедленно, поэтому вы попадаете в рекурсию, которую вы наверняка не ожидали.В моем случае я получил неприятную невозможность найти сбой.

В итоге я поставил в очередь вызов в главной очереди:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if(scrollView == yourScrollView) {
        // dispatch fixes some recursive call to scrollViewDidScroll in tilePages (related to removeFromSuperView)
        // The reason can be found here: http://stackoverflow.com/questions/9418311
        dispatch_async(dispatch_get_main_queue(), ^{ [self tilePages]; });
    }
}
...