IOS状态栏和导航栏的控制问题

 

目录:

一、设置导航栏样式

二、自定义导航栏返回按钮后侧滑不可用问题

三、隐藏导航栏底部的分割线

四、导航栏引起的布局问题

一、设置导航栏样式

设置导航栏的样式可分为全局设置与局部设置;

1.全局设置

全局设置一般的都是在AppDelegate中设置,这样整个app都会生效,相关的代码与效果图如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//1.设置导航栏背景颜色

[[UINavigationBar appearance] setBarTintColor:[UIColor orangeColor]];

     

//2.设置导航栏背景图片

[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"navigationBarImg"] forBarMetrics:UIBarMetricsDefault];

     

//3.设置导航栏标题样式

[[UINavigationBar appearance] setTitleTextAttributes: [NSDictionary dictionaryWithObjectsAndKeys:

                                                           [UIColor purpleColor], NSForegroundColorAttributeName,

                                                           [UIFont boldSystemFontOfSize:25], NSFontAttributeName, nil]];

     

//4.设置导航栏返回按钮的颜色

[[UINavigationBar appearance] setTintColor:[UIColor greenColor]];

//5.设置导航栏隐藏

[[UINavigationBar appearance] setHidden:YES];

1.png

设置导航栏样式效果图

2.局部设置

全局设置后,如果只有其中几个页面导航栏样式不同,那么我们可以使用局部设置。

注意1:局部设置与全局设置方法相同,但调用方法的对象变成了"self.navigationController.navigationBar"

注意2:局部设置必须遵循一个原则:"进入页面时修改,离开页面时还原”。

比如我们进入一个页面,需要设置当前导航栏的背景色为灰色,使用如下方法:

1

2

3

4

5

6

7

8

9

10

//进入页面时设置颜色:灰色

- (void)viewWillAppear:(BOOL)animated{

    [super viewWillAppear:animated];

    [self.navigationController.navigationBar setBarTintColor:[UIColor grayColor]];

}

//离开页面时还原为全局设置:橙色

- (void)viewWillDisappear:(BOOL)animated{

    [super viewWillDisappear:animated];

    [self.navigationController.navigationBar setBarTintColor:[UIColor orangeColor]];

}

二、解决自定义导航栏返回按钮后侧滑不可用问题

iOS导航栏自带的返回按钮形式单一,所以大多情况下,我们都需要自定义导航栏返回按钮。但是此时我们却发现页面的侧滑返回功能不可用了。为了解决这个问题,我们需要在App中使用我们自定义的导航控制控制器,示例代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

#import “BaseNavigationController.h"

//第一步:设置自定义导航控制器使用UIGestureRecognizerDelegate

@interface BaseNavigationController ()<uigesturerecognizerdelegate>

@end

@implementation BaseNavigationController

- (void)viewDidLoad {

    [super viewDidLoad];

    //第二步:设置自定义导航控制器的侧滑手势的代理

    self.interactivePopGestureRecognizer.delegate = self;

}

  

//第三步:实现代理方法

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{

    if (self.childViewControllers.count == 1) {

        // 表示用户在根控制器界面,就不需要触发滑动手势,

        return NO;

    }

    return YES;

}

@end</uigesturerecognizerdelegate>

三、隐藏导航栏底部的分割线

隐藏导航底部分割线也是我们偶尔会遇到的开发需求,首先我们可以通过Xcode的Debug View Hierarchy功能查看导航栏的视图结构,效果如下:

1.png

导航栏视图层级图

从图中可以看出,导航栏的底部分割线是一个UIImageView对象,而且高度只有0.5,所以我们可以据此获取到导航栏的底部分割线对象,在一个视图控制器中实现此需求,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

#import "TestViewController.h"

@interface TestViewController ()

//第一步:设置一个属性,存放导航栏底部分割线对象

@property (nonatomic, strong) UIImageView *navBarBottomImage;

@end

@implementation TestViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    第三步:获取导航栏底部分割线对象

    UIImageView *navBarBottomImage = [self findNavBarBottomImage:self.navigationController.navigationBar];

    self.navBarBottomImage = navBarBottomImage;

 }

//第四步:设置分割线的显示或隐藏

//进入页面隐藏分割线

- (void)viewWillAppear:(BOOL)animated{

    [super viewWillAppear:animated];

        self.navBarBottomImage.hidden = YES;

}

//离开页面时显示分割线

-(void)viewWillDisappear:(BOOL)animated{

    [super viewWillDisappear:animated];

    self.navBarBottomImage.hidden = NO;

}

//第二步:添加用于获取导航栏分割线的方法

//导航栏底部分割线是一个UIImageView,且高度不超过1.0个高度,可据此查找此对象

-(UIImageView *)findNavBarBottomImage:(UIView *)view {

    if ([view isKindOfClass:UIImageView.class] && view.bounds.size.height <= 1.0) {

        return (UIImageView *)view;

    }

    for (UIView *subview in view.subviews) {

        UIImageView *imageView = [self findNavBarBottomImage:subview];

        if (imageView) {

            return imageView;

        }

    }

    return nil;

}

四、导航栏引起的布局问题

1.内容偏移属性:automaticallyAdjustsScrollViewInsets

automaticallyAdjustsScrollViewInsets是视图控制器的一个属性,默认为YES,用于优化滑动类视图(继承于UIScrollView的视图)在视图控制里的显示:

iOS系统的导航栏UINavigationBar与标签栏UITabBar默认都是半透明模糊效果,在这种情况下系统会对视图控制器的UI布局进行优化:视图控制器里面第一个被添加进去的视图是滑动类视图,并且其Frame是整个屏幕大小时,系统会自动调整其contenInset,以保证滑动视图里的内容不被UINavigationBar与UITabBar遮挡。

但是对于普通的视图,此时我们仍然需要注意:非滑动视图的布局仍然要考虑导航栏和标签栏高度,注意不被遮挡,比如布局的时候加上导航栏高度,以免内容被导航栏遮挡。

我们可以通过一段代码来测试一下效果,在默认导航栏(半透明)的视图控制器里添加如下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

//UITextView是滑动视图,内容自动向下偏移,不会被导航栏覆盖

UITextView *leftTextView = [[UITextView alloc] init];

leftTextView.frame = CGRectMake(00,100, kDeviceHeight); //

leftTextView.backgroundColor = [UIColor lightGrayColor];

leftTextView.text = @"君不见,黄河之水天上来,奔流到海不复回。君不见,高堂明镜悲白发,朝如青丝暮成雪。人生得意须尽欢,莫使金樽空对月。天生我材必有用,千金散尽还复来。";

leftTextView.font = [UIFont systemFontOfSize:18];

leftTextView.editable = NO;

[self.view addSubview:leftTextView];

     

//UIView是非滑动视图,内容被导航栏部分覆盖

UIView *rightView= [[UIView alloc] initWithFrame:CGRectMake(1500100100)];

rightView.backgroundColor = [UIColor redColor];

[self.view addSubview:rightView];

1.png

导航栏透明情况下,滑动视图自动偏移,普通视图被遮挡

其实,这种系统的优化也是可以控制关闭的,关闭优化之后,滑动视图就会和普通视图一样,如果还设置其布局的原点是(0,0),其内容就会被导航栏所覆盖,关键代码如下:

1

2

3

4

5

6

7

//automaticallyAdjustsScrollViewInsets在11.0后失效,所以需要判断

if (@available(iOS 11.0,*)) {

       scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;

}else{

       //automaticallyAdjustsScrollViewIn,关闭自动偏移的系统优化

       self.automaticallyAdjustsScrollViewInsets = NO;

}

2.边缘延伸属性:edgesForExtendedLayout

edgesForExtendedLayout也是视图控制器的布局属性,默认值是UIRectEdgeAll,即:当前视图控制器里各种UI控件会忽略导航栏和标签的存在,布局时若设置其原点设置为(0,0),视图会延伸显示到导航栏的下面被覆盖。

所以我们可以设置self.edgesForExtendedLayout=UIRectEdgeNone,此时视图控制器里内容就会避开导航栏和标签栏了,依然是上面的leftTextView和rightView,设置了UIRectEdgeNone之后的效果图如下:

1.png

self.edgesForExtendedLayout=UIRectEdgeNone

3.导航栏透明属性translucent

上述两种属性都是在解决导航栏半透明情况下的布局问题,但是如果我们的需求就是导航栏不透明,那么视图控制器里的控件就会默认从(0,64)开始布局了,设置导航栏不透明的方法如下:

1

self.navigationController.navigationBar.translucent= NO;

IOS的项目多数会遇到控制状态栏和导航栏的问题,比如隐藏状态栏、控制状态栏的文字颜色等,导航栏也有同样需求。本文总结一下操作方法。

首先一点,IOS的界面分为状态栏和导航栏,状态栏是指显示电池、时间的最顶部的一个窄条,高度为20个点;而导航栏是紧接着状态栏的44个点高度的横条,一般用于显示app标题,返回按钮等操作按钮。

在ios7之前,状态栏和导航栏是分开的,而从ios7开始状态栏和导航栏交织在一起了,状态栏变为透明,导航栏的高度变为44+20=64:

状态栏控制

对状态栏的控制分两种情况:全局设置和分页面设置。控制这两种模式的开关是info.plist文件的View controller-based status bar appearance配置项。

全局设置状态栏

info.plist文件的View controller-based status bar appearance设置为NO,即可开启全局设置,也就是说你在VC中对状态栏的控制都将无效,相比之下,是通过下面的代码来全局控制:

//设置状态栏的字体颜色模式
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
//设置状态栏是否隐藏
[[UIApplication sharedApplication] setStatusBarHidden:YES];

注意,我们并不能对状态栏的字体颜色做任意的控制,只有两种选择UIStatusBarStyleDefaultUIStatusBarStyleLightContent,前者是默认的黑色,而后者是白色。也就是说如果你的背景色是偏深色,那么设置状态栏的字体颜色为白色。另外,我们可以全局设置状态栏是否显示,但是一般而言app不会对所有界面都不显示状态栏,而是只在特定的页面需要隐藏状态栏,比如对于视频播放界面不希望显示状态栏。

对于状态栏的背景色设置,上面提到从ios7开始状态栏本身实际上是透明的,它的背景色其实取决于导航栏的背景色。下面会讲导航栏的设置。

分页面设置状态栏

info.plist文件的View controller-based status bar appearance设置为YES,即可开启由VC来控制状态栏的功能,在这种模式下,全局的设置将无效!!所以我们必须逐个页面对状态栏进行设置,否则状态栏将维持默认的黑色字体和默认为显示状态。

字体设置

对于设置状态栏字体颜色,分两种情况:VC是否属于UINavigationController中:

1) 当VC不在UINavigationController中时,在VC中添加一个方法


- (UIStatusBarStyle)preferredStatusBarStyle
{ 
    //返回白色
    return UIStatusBarStyleLightContent;
    //返回黑色
    //return UIStatusBarStyleDefault;
}

保险起见,在view的某个加载阶段比如viewWillAppear中,执行:


[self setNeedsStatusBarAppearanceUpdate];

2) 当VC在UINavigationController中时,VC并不能通过1)的方式控制状态栏的颜色,详见本文后面的参考资料,那么这个时候,有一个trick的方法可以在VC中间接的控制:


self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

隐藏控制

对于控制状态栏的隐藏同样存在VC是否是根控制器的问题,也就说只有根控制器才能直接控制状态栏的显示与否。

1) 如果是VC本身就是根控制器,那么在VC中添加如下代码:


- (BOOL)prefersStatusBarHidden {
    return YES;
}

当然,保险起见,在适当的时候调用


[self setNeedsStatusBarAppearanceUpdate];

2) 如果VC不是根控制器,那么不像控制字体颜色那样有trick,我们只能间接的通过在子VC中控制根VC,从而间接控制根控制器。那么这个方法就很多了,比如我的根VC是个tab的VC,首先现在tab的VC中,实现1):


@interface YYCTabBarController : RDVTabBarController

//定义一个变量来控制状态栏显示,子VC通过修改这个值来间接控制
@property (nonatomic,assign)BOOL statusBarHidden;

@end

@implementation YYCTabBarController

- (BOOL)prefersStatusBarHidden {
    return _statusBarHidden;
}

@end

在子VC中:


- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    
    //rdv_tabBarController指向YYCTabBarController
    if([self.rdv_tabBarController respondsToSelector:@selector(setStatusBarHidden:)]){
        [self.rdv_tabBarController performSelector:@selector(setStatusBarHidden:) withObject:@(YES)];
        [self setNeedsStatusBarAppearanceUpdate];
    }
}

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    
    if([self.rdv_tabBarController respondsToSelector:@selector(setStatusBarHidden:)]){
        //注意对NO的情况,不能传@NO,只能传nil才能被当成NO
        [self.rdv_tabBarController performSelector:@selector(setStatusBarHidden:) withObject:nil];
        [self setNeedsStatusBarAppearanceUpdate];
    }
}

可以看到在子VC中通过设置根VC的属性,并调用setNeedsStatusBarAppearanceUpdate后,根VC的prefersStatusBarHidden就会被调用,从而隐藏或显示状态栏。

导航栏控制

背景控制

在IOS7中使用barTintColor来控制导航栏的背景色:


[[UINavigationBar appearance] setBarTintColor:[UIColor yellowColor]]; 

这个设置方法可以在AppDelegate中设置,全局可以生效。

如果希望使用图片来作为导航的背景,那么需要注意的是ios7中图片的高度问题。上面提到过了,ios7导航栏的高度其实是算上状态栏的,即44+20=64个点的高度。可以通过setBackgroundImage来设置:


[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@ "nav_bg.png" ] forBarMetrics:UIBarMetricsDefault]; 

前景控制

前景控制分为标题控制和返回按钮(等系统按钮)的控制

标题需要通过setTitleTextAttributes来设置,相对比较复杂一些,例如:


NSShadow *shadow = [[NSShadow alloc] init]; 
shadow.shadowColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8]; 
shadow.shadowOffset = CGSizeMake(0, 1); 
[[UINavigationBar appearance] setTitleTextAttributes: [NSDictionary dictionaryWithObjectsAndKeys: 
        [UIColor colorWithRed:245.0/255.0 green:245.0/255.0 blue:245.0/255.0 alpha:1.0], NSForegroundColorAttributeName, 
        shadow, NSShadowAttributeName, 
        [UIFont fontWithName:@ "HelveticaNeue-CondensedBlack"  size:21.0], NSFontAttributeName, nil]]; 

设置返回按钮(等系统按钮)可以通过TintColor,直接来设置颜色


[[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];

显示设置

有时我们希望导航栏不显示,而有时又希望显示,那么最好通过每个个体的VC来控制,如果某个VC需要与其他VC有所区别,那么最好是“负责到底”,即在进入VC时改变导航栏的显示状态,而退出时还原:


- (void) viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.navigationController.navigationBar setHidden:YES];
    [self.rdv_tabBarController setTabBarHidden:YES animated:NO];
}

- (void) viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.navigationController.navigationBar setHidden:NO];
    [self.rdv_tabBarController setTabBarHidden:NO animated:NO];
}

如何在有导航栏的情况下定位控件的Y

可能初学ios的同学(尤其是通过手写代码布局的同学)都会有这么个感受,为什么我的控件有的时候明明定位在VC上,但会被导航栏遮住,那么你可能会得出结论原点(0,0)是在屏幕的左上角被导航栏遮住的;而对于像UITableView这样的,设置了全屏铺满,怎么就没有被导航栏遮住呢?原点难道不在左上角?

笔者被这个问题困扰了很久,这里谈一下最近的一个理解。我们拿UITextView来看

当我们把一个UITextView放到一个没有导航的VC中时:


UITextView *textView = [[UITextView alloc] init];
textView.frame = CGRectMake(10, 200, 300, 120);
textView.backgroundColor = [UIColor redColor];
textView.text = @"游戏分两种,一种是在生活中玩的,另一种是生活在其中的。这两个世界相互矛盾,而两位约翰就分别属于这不同的世界。";
textView.font = [UIFont boldSystemFontOfSize:40];
textView.editable = NO;
[self.view addSubview:textView]

效果是这样的,看起来并没有什么问题

然而如果我们把这个VC放到一个导航控制器中,同样的代码却是这样结果

首先,看起来UITextView距离设备顶部的绝对距离似乎并没有变化,但是请注意UITextView的滚动条,滚动条竟然没有顶部对齐,而且文字也向下移位了,看起来空出一大块。仔细看空出的这段高度其实刚好是导航栏的高度64个点!!经过搜索,我发现只要设置如下代码即可恢复这种异常的状况:


self.automaticallyAdjustsScrollViewInsets = NO;

这下明白了,原来VC会对其内部的UIScrollView的内容部分进行一个Inset,这个Inset在上半部分刚好就对应导航栏的高度,而UIScrollView包括UITableViewUITextView等。到这里,似乎有些问题明朗了:

  1. VC中的view默认会对UIScrollView做一个适应导航栏的处理,由此推测,其实只要是VC中的控件,都是从设备左上角的(0,0)开始算的,只是对于UIScrollView,VC会自动调整一下内容的位置而已。

  2. 在有导航的情况下,可视范围的Y坐标就是从64开始的,除了UIScrollView的控件,定位的时候,都应当以(0,64)为原点;而UIScrollView如果是全屏的,那么无所谓,如果不是全屏的,请注意是否需要设置VC的automaticallyAdjustsScrollViewInsets

 

原文:

状态栏设置  http://www.jianshu.com/p/5c09c2700038

https://blog.csdn.net/yst19910702/article/details/78468491

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值