Установка contentOffset программно запускает scrollViewDidScroll

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

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

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

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

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

6 ответов


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

CGRect scrollBounds = scrollView.bounds;
scrollBounds.origin = desiredContentOffset;
scrollView.bounds = scrollBounds;

попробовать

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

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


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

if(scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating) {
    //your code
}

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

scrollView.bounds.origin = CGPoint(x:0, y:100); // whatever values you'd like

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

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

это не прямой ответ на вопрос, но если вы получаете то, что кажется ложными такими сообщениями, это также может быть потому, что вы меняете границы. Я использую некоторый пример кода Apple с методом "tilePages", который удаляет и добавляет subview в scrollview. Это редко приводит к дополнительному 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]; });
    }
}