iOS 自定义 Navigation Bar

Image Source

iOS 7 后时代的NavBar和页面布局

iOS 7之后,曾被Apple控件的死死的Status Bar一样子放开了,和View成了一体,不再占用空间。同时,View的Frame的Point也不再像之前一样,之前从{0.0, 20.0}计算,iOS 7以后则为{0.0, 0.0}。好在AutoLayout提供了Top Layout GuideBottom Layout Guide来进行布局支持。

EdgesForExtendedLayout

  • None
  • Top
  • Left
  • Bottom
  • Right
  • All

iOS 7中引入了这个属性,值为UIExtendedEdge的属性,指定边缘要延伸的方向。默认值为UIRectEdge.All,它代表布局是从整个View(含NavBar, StatusBar)顶部开始的,并到达View(含TabBar)底部。

如果遇到偏移问题或ScorllView底部被覆盖,最简单的做法是设置为UIRectEdge.None。但会将iOS 7中最Cool的毛玻璃效果也给排除掉了。

将NavigationBar的Translucent效果关闭,也会影响View的Frame。

1
self.edgesForExtendedLayout = UIRectEdge.None

AutomaticallyAdjustsScrollViewInsets

iOS 7在ViewController中还新增了另一个Bool属性,当设置为true时(默认),如果视图里面存在唯一一个UIScrollView或其子类View,那么它会自动设置相应的内边距,这样可以让scroll占据整个视图,又不会让导航栏遮盖。它的实现代码如下:

1
2
3
4
5
6
let topInset = self.tableView!.contentInset.top + self.topLayoutGuide.length
let leftInset = self.tableView!.contentInset.left
let bottomInset = self.tableView!.contentInset.bottom
let rightInset = self.tableView!.contentInset.right
self.tableView!.contentInset = UIEdgeInsetsMake(topInset, leftInset, bottomInset, rightInset)

不过这样实现后,会产生右边索引条偏移等问题。

开启半透明效果

在NavigationController的NavigationBar下开启Translucent效果。开启后NavigationBar才会不占据View布局启点。

同时针对浅暗色,可以调整StatusBar的样式

1
2
UIStatusBarStyleDefault = 0 //SB 黑色文字,浅色背景时使用
UIStatusBarStyleLightContent = 1 // SB 白色文字,深色背景时使用

自定义返回样式

1
2
3
4
5
6
7
8
9
10
11
// 修改返回按钮左侧图标
UIImage *backButtonBackgroundImage = [UIImage imageNamed:@"Menu"];
// The background should be pinned to the left and not stretch.
backButtonBackgroundImage = [backButtonBackgroundImage resizableImageWithCapInsets:UIEdgeInsetsMake(0, backButtonBackgroundImage.size.width - 1, 0, 0)];
id appearance = [UIBarButtonItem appearanceWhenContainedIn:[CustomBackButtonNavController class], nil];
[appearance setBackButtonBackgroundImage:backButtonBackgroundImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
// 将返回按钮文字置空
UIBarButtonItem *backBarButton = [[UIBarButtonItem alloc] initWithTitle:@" " style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backBarButton;

Status Bar

系统对StatusBar提供了两种默认的样式,以方便在不同情形下使用,默认是暗设(状态栏字体为暗色)。

  • LightContent 深底白字
  • Default 浅底黑字

在设置方面,系统也提供了几种方式。

启动页 Status Bar 设置

可以通过 Project Info -> Status bar style 进行设置

  • Gay style(default) 启动时黑色字体
  • Transparent black style (alpha of 0.5) 启动时白色字体
  • Opaque black style 启动时白色

如果不再进行其它设置,应用打开后的StatusBar样式,会沿用启动页样式。

全局 Status Bar 样式改变

或者应用启动后可以程序改变全局Status Bar样式:

1
UIApplication.sharedApplication().statusBarStyle = .LightContent

特定页面单独设置 Status Bar

如果项目中需要对特定的页面进行自定义,则需要在 Project Info 下添加一个新项 View controller-based status bar appearance,对应到源码则为 UIViewControllerBasedStatusBarAppearance

  • YES 表示 NavigationContrller 中可以 override 掉 StatusBar 的样式。
  • NO 则相反
1
2
3
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent
}

如果设置 View based 的值为YES,而未在 NavigationContrller 中写 override 方法,样式也会被 override 为 Default(深色字体),同时 UIApplication 设置无效

隐藏Status Bar

如果需要对特定页的Status Bar进行隐藏,可以通过ViewController中Override 方法来实现:

1
2
3
override func prefersStatusBarHidden() -> Bool {
return true
}

记得 View based 对此设置有效,要设为 YES 才会生效。

右上角显示多个按钮

按照系统提供的方法直接添加,空隙大到没有朋友认识。通过下面代码可以调节:

1
2
3
4
5
6
7
8
9
10
11
12
let shareButtonItem = UIBarButtonItem(image: UIImage(named: "nav_more"), style: UIBarButtonItemStyle.Plain, target: self, action: nil)
// 插入一个FixedSpace,让最右的Item靠右边
let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil)
negativeSpacer.width = -15
let favoriteButtonItem = UIBarButtonItem(image: UIImage(named: "nav_fav"), style: UIBarButtonItemStyle.Plain, target: self, action: "favorateButtonTapped")
// 将左边的Item也向右移
favoriteButtonItem!.imageInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, -40)
// 显示顺序是从右到左
self.navigationItem.rightBarButtonItems = [negativeSpacer, shareButtonItem, favoriteButtonItem]

需求

  • 正常NavBar下扩展出一行,加上一个自定义View。
  • 自定义View和标准NavBar一样,有Transparent效果。
  • Push ViewController后,恢复正常NavBar。
  • Push后再Pop仍是Extended的NavBar。

尝试了几种方法都不好用,后来忽然想到用Reveal Debug下,愕然发现,它用了一个私有类_UIBackdropView 来实现和NavigationBar一样的透明效果,不明白这样的类为什么不开放。

它的实现:在NavigationBar下加入一个自定义View,将NavBar的底部阴影去除,在自定义View下加入一条阴影线,并且自定义View也实现同样的Translucent效果。

REF::