UIScrollView рекурсия

В UIScrollViewDelegatescrollViewDidScroll(UIScrollView) функция, если вы измените кадр делегата UIScrollView, затем scrollViewDidScroll(UIScrollView) снова вызывается. Типичный пример этого - когда у вас есть заголовок, который сжимается при прокрутке пользователя вверх и расширяется при прокрутке пользователя вниз.

Помимо реализации, в которой вы сбрасываете и сбрасываете свойство делегата scrollView, как показано ниже, есть ли способы предотвратить рекурсию? Объект UIScrollView имеет isTracking а также isDragging свойства, которые перекрываются, а также остаются верными в следующем примере.

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let d = scrollView.delegate // Prevent recursion by setting the 
    scrollView.delegate = nil

    // Do stuff
    if someCondition == true {
        scrollView.contentOffset = somePoint
        scrollView.frame = someRect
    }

    scrollView.delegate = d // Restore the scrollView's delegate
}

ПРИМЕЧАНИЕ: мне не нужен учебник для создания складного заголовка, и мне не нужен простой, если проверка. Я только конкретно спрашиваю, какие варианты доступны для прерывания прокрутки событий в зависимости от их происхождения. Например, вы можете поставить это в начале scrollViewDidScroll:

if(!scrollView.isTracking && !scrollView.isDragging) {
    // Break out if we got here through anything but a touch event
    return
}

1 ответ

Я просто попробовал, и настройка .contentOffset не вызывает рекурсии.

При перетаскивании содержимого делегат получает непрерывный scrollViewDidScroll сообщения... когда вы звоните:

scrollView.contentOffset = CGPoint(x: 0, y: 0)

вы все еще перетаскиваете, и содержимое продолжает сбрасываться до 0,0.

Измените свою функцию на это:

var i = 0

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    i += 1
    print("did scroll", i)
    scrollView.contentOffset = CGPoint.zero
}

и вы увидите, что он перестанет считать, когда вы прекратите перетаскивать, поэтому он не рекурсивный.

Возможно, вы действительно хотите использовать scrollViewDidEndDragging или же scrollViewDidEndDecelerating?

Редактировать:

Возможно, это будет работать лучше для вас. Имейте в виду, пока вы продолжаете перетаскивать в представлении прокрутки, scrollViewDidScroll() будет продолжать называться.

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    print("did end dragging", scrollView.contentOffset)
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    print("did scroll", scrollView.contentOffset)
    scrollView.setContentOffset(CGPoint.zero, animated: false)
}

Если вы посмотрите на вывод консоли, вы никогда не увидите больше 2 print() после того, как "перестанете перетаскивать".

Другие вопросы по тегам