我正在尝试实现一个自定义顶部栏,它的行为类似于 iOS 11+ 大标题导航栏,当向下滚动内容时,该栏的大标题部分会折叠:
不同之处在于我的栏需要一个自定义高度以及一个在滚动时不会折叠的底部部分。我设法让那部分工作:
该栏是使用 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 时,该栏可能最终会处于半折叠状态。再一次,让我们看看期望的行为:
内容偏移量与紧凑高度或展开高度对齐,以“更近”为准。我试图在 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 能有所帮助。这是 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/