ios - UIScrollView 滚动时更改 contentInset

标签 ios uiscrollview uikit uiscrollviewdelegate

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

iOS navigation bar

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

My custom bar

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

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 存储最小和最大条形高度

我遇到的一个问题是,当用户停止滚动 ScrollView 时,该栏可能最终会处于半折叠状态。再一次,让我们看看期望的行为: 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 能有所帮助。这是 repo 协议(protocol):https://github.com/AleksanderMaj/ScrollView

关于如何使 ScrollView /条形组合正确对齐压缩/扩展高度有什么想法吗?

最佳答案

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

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

关于ios - UIScrollView 滚动时更改 contentInset,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53938079/

相关文章:

ios - 在 MPMoviePlayerController 中全屏播放视频问题

c# - 如何在 Xamarin iOS 中绘制文本?

ios - 如何保存图像和其上绘制的线条的组合? swift

ios - 无尽的赛跑者 imageview 动画(无 SpriteKit)

ios - UIScrollView 或 UICollectionView 按从 firebase 调用的顺序滑动图像?

ios - 在 IOS 中滚动后如何获得 ScrollView 可见的矩形框?

swift - UIView 中神秘的 UIColor 行为以及 Swift 中的一般情况

ios - Apple B2B 应用程序如何使用兑换码通过 VPP 分发

ios - viewDidAppear 出现在代码中会扰乱布局

ios - 水平 UIPageViewController 内的垂直 UIScrollView 不滚动