原文地址:http://goldfish0506.github.io/blog/2013/12/19/custom-navigation-bar/
自定义UI控件的外观是iOS开发者经常遇到的问题,再加上烦人的系统兼容,足够你研究一阵子了.
是时候解决这个痛点了! 我们就先从UINavigationBar开始吧~
iOS中默认的导航栏
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/contrast_default_navigation_bar.png)
设置导航栏的背景颜色
在iOS 7中,不再使用tintColor
属性来设置导航栏的颜色,而是使用barTintColor
属性来修改背景色。我们可以在AppDelegate.m
文件中 定义方法- (void)customizeAppearance
里面添加如下代码来修改颜色,并在didFinishLaunchingWithOptions:
中调用.
首先我们先定义个宏,来帮助我们设置颜色:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #define UIColorFromRGB(rgbValue) \
[UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self customizeAppearance];
return YES;
}
- (void)customizeAppearance
{
[[UINavigationBar appearance] setBarTintColor:UIColorFromRGB(0x0099cc)];
}
|
在iOS6中请使用:
1
| [[UINavigationBar appearance] setTintColor:UIColorFromRGB(0x0099cc)];
|
Table 1-1 Contrast
| ios7 | ios6 |
---|
Bar style | Translucent light (default) or translucent dark. | Opaque gradient blue (default) or opaque black. |
Translucent | Default YES | Default NO |
Appearance | 底部有一像素的线 | 底部边缘有投影 |
Tinting | tintColor 用来设置bar button items barTintColor 用来设置bar的背景色 | tintColor 用来设置bar的背景色 |
iOS7默认情况下,导航栏的translucent属性为YES.并对导航栏做模糊处理。 你可以在对应的ViewController中设置translucent.
而在iOS6及以前默认为NO,如果barStyle设置为UIBarStyleBlackTranslucent 那么translucent属性将总是YES. 如下图,是translucent值为NO和YES的对比效果:
iOS7:
![default_navigation_ios7](http://goldfish0506.github.io/images/article/custom_navigation_bar/ios7_contrast-_translucent_navigation_bar.png)
iOS6:
![default_navigation_ios7](http://goldfish0506.github.io/images/article/custom_navigation_bar/ios6_contrast-_translucent_navigation_bar.png)
在导航栏中使用背景图片
如果想要获得更惊艳的效果,那么你需要提供一张图片作为背景.你可以用以下方法设置背景图片:
1
| [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"navigation_bar_bg"] forBarMetrics:UIBarMetricsDefault];
|
一般情况下你可能会用这样一张图片(320x44)
![default_navigation_ios7](http://goldfish0506.github.io/images/article/custom_navigation_bar/navigation_bar_bg@2x.png)
但实际上我们并不推荐这么使用,因为纹理相同的图片我们可以截取其中一段并平铺,这样做的好处是可以让你的App占用更少的空间,也为将来适配更多尺寸埋下伏笔.
iOS 为我们提供了平铺图片的方法:
1
2
3
4
| UIEdgeInsets insets = UIEdgeInsetsMake(0, 10, 0, 10);
UIImage *navigationBGImage = [[UIImage imageNamed:@"navigation_bar_bg_pattern_44"]
resizableImageWithCapInsets:insets];
[[UINavigationBar appearance] setBackgroundImage:navigationBGImage forBarMetrics:UIBarMetricsDefault];
|
我们可以选择平铺的范围,而边缘我们可以让图片保留原有的纹理.示意图:
![default_navigation_ios7](http://goldfish0506.github.io/images/article/custom_navigation_bar/reszie_image_demo.png)
导航栏与状态栏
在iOS7中导航栏的高度从44 points(88 pixels)变为了64 points(128 pixels)。 获得的效果也与我们提供的图片高度有关.
你需要注意:
Table 1-2 Treatment of resizable background images for bars at the top of the screen
Height | Resizing treatment | Status bar background appearance |
---|
44 points | Horizontally resized as appropriate (the image is not vertically tiled or stretched). | Black, if using UIBarPositionTopAttached . Provided by the window background, if using UIBarPositionTop . |
Less than 44 points | Vertically resized to 64 points if using UIBarPositionTopAttached or 44 points if using UIBarPositionTop . Horizontally resized as appropriate. | Provided by the bar background. |
64 points | Horizontally resized as appropriate. | Provided by the bar background. |
1 point | Vertically resized to 64 points if using UIBarPositionTopAttached or if using a navigation controller. Vertically resized to 44 points if using UIBarPositionTop . Horizontally resized as appropriate. | Provided by the bar background. |
简单的说:
如果你想让Status Bar
保持iOS6的效果(黑色背景)请使用高度为44points
的图片并只是水平方向平铺.
如果你想让Status Bar
保持iOS7的效果 请使用高度为64points
的图片并保持水平方向平铺,或者使用高度为44points
的图片,水平和垂直方向都进行平铺.
我们来看下效果:
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/iOS7_navigation_bar_pattern.png)
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/iOS6_navigation_bar_pattern.png)
我们发现使用64points的图片对iOS6的支持并不好,所以这里还是建议IOS6/7分别用44/64高度的图片.
这里还是需要补充下:
iOS7 | 如果使用不透明的图片作为背景 并将translucent设为YES(默认) 那么系统会为图片设置一个小于1.0的不透明度 若要获得不透明的效果 请将并将translucent设为NO 如果使用透明的图片作为背景 且将translucent设为NO 系统会提供一个以barTintColor为颜色的不透明的背景 若barTintColor为nil的话,会根据bar style来改变背景 UIBarStyleBlack –> 黑色 UIBarStyleDefault –>白色 |
iOS6 | 如果使用不透明图片作为背景 并将translucent设为NO(默认) status bar会用图片的主色调作为背景 如果使用不透明图片作为背景 并将translucent设为YES status bar会变为黑色背景 此时若不设background color 则系统会为图片设置一个小于1.0的不透明度 若设置background color 则navigation bar 不透明 如果使用透明图片作为背景 并将translucent设为NO(默认) 系统会为navigation bar 提供一个以tintColor为颜色的背景 若要获得透明效果 请将translucent设为YES |
Tips:
iOS6在导航栏下方有系统提供的阴影,你可以替换成自己的图片,或者将它干掉:
1
| [[UINavigationBar appearance] setShadowImage: [[UIImage alloc] init]];
|
在iOS7中Status Bar是透明的,我们的视图会与Status bar有所重合.在这里我们需要注意视图的Layout. 在有导航栏的界面我们需要根据不同的情况进行处理:
Table 1-3 Status bar & Translucent
Translucent | iOS7 | iOS6 |
---|
YES | 视图从Status bar 顶部 算起 | 视图从Status bar 底部 算起 |
NO | 视图从Navigation bar 底部 算起 | 视图从Navigation bar 底部 算起 |
我们来看一下效果, 首先创建一个Label:
1
2
3
| UILabel *myLable = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 21)];
[myLable setText:@"My Test Label"];
[self.view addSubview:myLable];
|
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/iOS7_navigation_bar_status_bar.png)
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/iOS6_navigation_bar_status_bar.png)
Tips:
所以在进行开发时我们要从充分虑到视图的Layout,建议你的ViewController都继承自基类BaseViewController,并在其中做layout处理.
你可以用如下方法判断iOS版本:
1
2
3
4
5
6
7
| #define OS_PRIOR_IOS_7 (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1)
if (OS_PRIOR_IOS_7) {
// Load resources for iOS 6.1 or earlier
} else {
// Load resources for iOS 7 or later
}
|
定制返回按钮的颜色
iOS7:
导航栏上的按钮都是无边框的。要想给返回按钮着色,可以使用tintColor属性。如下代码所示:
1
| [[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
|
如果想要用自己的图片替换返回图标,可以设置图片的backIndicatorImage
和backIndicatorTransitionMaskImage
。如下代码所示:
1
2
| [[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:@"back_btn.png"]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"back_btn.png"]];
|
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/IOS7_navigation_bar_cutom_back_btn.png)
iOS6:
导航栏上的按钮都是有边框的。要想改变返回按钮UI, 可以使用以下方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
| NSDictionary *barItemAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
UIColorFromRGB(0xffffff), UITextAttributeTextColor,
UIColorFromRGB(0x000000), UITextAttributeTextShadowColor,
[NSValue valueWithUIOffset:UIOffsetMake(0, 1)], UITextAttributeTextShadowOffset,
[UIFont fontWithName:@"AmericanTypewriter" size:0.0], UITextAttributeFont,
nil];
[[UIBarButtonItem appearance] setTitleTextAttributes:barItemAttributes forState:UIControlStateNormal];
UIImage *buttonBack30 = [[UIImage imageNamed:@"button_back_textured_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
UIImage *buttonBack24 = [[UIImage imageNamed:@"button_back_textured_24"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 5)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:buttonBack30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:buttonBack24 forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
|
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/IOS6_navigation_bar_cutom_back_btn.png)
为了更好的兼容横屏模式你需要准备两种尺寸的背景图:
48x24
48x30
修改导航栏标题的字体
跟iOS 6一样,我们可以使用导航栏的titleTextAttributes属性来定制导航栏的文字风格。
在text attributes字典中使用如下一些key,可以指定字体、文字颜色、文字阴影色以及文字阴影偏移量:
iOS5/6:
1
2
3
4
5
6
| UIFont *titleFont = [UIFont fontWithName:@"HelveticaNeue-CondensedBlack" size:21.0];
NSDictionary *titleTextAttributes = @{UITextAttributeTextColor : UIColorFromRGB(0xf5f5f5),
UITextAttributeTextShadowColor : UIColorFromRGB(0x000000),
UITextAttributeTextShadowOffset : [NSValue valueWithUIOffset:UIOffsetMake(0, 1)],
UITextAttributeFont : titleFont};
[[UINavigationBar appearance] setTitleTextAttributes:titleTextAttributes];
|
iOS7:
1
2
3
4
5
6
7
8
9
10
| 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);
UIFont *titleFont = [UIFont fontWithName:@"HelveticaNeue-CondensedBlack" size:21.0];
NSDictionary *titleTextAttributes = @{NSForegroundColorAttributeName: UIColorFromRGB(0xf5f5f5),
NSShadowAttributeName : shadow,
NSFontAttributeName : titleFont};
[[UINavigationBar appearance] setTitleTextAttributes:titleTextAttributes];
|
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/contrast_navigation_bar_custom_title.png)
修改导航栏标题为图片
如果要想将导航栏标题修改为一个图片或者logo,那么只需要使用下面这行代码即可:
1
| self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"app_title"]];
|
上面的代码简单的修改了titleView属性,将一个图片赋值给它。 注意:这不是iOS 7中的新功能,之前的iOS版本就可以已经有了。
具体效果如下图所示:
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/contrast_navigation_bar_custom_image_title.png)
添加多个按钮
同样,这个技巧也不是iOS 7的,开发者经常会在导航栏中添加多个按钮,所以我决定在这里进行介绍.
我们可以在导航栏左边或者右边添加多个按钮.例如,我们希望在导航栏右边添加一个照相机和分享按钮,那只需要使用下面的代码即可:
1
2
3
4
5
| UIBarButtonItem *shareItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:nil];
UIBarButtonItem *cameraItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:nil];
NSArray *actionButtonItems = @[shareItem, cameraItem];
self.navigationItem.rightBarButtonItems = actionButtonItems;
|
在iOS6上系统提供样式不够好? 当然你可以用自己的图片来完成想要的效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
| NSDictionary *barItemAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
UIColorFromRGB(0xffffff), UITextAttributeTextColor,
UIColorFromRGB(0x000000), UITextAttributeTextShadowColor,
[NSValue valueWithUIOffset:UIOffsetMake(0, 1)], UITextAttributeTextShadowOffset,
[UIFont fontWithName:@"AmericanTypewriter" size:0.0], UITextAttributeFont,
nil];
[[UIBarButtonItem appearance] setTitleTextAttributes:barItemAttributes forState:UIControlStateNormal];
UIImage *button30 = [[UIImage imageNamed:@"button_textured_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
UIImage *button24 = [[UIImage imageNamed:@"button_textured_24"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 5)];
[[UIBarButtonItem appearance] setBackgroundImage:button30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:button24 forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
|
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/contrast_navigation_bar_muti_items.png)
修改状态栏的风格
在老版本的iOS中,状态栏永远都是白色风格.而在iOS 7中,我们可以修改每个view controller中状态栏的外观.
我们需要在对应的view controller中复写 preferredStatusBarStyle:
方法
1
2
3
4
| - (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
|
需要注意的是如果你的ViewController套在NavigationController中你只能通过改NavigationController的preferredStatusBarStyle
才能起到效果.
如果你用的是xib或者storyboard那你可以在xib中设置对应视图的status bar style.
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/xib_status_bar.png)
还有另外一种方法来改变status bar的样式(推荐
):
可以使用UIApplication的statusBarStyle方法来设置状态栏,不过,首先需要停止使用View controller-based status bar appearance
.
在project target
的Info tab
中,插入一个新的key,名字为View controller-based status bar appearance
,并将其值设置为NO.
![](http://goldfish0506.github.io/images/article/custom_navigation_bar/modify_info_status_bar_style.png)
之后你就可以通过下面的代码来改变status bar 的风格了
1
| [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
|
隐藏状态栏
有时候我们需要隐藏状态栏,那么此时我们在view controller中override方法prefersStatusBarHidden:即可,如下代码所示:
iOS6:
1
| [[UIApplication sharedApplication] setStatusBarHidden:YES];
|
iOS7:
1
2
3
4
| - (BOOL)prefersStatusBarHidden
{
return YES;
}
|
接下来?
还会继续总结Custom UI相关文章
你可以在这里下载到本文的Demo,(本文提供的代码需要用Xcode 5来执行)
如果文中有错误的地方,请指正,谢谢~