UIScrollView在滚动时更改contentInset

时间:2018-12-26 23:06:41

标签: ios uiscrollview uikit uiscrollviewdelegate

我正在尝试实现一个自定义顶部栏,其行为类似于iOS 11+大标题导航栏,该栏的大标题部分在向下滚动内容时会折叠:

iOS navigation bar

区别在于,我的栏需要自定义高度,并且底部需要滚动时不会塌陷。我设法使该部分正常工作:

My custom bar

该栏是使用UIStackView&实施的,并具有一些非必需的布局约束,但我相信其内部实现是不相关的。最重要的是,栏的高度与滚动视图的顶部contentInset相关联。这些是由contentOffset方法中的scrollview的UIScrollViewDelegate.scrollViewDidScroll驱动的:

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已正确修改。但是,在应用程序中,在视觉上,内容偏移仅捕捉到紧凑的高度,而不捕捉到扩展的高度(您可以观察到,较大的“标题”标签最终被切成两半,而不是切回到“扩展”位置。 / p>

我怀疑此问题与在用户滚动时更改contentInset.top有关,但是我不知道如何解决此问题。

很难解释这个问题,所以我希望GIF有所帮助。这是仓库:https://github.com/AleksanderMaj/ScrollView

有什么主意如何使scrollview / bar组合正确对齐到紧凑/扩展高度?

1 个答案:

答案 0 :(得分:1)

我看了看你的项目并喜欢你的实现。

我在您的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)")
}