iOS/Swift - Hide/Show 滚动时的 UITabBarController down/up
iOS/Swift - Hide/Show UITabBarController when scrolling down/up
我对 iOS 开发还很陌生。现在我试图在向下滚动时隐藏我的标签栏,而在向上滚动时应该出现标签栏。我想以与导航栏相同的方式制作动画。对于导航栏,我只需单击属性检查器中的选项。我看到了一些工具栏的例子,但我不能采用它作为标签栏。
self.tabBarController?.tabBar.hidden = true
只是隐藏了我的标签栏,但它不像导航控制器那样具有动画效果。
您可以通过将 class 设置为 scrollView 的委托并在 scrollViewDidScroll:
方法中实现滚动来精确控制 UITabBar。
这是我如何在我的应用程序中执行此操作的示例。您可能可以根据需要轻松修改它。包含 UITabBar 的一些辅助函数。
#define LIMIT(__VALUE__, __MIN__, __MAX__) MAX(__MIN__, MIN(__MAX__, __VALUE__))
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat scrollOffset = scrollView.contentOffset.y;
CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;
CGFloat scrollOffsetGlobal = scrollOffset + scrollView.contentInset.top;
[self updateUITabBarY:[self UITabBarView].frame.origin.y + scrollDiff];
self.previousScrollViewYOffset = scrollOffset;
}
- (UITabBar*) UITabBarView
{
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
return (UITabBar*) view;
}
}
return nil;
}
- (void) updateUITabBarY:(CGFloat) y
{
UITabBar* tabBar = [self UITabBarView];
if(tabBar)
{
CGRect frame = tabBar.frame;
frame.origin.y = LIMIT(y, [self UITabBarMiny], [self UITabBarMaxY]);
tabBar.frame = frame;
}
}
- (CGFloat) UITabBarMiny
{
return [UIScreen mainScreen].bounds.size.height - [self UITabBarView].frame.size.height - [[UIApplication sharedApplication] statusBarFrame].size.height + 20.0f;
}
- (CGFloat) UITabBarMaxY
{
return [UIScreen mainScreen].bounds.size.height;
}
这是我实际在生产应用中使用的代码。
它在 Swift 中,它还会更新 UITabBar.hidden
var.
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0{
changeTabBar(hidden: true, animated: true)
}
else{
changeTabBar(hidden: false, animated: true)
}
}
您还可以使用其他回调方法:
func scrollViewDidScroll(scrollView: UIScrollView) {
...
}
但是如果您选择这样做,那么您必须处理对实际隐藏 tabBar 的辅助方法的多次调用。
然后您需要添加此方法来为 tabBar 的 hide/show 设置动画。
func changeTabBar(hidden:Bool, animated: Bool){
var tabBar = self.tabBarController?.tabBar
if tabBar!.hidden == hidden{ return }
let frame = tabBar?.frame
let offset = (hidden ? (frame?.size.height)! : -(frame?.size.height)!)
let duration:NSTimeInterval = (animated ? 0.5 : 0.0)
tabBar?.hidden = false
if frame != nil
{
UIView.animateWithDuration(duration,
animations: {tabBar!.frame = CGRectOffset(frame!, 0, offset)},
completion: {
println([=12=])
if [=12=] {tabBar?.hidden = hidden}
})
}
}
更新Swift4
func changeTabBar(hidden:Bool, animated: Bool){
guard let tabBar = self.tabBarController?.tabBar else { return; }
if tabBar.isHidden == hidden{ return }
let frame = tabBar.frame
let offset = hidden ? frame.size.height : -frame.size.height
let duration:TimeInterval = (animated ? 0.5 : 0.0)
tabBar.isHidden = false
UIView.animate(withDuration: duration, animations: {
tabBar.frame = frame.offsetBy(dx: 0, dy: offset)
}, completion: { (true) in
tabBar.isHidden = hidden
})
}
基于 Ariel 的回答,我更新了 Swift3 的代码。这对我的 collection 观看次数非常有用。
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 {
changeTabBar(hidden: true, animated: true)
}else{
changeTabBar(hidden: false, animated: true)
}
}
func changeTabBar(hidden:Bool, animated: Bool){
let tabBar = self.tabBarController?.tabBar
if tabBar!.isHidden == hidden{ return }
let frame = tabBar?.frame
let offset = (hidden ? (frame?.size.height)! : -(frame?.size.height)!)
let duration:TimeInterval = (animated ? 0.5 : 0.0)
tabBar?.isHidden = false
if frame != nil
{
UIView.animate(withDuration: duration,
animations: {tabBar!.frame = frame!.offsetBy(dx: 0, dy: offset)},
completion: {
print([=10=])
if [=10=] {tabBar?.isHidden = hidden}
})
}
}
这个答案是对 Ariel 答案的轻微修改,它在用户滚动时添加了动画。
extension ViewController:UIScrollViewDelegate{
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0{
//scrolling down
changeTabBar(hidden: true, animated: true)
}
else{
//scrolling up
changeTabBar(hidden: false, animated: true)
}
}
func changeTabBar(hidden:Bool, animated: Bool){
let tabBar = self.tabBarController?.tabBar
let offset = (hidden ? UIScreen.main.bounds.size.height : UIScreen.main.bounds.size.height - (tabBar?.frame.size.height)! )
if offset == tabBar?.frame.origin.y {return}
print("changing origin y position")
let duration:TimeInterval = (animated ? 0.5 : 0.0)
UIView.animate(withDuration: duration,
animations: {tabBar!.frame.origin.y = offset},
completion:nil)
}
}
根据@Ariel Hernández Amador 对隐藏 Tabbar 后黑屏的回答,只需在 ViewDidLoad() 中使用这行代码。工作出色...我已将此张贴在这里,因为我无法在那里发表评论。
viewDidLoad()
{
if #available(iOS 11.0, *) {
self.myScroll.contentInsetAdjustmentBehavior = .never
}
}
这里的 myScroll 是我在 VC 中使用的 Scrollview。只需将其替换为您的 VC.
Ariels 的回答有效,但有些值似乎不对。当您比较 scrollView scrollView.panGestureRecognizer.translation(in: scrollView).y
的 y 值时,“0”具有副作用,即当您停止滚动时 tabBar 显示或隐藏。它使用“0”值再次调用该方法。我用 didEndDragging、didScroll 和 willBeginDragging 尝试了类似的效果。这感觉非常违反直觉或错误。
我在比较 y 值时使用了 +/- 0.1 并获得了预期的效果,它只是在您真正向上或向下滚动时显示和隐藏。
另一件没有提到的事情是,当应用程序移至后台时,您使用 tabBar.frame = frame.offsetBy(dx: 0, dy: offset)
设置的偏移量将被重置。您向下滚动,tabBar 消失,您更改应用程序,再次打开它,tabBar 仍然隐藏但框架回到旧位置。因此,当再次调用该函数时,tabBar 会向上移动更多,并且您有 tabBar.frame.
大小的间隙
为了摆脱这种情况,我比较了当前帧位置并对 alpha 值进行了动画处理。我无法让通常的恢复动画工作,也许有人会尝试,不会那么难。不过这样也可以,因为这种事不常发生。
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let yValue = scrollView.panGestureRecognizer.translation(in: scrollView).y
if yValue < -0.1 {
//hide tabBar
changeTabBar(hidden: true, animated: true)
} else if yValue > 0.1 {
//show tabBar
changeTabBar(hidden: false, animated: true)
}
}
func changeTabBar(hidden:Bool, animated: Bool) {
guard let tabBar = self.tabBarController?.tabBar else {
return
}
if tabBar.isHidden == hidden{
return
}
let frame = tabBar.frame
let frameMinY = frame.minY //lower end of tabBar
let offset = hidden ? frame.size.height : -frame.size.height
let viewHeight = self.view.frame.height
//hidden but moved back up after moving app to background
if frameMinY < viewHeight && tabBar.isHidden {
tabBar.alpha = 0
tabBar.isHidden = false
UIView.animate(withDuration: 0.5) {
tabBar.alpha = 1
}
return
}
let duration:TimeInterval = (animated ? 0.5 : 0.0)
tabBar.isHidden = false
UIView.animate(withDuration: duration, animations: {
tabBar.frame = frame.offsetBy(dx: 0, dy: offset)
}, completion: { (true) in
tabBar.isHidden = hidden
})
}
我对 iOS 开发还很陌生。现在我试图在向下滚动时隐藏我的标签栏,而在向上滚动时应该出现标签栏。我想以与导航栏相同的方式制作动画。对于导航栏,我只需单击属性检查器中的选项。我看到了一些工具栏的例子,但我不能采用它作为标签栏。
self.tabBarController?.tabBar.hidden = true
只是隐藏了我的标签栏,但它不像导航控制器那样具有动画效果。
您可以通过将 class 设置为 scrollView 的委托并在 scrollViewDidScroll:
方法中实现滚动来精确控制 UITabBar。
这是我如何在我的应用程序中执行此操作的示例。您可能可以根据需要轻松修改它。包含 UITabBar 的一些辅助函数。
#define LIMIT(__VALUE__, __MIN__, __MAX__) MAX(__MIN__, MIN(__MAX__, __VALUE__))
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat scrollOffset = scrollView.contentOffset.y;
CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;
CGFloat scrollOffsetGlobal = scrollOffset + scrollView.contentInset.top;
[self updateUITabBarY:[self UITabBarView].frame.origin.y + scrollDiff];
self.previousScrollViewYOffset = scrollOffset;
}
- (UITabBar*) UITabBarView
{
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
return (UITabBar*) view;
}
}
return nil;
}
- (void) updateUITabBarY:(CGFloat) y
{
UITabBar* tabBar = [self UITabBarView];
if(tabBar)
{
CGRect frame = tabBar.frame;
frame.origin.y = LIMIT(y, [self UITabBarMiny], [self UITabBarMaxY]);
tabBar.frame = frame;
}
}
- (CGFloat) UITabBarMiny
{
return [UIScreen mainScreen].bounds.size.height - [self UITabBarView].frame.size.height - [[UIApplication sharedApplication] statusBarFrame].size.height + 20.0f;
}
- (CGFloat) UITabBarMaxY
{
return [UIScreen mainScreen].bounds.size.height;
}
这是我实际在生产应用中使用的代码。
它在 Swift 中,它还会更新 UITabBar.hidden
var.
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0{
changeTabBar(hidden: true, animated: true)
}
else{
changeTabBar(hidden: false, animated: true)
}
}
您还可以使用其他回调方法:
func scrollViewDidScroll(scrollView: UIScrollView) {
...
}
但是如果您选择这样做,那么您必须处理对实际隐藏 tabBar 的辅助方法的多次调用。
然后您需要添加此方法来为 tabBar 的 hide/show 设置动画。
func changeTabBar(hidden:Bool, animated: Bool){
var tabBar = self.tabBarController?.tabBar
if tabBar!.hidden == hidden{ return }
let frame = tabBar?.frame
let offset = (hidden ? (frame?.size.height)! : -(frame?.size.height)!)
let duration:NSTimeInterval = (animated ? 0.5 : 0.0)
tabBar?.hidden = false
if frame != nil
{
UIView.animateWithDuration(duration,
animations: {tabBar!.frame = CGRectOffset(frame!, 0, offset)},
completion: {
println([=12=])
if [=12=] {tabBar?.hidden = hidden}
})
}
}
更新Swift4
func changeTabBar(hidden:Bool, animated: Bool){
guard let tabBar = self.tabBarController?.tabBar else { return; }
if tabBar.isHidden == hidden{ return }
let frame = tabBar.frame
let offset = hidden ? frame.size.height : -frame.size.height
let duration:TimeInterval = (animated ? 0.5 : 0.0)
tabBar.isHidden = false
UIView.animate(withDuration: duration, animations: {
tabBar.frame = frame.offsetBy(dx: 0, dy: offset)
}, completion: { (true) in
tabBar.isHidden = hidden
})
}
基于 Ariel 的回答,我更新了 Swift3 的代码。这对我的 collection 观看次数非常有用。
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 {
changeTabBar(hidden: true, animated: true)
}else{
changeTabBar(hidden: false, animated: true)
}
}
func changeTabBar(hidden:Bool, animated: Bool){
let tabBar = self.tabBarController?.tabBar
if tabBar!.isHidden == hidden{ return }
let frame = tabBar?.frame
let offset = (hidden ? (frame?.size.height)! : -(frame?.size.height)!)
let duration:TimeInterval = (animated ? 0.5 : 0.0)
tabBar?.isHidden = false
if frame != nil
{
UIView.animate(withDuration: duration,
animations: {tabBar!.frame = frame!.offsetBy(dx: 0, dy: offset)},
completion: {
print([=10=])
if [=10=] {tabBar?.isHidden = hidden}
})
}
}
这个答案是对 Ariel 答案的轻微修改,它在用户滚动时添加了动画。
extension ViewController:UIScrollViewDelegate{
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0{
//scrolling down
changeTabBar(hidden: true, animated: true)
}
else{
//scrolling up
changeTabBar(hidden: false, animated: true)
}
}
func changeTabBar(hidden:Bool, animated: Bool){
let tabBar = self.tabBarController?.tabBar
let offset = (hidden ? UIScreen.main.bounds.size.height : UIScreen.main.bounds.size.height - (tabBar?.frame.size.height)! )
if offset == tabBar?.frame.origin.y {return}
print("changing origin y position")
let duration:TimeInterval = (animated ? 0.5 : 0.0)
UIView.animate(withDuration: duration,
animations: {tabBar!.frame.origin.y = offset},
completion:nil)
}
}
根据@Ariel Hernández Amador 对隐藏 Tabbar 后黑屏的回答,只需在 ViewDidLoad() 中使用这行代码。工作出色...我已将此张贴在这里,因为我无法在那里发表评论。
viewDidLoad()
{
if #available(iOS 11.0, *) {
self.myScroll.contentInsetAdjustmentBehavior = .never
}
}
这里的 myScroll 是我在 VC 中使用的 Scrollview。只需将其替换为您的 VC.
Ariels 的回答有效,但有些值似乎不对。当您比较 scrollView scrollView.panGestureRecognizer.translation(in: scrollView).y
的 y 值时,“0”具有副作用,即当您停止滚动时 tabBar 显示或隐藏。它使用“0”值再次调用该方法。我用 didEndDragging、didScroll 和 willBeginDragging 尝试了类似的效果。这感觉非常违反直觉或错误。
我在比较 y 值时使用了 +/- 0.1 并获得了预期的效果,它只是在您真正向上或向下滚动时显示和隐藏。
另一件没有提到的事情是,当应用程序移至后台时,您使用 tabBar.frame = frame.offsetBy(dx: 0, dy: offset)
设置的偏移量将被重置。您向下滚动,tabBar 消失,您更改应用程序,再次打开它,tabBar 仍然隐藏但框架回到旧位置。因此,当再次调用该函数时,tabBar 会向上移动更多,并且您有 tabBar.frame.
为了摆脱这种情况,我比较了当前帧位置并对 alpha 值进行了动画处理。我无法让通常的恢复动画工作,也许有人会尝试,不会那么难。不过这样也可以,因为这种事不常发生。
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let yValue = scrollView.panGestureRecognizer.translation(in: scrollView).y
if yValue < -0.1 {
//hide tabBar
changeTabBar(hidden: true, animated: true)
} else if yValue > 0.1 {
//show tabBar
changeTabBar(hidden: false, animated: true)
}
}
func changeTabBar(hidden:Bool, animated: Bool) {
guard let tabBar = self.tabBarController?.tabBar else {
return
}
if tabBar.isHidden == hidden{
return
}
let frame = tabBar.frame
let frameMinY = frame.minY //lower end of tabBar
let offset = hidden ? frame.size.height : -frame.size.height
let viewHeight = self.view.frame.height
//hidden but moved back up after moving app to background
if frameMinY < viewHeight && tabBar.isHidden {
tabBar.alpha = 0
tabBar.isHidden = false
UIView.animate(withDuration: 0.5) {
tabBar.alpha = 1
}
return
}
let duration:TimeInterval = (animated ? 0.5 : 0.0)
tabBar.isHidden = false
UIView.animate(withDuration: duration, animations: {
tabBar.frame = frame.offsetBy(dx: 0, dy: offset)
}, completion: { (true) in
tabBar.isHidden = hidden
})
}