UIScrollView изменить contentInset при прокрутке - PullRequest
0 голосов
/ 27 декабря 2018

Я пытаюсь реализовать настраиваемую верхнюю панель, которая работает аналогично панели навигации с большим заголовком в iOS 11+, где большой раздел заголовка на панели сворачивается при прокрутке содержимого вниз:

iOS navigation bar

Разница в том, что моему бару нужна настраиваемая высота, а также нижняя часть, которая не сворачивается при прокрутке.Мне удалось заставить эту часть работать:

My custom bar

Панель реализована с использованием UIStackView и с некоторыми необязательными ограничениями макета, но я считаю, что ее внутренняяреализация не актуальна.Самое главное, что высота панели привязана к вершине scrollview contentInset.Они управляются методом contentOffset в UIScrollViewDelegate.scrollViewDidScroll от scrollview:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let topInset = (-scrollView.contentOffset.y).limitedBy(topBarHeightRange)

    // changes both contentInset and scrollIndicatorInsets
    adjustTopContentInset(topInset)

    // changes top bar height
    heightConstraint?.constant = topInset

    adjustSmallTitleAlpha()
}

topBarHeightRange хранит минимальную и максимальную высоту бара

Одна проблема, с которой у меня возникает проблема, эточто, когда пользователь прекращает прокручивать вид прокрутки, возможно, что панель окажется в полусвернутом состоянии.Опять же, давайте посмотрим на желаемое поведение: iOS default bar details

Смещение содержимого привязывается к компактной или расширенной высоте, в зависимости от того, что ближе.Я пытаюсь добиться того же в методе UIScrollViewDelegate.scrollViewWillEndDragging:

func scrollViewWillEndDragging(_ scrollView: UIScrollView,
                               withVelocity velocity: CGPoint,
                               targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let targetY = targetContentOffset.pointee.y

    // snaps to a "closer" value
    let snappedTargetY = targetY.snappedTo([topBarHeightRange.lowerBound, topBarHeightRange.upperBound].map(-))

    targetContentOffset.pointee.y = snappedTargetY
    print("Snapped: \(targetY) -> \(snappedTargetY)")
}

Эффект далек от совершенства: My custom bar details

Когда я смотрю нараспечатка показывает, что targetContentOffset изменен правильно.Однако визуально в приложении смещение содержимого привязывается только к компактной высоте, но не к увеличенной высоте (вы можете заметить, что большая метка «Заголовок» в конечном итоге обрезается пополам, а не обратно в «расширенную» позицию.

Я подозреваю, что эта проблема связана с изменением contentInset.top во время прокрутки, но я не могу понять, как исправить это поведение.

Это немного сложно объяснитьпроблема, поэтому я надеюсь, что GIF-файлы помогут. Вот репозиторий: https://github.com/AleksanderMaj/ScrollView

Есть какие-нибудь идеи, как сделать комбо scrollview / bar привязанным к компактной / расширенной высоте должным образом?

1 Ответ

0 голосов
/ 27 декабря 2018

Я посмотрел на ваш проект и мне понравилась ваша реализация.

Я нашел решение в вашем методе scrollViewWillEndDragging, добавив следующий код в конце метода:

    if abs(targetY) < abs(snappedTargetY) {
        scrollView.setContentOffset(CGPoint(x: 0, y: snappedTargetY), animated: true)
    }

Обычно, если количество прокрутки не стоит скрыватьбольшой заголовок (это происходит, если targetY меньше snappedTargetY), затем просто прокрутите до значения snappedTargetY, чтобы отобразить большой заголовок.

Кажется, что работает на данный момент, но дайте мне знать, если вы обнаружите какие-либо ошибки или найдетеспособ улучшения.

Весь метод scrollViewWillEndDragging:

func scrollViewWillEndDragging(_ scrollView: UIScrollView,
                               withVelocity velocity: CGPoint,
                               targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let targetY = targetContentOffset.pointee.y

    // snaps to a "closer" value
    let snappedTargetY = targetY.snappedTo([topBarHeightRange.lowerBound, topBarHeightRange.upperBound].map(-))

    targetContentOffset.pointee.y = snappedTargetY

    if abs(targetY) < abs(snappedTargetY) {
        scrollView.setContentOffset(CGPoint(x: 0, y: snappedTargetY), animated: true)
    }

    print("Snapped: \(targetY) -> \(snappedTargetY)")
}
...