基于滚动视图在滚动上缩小导航栏

Shrink Navigation Bar on scroll based on a Scroll View

我在视图控制器中将滚动视图设置为固定高度。我想在顶部使用带有大标题的导航栏,所以当我滚动滚动视图时,它应该像在导航控制器中一样折叠。是否有可能做到这一点?我的场景是这样的:

导航栏对视图有 top/left/right 0 个约束。目前它正确地保持在顶部,但是它不会像预期的那样在滚动时折叠。

不要使用这样的 "loose" 导航栏。使用导航控制器,即使您不打算进行任何导航。它免费为您提供所需的行为。

最后我创建了一个自定义视图来复制导航栏。在这里您可以看到它的外观并阅读以下步骤进行复制:

  1. 要将您的视图控制器设置为与自定义滚动视图一起使用,请首先确保您的控制器使用的是自由格式尺寸。为此,select 在尺寸检查器中自由变形并将高度设置为新滚动视图的高度:

  2. 插入您的滚动视图并设置 0 top/left/right/bottom 约束,因此它将与您的视图控制器大小相同:

  3. 像往常一样将您的内容添加到滚动视图

  4. 现在要创建您的自定义导航栏,请在滚动视图之外添加一个视图并设置约束,如下所示:

    注意几点:

    • 顶部约束与超级视图而不是安全区域对齐,因此视图位于状态栏后面
    • 高度设置为>=44,所以它是最小高度,如果内容更大可以扩展
    • 在 Attribute Inspector 上,select 裁剪到边界,因此视图中的内容不会溢出(如 CSS、overflow:hidden)
    • 此时您可能会在 Storyboard 中看到一些错误,但不要担心:这是因为您的 View 中没有任何内容并且它不知道它应该有多高
  5. 将View背景设置为透明,并在里面添加一个"Visual Effect View with Blur",0个top/left/right/bottom约束。这会模糊自定义导航栏后面的内容

  6. 现在确保选中导航栏视图中的“安全区域布局指南”复选框(位于约束设置之上):

    这样您就可以在视图中添加不会在状态栏后面的内容,因为它在安全区域之外。它也适用于缺口。

  7. 在您的视图中添加一个标签,为安全区域设置顶部和底部约束,并确保您也定义了固定高度约束:

    现在您还可以看到 Storyboard 中的错误消失了 :) 此时一切应该是这样的:

  8. 现在是编码部分。在您的 ViewController 中,为 ScrollView 和自定义导航栏创建出口。为此,切换到助理编辑器(右上角的维恩图符号),select 故事板中的元素,按住 CTRL 并拖动到 ViewController class 中:

    对作为导航栏的视图执行相同操作:

     @IBOutlet weak var mainScrollView: UIScrollView!
     @IBOutlet weak var customNavigationBar: UIView!
    
  9. 接下来,您需要将UIScrollViewDelegate添加到您的class,这样您就可以监听滚动事件并获取当前滚动偏移的实际Y位置scrollViewDidScroll 函数:

     class ViewController: UIViewController, UIScrollViewDelegate {
    

    您还需要在 viewDidLoad 挂钩中设置委托:

    mainScrollView.delegate = self
    
  10. 创建一个名为 scrollViewDidScroll 的新函数来获取滚动位置,您可以使用它与其他元素一起制作各种动画。在这种情况下,如果滚动位置达到 44(这是我为我的自定义导航栏设置的高度),它将动画到完全不透明:

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let y = self.mainScrollView.contentOffset.y
        let barHeight = 44
    
        if(y < barHeight) {
            customNavigationBar.alpha = y/CGFloat(barHeight)
        } else {
            customNavigationBar.alpha = 1
        }
    }
    

    您可以使用相同的逻辑为导航栏内的标签设置动画,更改其大小等...

完整ViewController:

class ViewController: UIViewController, UIScrollViewDelegate {
    @IBOutlet weak var mainScrollView: UIScrollView!
    @IBOutlet weak var customNavigationBar: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        mainScrollView.delegate = self
        customNavigationBar.alpha = 0
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let y = self.mainScrollView.contentOffset.y
        let barHeight = 44

        if(y < 44) {
            customNavigationBar.alpha = y/CGFloat(barHeight)
        } else {
            customNavigationBar.alpha = 1
        }
    }

}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
        var height = CGFloat()
        if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0) {
            height = 130
        }
        else {
            height = 44
        }
        UIView.animate(withDuration: 0.5) {
            self.navBarHeightConstraint?.constant = height
            self.view.layoutIfNeeded()
        }
    }