【问题标题】:UIScrollView change contentInset while scrollingUIScrollView 在滚动时更改 contentInset
【发布时间】:2019-05-25 01:43:31
【问题描述】:

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

不同之处在于我的栏需要自定义高度以及滚动时不会折叠的底部。我设法让那部分工作:

该栏是使用 UIStackView 实现的,并带有一些非必需的布局约束,但我认为它的内部实现不相关。最重要的是bar的高度与scrollview的顶部contentInset相关联。这些是由滚动视图的contentOffsetUIScrollViewDelegate.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 存储最小和最大条形高度

我遇到的一个问题是,当用户停止滚动滚动视图时,栏可能最终处于半折叠状态。再次,让我们看看所需的行为:

内容偏移量捕捉到紧凑或展开高度,以“更接近”为准。我试图在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)")
}

效果远非完美:

当我查看打印输出时,它显示targetContentOffset 已正确修改。然而,在应用程序中,视觉上的内容偏移量仅捕捉到紧凑高度而不是展开高度(您可以观察到大的“标题”标签最终被切成两半而不是回到“展开”位置。

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

解释这个问题有点困难,所以我希望 GIF 能有所帮助。这是回购:https://github.com/AleksanderMaj/ScrollView

任何想法如何使滚动视图/栏组合正确对齐到紧凑/展开高度?

【问题讨论】:

  • UidynamicItem 可能是你需要实现的
  • 谢谢。我对 UIDynamics 的了解非常模糊。您能否简要(至少在概念上)描述这种实现将如何工作?
  • @AleksanderMaj 你有没有想过解决这个问题?

标签: ios uiscrollview uikit uiscrollviewdelegate


【解决方案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)")
}

【讨论】:

  • 嗯。这是一个有趣的想法。我希望有一个更优雅的解决方案,但你的解决方案可能会成功?还有一个问题我必须解决:当velocity 不接近于零时,条形图仍然卡在半折叠状态。这代表了当滚动视图被快速拖动时调用scrollViewWillEndDragging 方法并且还有很多减速要做的情况。我一定会努力完善这一点,并会在某个时候发布更新。
  • 谢谢,是的,这是一个简单的解决方案,而不是具体的解决方案,有时间我会尝试再看一下,如果您也想出更好的解决方案,请告诉我知道。新年快乐:))
猜你喜欢
  • 2011-01-02
  • 2017-01-20
  • 2018-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-29
  • 1970-01-01
相关资源
最近更新 更多