iOS踩坑系列-2

争取做到每周一记,把平时遇到的问题都汇总整理。我觉得用博客写笔记,比自己在本地写笔记的优点就是:博客是要给别人看的,所以会不由自主的认真许多。

废话不多说,我们来看看今天会涉及到的几个问题

  • 下拉放大tableview的header
  • 改变statusBar的背景色
  • 动态改变UINavigationBar的alpha
  • 上滑隐藏tabBar和UINavigationBar,下滑显示

下拉放大tableview的header

这个在很多App都可以看到,特别是个人主页的页面。效果如下:

这里头部的实现我是用的是xib,然后加载到tableview的header。具体实现看这篇文章iOS踩坑记-1

思路:

1、默认情况如何tableview置顶了,这个时候下拉tableview,整个tableview会下移,但是这里是始终黏在顶部。如何实现呢?

我们知道uitableview是内置于scrollview的,置顶的时候如果下拉tableview,那么会导致contentoffset的y值为负,而tableview的header的y值默认始终是0,我们就看到tableview向下偏移。

这个时候我们只要不断改变tableview的header的的y值和contentoffset的y值始终相同,就可以保证tableview不会向下偏移了。

2、header随着下拉,高度在变长。这个很简单,我们只要实时的把header的高度进行改变即可。具体为每次scrollview滚动的时候,新的header.frame.size.height = -contentoffset.y + 原来header的高度。

代码实现如下:

- (void)viewDidLoad{
    self.headerViews = [[UIView alloc] initWithFrame:CGRectMake(0, 0,ScreenWidth , 348)];
    self.headerView = [SGMeHeaderView createView];
    self.headerView.frame = self.headerViews.frame;
    [self.headerViews addSubview:_headerView];
    self.tableView.tableHeaderView = self.headerViews;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat contentOffsetY = scrollView.contentOffset.y;
    if ( contentOffsetY < 0) {//向下拖动到,头部保持顶部一直置顶,并垂直放大
        CGRect headerViewframe = self.tableView.tableHeaderView.frame;
        CGFloat zoomHeight = - contentOffsetY + headerViewframe.size.height;
        headerViewframe.origin.y = contentOffsetY;
        headerViewframe.size.height = zoomHeight;
        self.headerView.frame = headerViewframe;

    }else{
        return;
    }
}

改变statusBar的背景色

有的时候我们不想要系统的默认的statusBar的背景色,我们想将其设置为和我们自己的App相同的颜色。 实现代码如下:

Objective-C
- (void)setStatusBarBackgroundColor:(UIColor *)color {

    UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];

    if ([statusBar respondsToSelector:@selector(setBackgroundColor:)]) {
        statusBar.backgroundColor = [UIColor redColor];
    }
}
Swift
func setStatusBarBackgroundColor(color: UIColor) {

    guard  let statusBar = UIApplication.sharedApplication().valueForKey("statusBarWindow")?.valueForKey("statusBar") as? UIView else {
        return
    }

    statusBar.backgroundColor = color
}
系统默认的两种背景色
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;//透明背景,字体黑色
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;//透明背景,字体白色

动态改变UINavigationBar的alpha

先看效果图:

思路:

监听scrollview的滚动,把contentoffset换算成alpha,从而动态改变navigationBar的alpha。

实现代码:
- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    self.navigationController.navigationBar.alpha = 0.0;
    self.navigationController.navigationBar.translucent = YES;
}
- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    self.navigationController.navigationBar.alpha = 1.0;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat currentOffsetY = scrollView.contentOffset.y;
    CGFloat alpha = 0;
    if (currentOffsetY <= 0) {//当tableview置顶的时候,下拉始终保持alph=0;
        alpha = 0;
    }else {//上拉
        alpha = 1 - ((self.headerView.h - 64 - currentOffsetY) / (self.headerView.h - 64));
    }
    self.navigationController.navigationBar.alpha = alpha;
  }
说明:

alpha = 1 - ((self.headerView.h - 64 - currentOffsetY) / (self.headerView.h - 64));

这行代码是关键代码。实现的效果就是,当向上滚动tableview的时候,直到navigationBar的下边缘和tableview的header下边缘对齐的时候,navigationBar的alph=1,64就是navigationBar加上statuBar的高度。 因为有navigationBar存在的时候,虽然透明度为0看不见,但是滚动条是从navigationBar的下边缘开始滚动的,也就是说此时contentoffset的y值为0的点并不是页面的顶部,而是navigationBar的下边缘。

因为此时tableview的header是置顶的,这就导致tableview的header的height始终比contentoffset的y值大64,所以上面要减去64.


上滑隐藏tabBar和UINavigationBar,下滑显示

要达到的效果就是不管用户是手指不离开屏幕按在上面滑动,还是滑动后离开屏幕,都要实现隐藏和显示效果。

只需要在scrollview的代理方法scrollViewDidScroll 中判断是上滑还是下滑,然后隐藏显示就完了。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    [super scrollViewDidScroll:(UIScrollView *)scrollView];
    CGFloat currentOffset = scrollView.contentOffset.y;
    static CGFloat lastOffset ;

    if (currentOffset - lastOffset < 0 || currentOffset == -64) { //向下滚动,或者当tableview已经紧挨着navigationBar的底部的时候,再向下拉tableview的时候,显示navigationBar和TabBar,设置statusBar不透明
        [self.navigationController setNavigationBarHidden:NO animated:YES];
        [ UIView animateWithDuration:2.0 animations:^{
            self.tabBarController.tabBar.hidden = NO;
        }];

    }
    else{//向上滚动,隐藏navigationBar和TabBar,设置statusBar透明
        [self.navigationController setNavigationBarHidden:YES animated:YES];
        [ UIView animateWithDuration:2.0 animations:^{
            self.tabBarController.tabBar.hidden = YES;
        }];

    }
    lastOffset = currentOffset;

}

上面的代码是直接隐藏和显示navigationBar和tabBar的,这样看起来很突兀。好的体验应该是渐渐隐藏和显示他们。更准确的说因该是改变他们的y值,把他们推倒屏幕外面,或者拉回到屏幕。但是他们一直在,只是不再屏幕上看不见而已。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    [super scrollViewDidScroll:(UIScrollView *)scrollView];
    CGFloat currentOffset = scrollView.contentOffset.y;
    static CGFloat lastOffset ;

    if (currentOffset <= -64) {//此时tableview挨着navigationBar的下边缘,再向下拉tableview,不应该改变navigationBar和tabBar的y值。
        return;
    }

    if (currentOffset - lastOffset < 0 ) { //向下滚动,显示navigationBar和TabBar。设置statusBar不透明

        navBarY += fabs((marginOffset));
        if (navBarY >=20) {//下移navigationBar到y=20的时候,不准再下移。这个是navigationBar正好贴在顶部,20是statusBar的高度。
            navBarY =20;
        }

        tabBarY -= fabs((marginOffset));
        if (tabBarY <= ScreenHeight - 49) {//上移tabBar到其底部和屏幕底部相同的时候,不准在上移,49是tabBar高度
            tabBarY = ScreenHeight - 49;
        }



    }else{//向上滚动,隐藏navigationBar和TabBar,设置statusBar透明
        navBarY -= fabs((marginOffset));
        if (navBarY <= -44) {//上移navigationBar,到完全隐藏navigationBar,就不准在上移了,44是其高度
            navBarY =-44;
        }

        tabBarY += fabs((marginOffset));
        if (tabBarY >= ScreenHeight; {//下移tabBar到其顶部和屏幕底部相同的时候,不准在下移
           tabBarY = ScreenHeight ;
        } 
    }

    lastOffset = currentOffset;
}

我们发现虽然两个bar的y值可以随着contentoffset动态改变,达到隐藏和显示的效果。但是可以发现有的时候,两个bar只显示一半就停住了,这是有问题的。如下图所示

这个时候需要做判断,当navigationBar向下偏移到达一定量,但是还没有完全显示完全的时候,就直接让两个bar完全显示。同理,当navigationBar向上偏移到达一定量的时候,虽然还没有完全隐藏,但是要让他直接隐藏。这样就不会出现上面两个bar显示一半的情况。

实现代码:
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    if (navBarY <= -12 ) {//当navigationBar的Y值小于-12的时候,就隐藏两个bar
        [UIView animateWithDuration:0.3 animations:^{
            self.navigationController.navigationBar.frame = CGRectMake(0, -44, ScreenWidth, 44) ;
            self.tabBarController.tabBar.frame = CGRectMake(0, ScreenHeight,  ScreenWidth, 49);
            if (scrollView.contentOffset.y <= 0) {
                self.tableView.contentOffset =CGPointMake(0, 0);
            }
        }];

    }else{//否则显示两个bar
        [UIView animateWithDuration:0.3 animations:^{
            self.navigationController.navigationBar.frame = CGRectMake(0, 20, ScreenWidth, 44) ;
            self.tabBarController.tabBar.frame = CGRectMake(0, ScreenHeight-49,  ScreenWidth, 49);
        }];
    }
}

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    if (navBarY <= -12 ) {//当navigationBar的Y值小于-12的时候,就隐藏两个bar
        [UIView animateWithDuration:0.3 animations:^{
            self.navigationController.navigationBar.frame = CGRectMake(0, -44, ScreenWidth, 44) ;
            self.tabBarController.tabBar.frame = CGRectMake(0, ScreenHeight,  ScreenWidth, 49);
            if (scrollView.contentOffset.y <= 0) {
                self.tableView.contentOffset =CGPointMake(0, 0);
            }
        }];

    }else{//否则显示两个bar
        [UIView animateWithDuration:0.3 animations:^{
            self.navigationController.navigationBar.frame = CGRectMake(0, 20, ScreenWidth, 44) ;
            self.tabBarController.tabBar.frame = CGRectMake(0, ScreenHeight-49,  ScreenWidth, 49);
        }];
    }

}
效果图:

comments powered by Disqus