1.UINavigationBar和UINavigationItem
UINavigationItem : NSObject
When building a navigation interface, each view controller pushed onto the
navigation stack must have a `UINavigationItem` object that contains the
buttons and views it wants displayed in the navigation bar. The
managing UINavigationController object uses the navigation items of the
topmost two view controllers to populate the navigation bar with content.
当view controller push进入navigation stack的时候,UINavigationItem是必须存在的,通常navigationController已经自动创建了UINavigationItem,它负责管理UInavigationController中的item和Title的内容以及交互。
UINavigationBar :UIView<UIBarPositioning>
A UINavigationBar object is a bar, typically displayed at the top of the
window, containing buttons for navigating within a hierarchy of screens.
The primary components are a left (back) button, a center title, and an
optional right button. You can use a navigation bar as a standalone object or
in conjunction with a navigation controller object.
A navigation bar is most commonly used within a navigation controller.
The object creates, displays, and manages its
associated navigation bar, and uses attributes of the view controllers you
add to control the content displayed in the navigation bar.
If you use a navigation controller to manage the navigation between
different screens of content, the navigation controller creates a navigation
bar automatically and pushes and pops navigation items when appropriate.
通常情况下我们使用UInavigationController push或者pop一个viewController的时候,UINavigationBar会被自动的创建,UINavigationBar是一个View的子类,我们可以自定义其显示的样式,包括title字体,背景,颜色等。
关系
A navigation controller uses the navigationItem property
on UIViewController to provide the model objects to its navigation bar when
navigating a stack of view controllers. The default navigation item uses the
view controller’s title, but you can override the navigationItem on
a UIViewController subclass to gain complete control
of the navigation bar’s content.
UINavigationBar的内容通常是由navigationItem提供,包括title和items等,如果想自定义UINavigationBar中的内容,那么需要更改navigationItem的配置。
2.UINavigationBar
UIBarStyle默认有4中样式,translucent属性设置bar是否透明。
Bar默认带有毛玻璃效果。
typedef enum UIBarStyle : NSInteger {
UIBarStyleDefault = 0,
UIBarStyleBlack = 1,
UIBarStyleBlackOpaque = 1,
UIBarStyleBlackTranslucent = 2
} UIBarStyle;
UINavigationBar可以自定义的属性
Core Attributes : Style , Bar Tint , Shadow Image, Back Image, Back Mask.
Attribute: Title Font, Title Color, Title Shadow.
tip:去除UINavigationBar下方横线
[self.navigationBar setShadowImage:[[UIImage alloc] init]];
/* 全局 */
[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];
/* 另外一种方式 */
self.navigationController.navigationBar.clipsToBounds = YES;
3.UINavigationItem
UINavigationItem提供了API设置leftItems,titleView,rightItems 和BackItems的方法。
3.1 backBarButtonItem
When this navigation item is immediately below the top item in the stack,
the navigation controller derives the back button for the navigation bar from
this navigation item. When this property is `nil`, the navigation item uses
the value in its title property to create an appropriate back button. If you
want to specify a custom image or title for the back button, you can assign a
custom bar button item (with your custom title or image) to this property
instead. When configuring your bar button item, do not assign a custom
view to it; the navigation item ignores custom views in the back bar button
anyway.
backBarButtonItem在UINavigationController中push时候自动的添加,如果你没有指定title,则返回按钮中的文字默认是前一个view controller的title。
不要尝试去添加custom view,因为不会生效。
hidesBackButton设置为YES可以隐藏返回按钮,默认是NO。
The backBarButtonItem property of a navigation item reflects the back
button you want displayed when the current view controller is just below the
topmost view controller. In other words, the back button is not used when
the current view controller is topmost.
If the title of your back button is too long to fit in the available space on the
navigation bar, the navigation bar may substitute the string “Back” in place
of the button’s original title. The navigation bar does this only if the back
button is provided by the previous view controller. If the new top-level view
controller has a custom left bar button item—an object in the
leftBarButtonItems or BarButtonItem property of its navigation item—the navigation bar does not change the button title.
你在当前View controller中设置的backBarButtonItem并不是当前view中backBarButtonItem的样式,而是从当前view controller中push出的view controller中backBarButtonItem的样式,topmost view controller中backBarButtonItem是无效的。
换而言之,当你准备push一个view controller并想自定义这个view controller的backBarButtonItem,那么在push之前,就需要设置好backBarButtonItem的样式。
UIViewController *nextViewController = [[UIViewController alloc] initWithNibName:@"" bundle:nil];
//设置nextViewController中navigationbar的backBtn样式。
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"navBacklIcon"] style:UIBarButtonItemStylePlain target:self action:@selector(Navback)];
[self showViewController:nextViewController sender:nil];
backBarButtonItem的title如果过长则会显示"back"。
如果设置了leftBarButtonItem,则backBarButtonItem将不会显示,除非将leftItemsSupplementBackButton设置为YES,默认为NO。
你可以用leftBarButtonItem替代backBarButtonItem,但是backBarButtonItem和leftBarButtonItem的位置是有差异的。
3.自定义UINavigationController
通常一个项目NavigationBar的风格都是统一的,我们可以通过下面方法来设置全局样式.
[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];
[[UINavigationBar appearance] set........
但是一个更好的方法是自定义一个UINavigationController。
@interface Q_navigationController ()<UINavigationControllerDelegate,UIGestureRecognizerDelegate>
@end
@implementation Q_navigationController
- (void)viewDidLoad {
[super viewDidLoad];
[self configNavBar];
}
-(void)configNavBar{//全局样式
[self.navigationBar setShadowImage:[[UIImage alloc] init]];
self.navigationBar.translucent = NO;
self.navigationBar.titleTextAttributes = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:17],
NSKernAttributeName:[NSNumber numberWithInteger:2],
NSForegroundColorAttributeName:[UIColor colorWithRed:18.0/255 green:150.0/255 blue:219.0/255 alpha:1]
};
self.navigationBar.tintColor = [UIColor colorWithRed:18.0/255 green:150.0/255 blue:219.0/255 alpha:1];
}
@end
UINavigationControllerDelegate是UINavigationController的回调,我们可以在push和pop的时候收到回调,自定义一些需要的事情,比如设置自定义返回按钮。
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (navigationController.viewControllers.count > 1) {
viewController.navigationItem.leftBarButtonItem = self.navBackBtn;
}
}
-(UIBarButtonItem *)navBackBtn{
if(!_navBackBtn){
_navBackBtn =[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"navBacklIcon"] style:UIBarButtonItemStylePlain target:self action:@selector(Navback)];
}
return _navBackBtn;
}
自定义的UINavigationController也会带来一些问题,比如边缘返回的手势失效了,为了能让它正常的工作,我们还需要做一些工作,在需要它的时候将其enabled设置为YES。
- (void)viewDidLoad {
[super viewDidLoad];
[self configNavBar];
self.delegate = self;
self.interactivePopGestureRecognizer.delegate = self;
}
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (navigationController.viewControllers.count <= 1) {
self.interactivePopGestureRecognizer.enabled = NO;
}else{
self.interactivePopGestureRecognizer.enabled = YES;
}
}
但是interactivePopGestureRecognizer可能会引入一些手势冲突问题,比如https://www.jianshu.com/p/ffde99688cff中描述的那样。
总结
理解UINavigationBar和UINavigationItem的关系,这让我们更加优雅的设计代码去配置项目中导航栏。