基于滚动视图在滚动上缩小导航栏
Shrink Navigation Bar on scroll based on a Scroll View
我在视图控制器中将滚动视图设置为固定高度。我想在顶部使用带有大标题的导航栏,所以当我滚动滚动视图时,它应该像在导航控制器中一样折叠。是否有可能做到这一点?我的场景是这样的:
导航栏对视图有 top/left/right 0 个约束。目前它正确地保持在顶部,但是它不会像预期的那样在滚动时折叠。
不要使用这样的 "loose" 导航栏。使用导航控制器,即使您不打算进行任何导航。它免费为您提供所需的行为。
最后我创建了一个自定义视图来复制导航栏。在这里您可以看到它的外观并阅读以下步骤进行复制:
要将您的视图控制器设置为与自定义滚动视图一起使用,请首先确保您的控制器使用的是自由格式尺寸。为此,select 在尺寸检查器中自由变形并将高度设置为新滚动视图的高度:
插入您的滚动视图并设置 0 top/left/right/bottom 约束,因此它将与您的视图控制器大小相同:
像往常一样将您的内容添加到滚动视图
现在要创建您的自定义导航栏,请在滚动视图之外添加一个视图并设置约束,如下所示:
注意几点:
- 顶部约束与超级视图而不是安全区域对齐,因此视图位于状态栏后面
- 高度设置为>=44,所以它是最小高度,如果内容更大可以扩展
- 在 Attribute Inspector 上,select 裁剪到边界,因此视图中的内容不会溢出(如 CSS、overflow:hidden)
- 此时您可能会在 Storyboard 中看到一些错误,但不要担心:这是因为您的 View 中没有任何内容并且它不知道它应该有多高
将View背景设置为透明,并在里面添加一个"Visual Effect View with Blur",0个top/left/right/bottom约束。这会模糊自定义导航栏后面的内容
现在确保选中导航栏视图中的“安全区域布局指南”复选框(位于约束设置之上):
这样您就可以在视图中添加不会在状态栏后面的内容,因为它在安全区域之外。它也适用于缺口。
在您的视图中添加一个标签,为安全区域设置顶部和底部约束,并确保您也定义了固定高度约束:
现在您还可以看到 Storyboard 中的错误消失了 :) 此时一切应该是这样的:
现在是编码部分。在您的 ViewController 中,为 ScrollView 和自定义导航栏创建出口。为此,切换到助理编辑器(右上角的维恩图符号),select 故事板中的元素,按住 CTRL 并拖动到 ViewController class 中:
对作为导航栏的视图执行相同操作:
@IBOutlet weak var mainScrollView: UIScrollView!
@IBOutlet weak var customNavigationBar: UIView!
接下来,您需要将UIScrollViewDelegate
添加到您的class,这样您就可以监听滚动事件并获取当前滚动偏移的实际Y位置scrollViewDidScroll 函数:
class ViewController: UIViewController, UIScrollViewDelegate {
您还需要在 viewDidLoad 挂钩中设置委托:
mainScrollView.delegate = self
创建一个名为 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()
}
}
我在视图控制器中将滚动视图设置为固定高度。我想在顶部使用带有大标题的导航栏,所以当我滚动滚动视图时,它应该像在导航控制器中一样折叠。是否有可能做到这一点?我的场景是这样的:
导航栏对视图有 top/left/right 0 个约束。目前它正确地保持在顶部,但是它不会像预期的那样在滚动时折叠。
不要使用这样的 "loose" 导航栏。使用导航控制器,即使您不打算进行任何导航。它免费为您提供所需的行为。
最后我创建了一个自定义视图来复制导航栏。在这里您可以看到它的外观并阅读以下步骤进行复制:
要将您的视图控制器设置为与自定义滚动视图一起使用,请首先确保您的控制器使用的是自由格式尺寸。为此,select 在尺寸检查器中自由变形并将高度设置为新滚动视图的高度:
插入您的滚动视图并设置 0 top/left/right/bottom 约束,因此它将与您的视图控制器大小相同:
像往常一样将您的内容添加到滚动视图
现在要创建您的自定义导航栏,请在滚动视图之外添加一个视图并设置约束,如下所示:
注意几点:
- 顶部约束与超级视图而不是安全区域对齐,因此视图位于状态栏后面
- 高度设置为>=44,所以它是最小高度,如果内容更大可以扩展
- 在 Attribute Inspector 上,select 裁剪到边界,因此视图中的内容不会溢出(如 CSS、overflow:hidden)
- 此时您可能会在 Storyboard 中看到一些错误,但不要担心:这是因为您的 View 中没有任何内容并且它不知道它应该有多高
将View背景设置为透明,并在里面添加一个"Visual Effect View with Blur",0个top/left/right/bottom约束。这会模糊自定义导航栏后面的内容
现在确保选中导航栏视图中的“安全区域布局指南”复选框(位于约束设置之上):
这样您就可以在视图中添加不会在状态栏后面的内容,因为它在安全区域之外。它也适用于缺口。
在您的视图中添加一个标签,为安全区域设置顶部和底部约束,并确保您也定义了固定高度约束:
现在您还可以看到 Storyboard 中的错误消失了 :) 此时一切应该是这样的:
现在是编码部分。在您的 ViewController 中,为 ScrollView 和自定义导航栏创建出口。为此,切换到助理编辑器(右上角的维恩图符号),select 故事板中的元素,按住 CTRL 并拖动到 ViewController class 中:
对作为导航栏的视图执行相同操作:
@IBOutlet weak var mainScrollView: UIScrollView! @IBOutlet weak var customNavigationBar: UIView!
接下来,您需要将
UIScrollViewDelegate
添加到您的class,这样您就可以监听滚动事件并获取当前滚动偏移的实际Y位置scrollViewDidScroll 函数:class ViewController: UIViewController, UIScrollViewDelegate {
您还需要在 viewDidLoad 挂钩中设置委托:
mainScrollView.delegate = self
创建一个名为 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()
}
}