iOS 导航栏 知识总结

一点说明:本文中“导航控制器”区别于“视图控制器”存在


UINavigationController

UINavigationController
UINavigationController是一个导航控制器,它用来组织有层次关系的视图。导航控制器维护着一个视图控制器栈。在设计导航控制器时,UINavigationController默认也不会显示任何视图(这个控制器自身的UIView不会显示),需要指定用户看到的第一个视图,该视图控制器即是根控制器rootViewController,而且这个根控制器不会像其他子控制器一样被销毁,它是导航控制器栈中所有视图控制器的栈底。在UINavigationController中子控制器以栈的形式存储,只有在栈顶的控制器能够显示在界面中,一旦一个子控制器出栈则会被销毁。

子控制器入栈出栈相关方法
官方文档:You add and remove view controllers from the stack using segues or using the methods of this class. The user can also remove the topmost view controller using the back button in the navigation bar or using a left-edge swipe gesture.出栈:segue、method,移除栈顶:back button、left-edge swipe gesture


子控制器通过pop方法移除栈顶,先销毁的是子控制器自己本身,然后子控制器里面的View才被销毁,因为子控制器是持有View的。 导航控制下面的子控制器什么时候会被销毁

  • pop控制器,不会马上销毁栈顶控制器,而是告诉导航控制器需要把栈顶控制器出栈,等到恰当的时间就会把栈顶控制器出栈,并且销毁
  • initWithRootViewController:实际上是调用导航控制器的push方法
    //Convenience method pushes the root view controller without animation.
  • 在子视图中可以通过navigationController访问导航控制器,同时可以通过navigationController的childViewControllers获得当前栈中所有的子视图(注意每一个出栈的子视图都会被销毁)

UINavigationBar与UINavigationItem的关系

UINavigationBar
UINavigationItem

UINavigationBar导航栏:继承自UIView


*它最典型的用法就是放在屏幕顶端,包含着各级视图的导航按钮。它最首要的属性是左按钮(返回按钮)、中心标题,还有可选的右按钮(不过实际上UINavigationBar好像并没有这些属性,应该是存在于UINavigationItem类中的)。你可以单独用导航栏,或者和导航控制器一起使用(后者最普遍)。
如果你使用导航控制器去管理不同屏幕内容之间的导航,导航控制器会自动创建NavigationBar,以及在合适的时候push\pop navigation items

UINavigationItem导航项:继承自NSObject


一个UINavigationItem对象管理展示在导航栏上的按钮和视图。当创建一个导航界面的时候,每个压入导航栈中的视图控制器都需要一个navigation item,它包含了展示在导航栏上的按钮和视图。导航控制器利用最顶层的两个视图控制器的navigation item来提供导航栏的内容。(可以通过子视图控制器(包括根视图控制器)的navigationItem属性访问这个导航项,修改其左右两边的按钮和标题的内容。)

  • 关系:navigationcontroller直接控制viewcontrollers集合,然后它包含的navigationbar是整个工程的导航栏,bar有一个用来管理navigationItem的栈。@property(nonatomic, copy) NSArray <UINavigationItem *> *items
    navigationItem包含了navigationbar视图的全部元素(如title,tileview,backBarButtonItem等),每个视图控制器的导航项元素由所在视图控制器的navigationItem管理。即设置当前页面的左右barbutton,用 self.navigationItem.leftBarButtonItem等。
    总结来说:navigationcontroller和navigationbar是一对一的关系,而navigationbar和navigationItem则是一对多的关系。

记录一些需要注意的地方:

  • 默认情况下除了根视图控制器之外的其他子视图控制器左侧都会在导航栏左侧显示返回按钮,点击可以返回上一级视图,同时按钮标题默认为上一级视图的标题,可以通过navigationItem的backBarButtonItem属性修改。

  • leftBarButtonItem显示原则:
    1.如果当前的视图控制器设置了leftBarButtonItem,则显示当前VC所自带的leftBarButtonItem。
    2.如果没有设置leftBarButtonItem,且不是根视图控制器的时候,则显示前一层的backBarButtonItem。如果前一层没有指定backBarButtonItem的话,系统将会根据前一层的title属性自动生成一个back按钮,并显示出来。
    3.如果没有设置leftBarButtonItem,且已是根视图控制器的时候,左边不显示任何东西。

  • 下一级子视图返回按钮上的标题的显示优先级为:前一层的backBarButtonItem的标题(注意不能直接给backBarButtonItem的标题赋值:即直接更改backBarButtonItem.title,只能重新给backBarButtonItem赋值)、前一层navigationItem的标题,前一层视图控制器标题。

  • UINavigationController没有navigationItem这样一个直接的属性,但由于UINavigationController继承于UIViewController,它有navigationItem这个属性


    因此在视图控制器中这样写self.navigationController.navigationItem.title = @"friend";是没问题的,但是实际上并不能这样设置标题(self.navigationController.navigationItem 是应该被忽视的属性)。由于UINavigationController是视图控制器的容器,虽然它是特殊的视图控制器,但不应该把它当一般的UIViewController来使用.如果要设置视图控制器标题,应该这样写:self.navigationItem.title = @"friend";

  • navigationItem是UIViewController的属性,当第一次访问视图控制器的这个属性的时候,它会被创建。(不太明白)

  • 设置标题:self.navigationItem.title = @"friend";改变的是当前视图控制器的标题。(备注:self.navigationItem.title和self.title的效果是一样的)
    设置导航栏颜色:self.navigationController.navigationBar.barTintColor = [UIColor redColor];改变的是所有视图控制器的导航栏颜色

单独用导航栏UINavigationBar,(不是采用UINavigationController)
代码方式:

- (void)viewDidLoad{
    UINavigationBar *navBar = [[UINavigationBar alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 44)];
    //添加NavigationBar到视图上
    [self.view addSubview:navBar];
    UINavigationItem *navItem = [[UINavigationItem alloc]initWithTitle:@"welcome page"];
    
    UIBarButtonItem *btnlogin = [[UIBarButtonItem alloc]initWithTitle:@"login" style:UIBarButtonItemStyleDone target:self action:@selector(login)];
    navItem.leftBarButtonItem = btnlogin;
    //把NavigationItem添加到导航栏上(进栈)
    [navBar pushNavigationItem:navItem animated:NO];
}

UIBarButtonItem

UIBarButtonItem继承自UIBarItem,再往上的继承NSObject


是专门放在UIToolbar or UINavigationBar上的控件,具有按钮的行为。它分左、右、返回UIBarButtonItem,可以把她们添加到UINavigationItem上去

UINavigationItem *navItem = [[UINavigationItem alloc]initWithTitle:@"welcome page"];
UIBarButtonItem *btnlogin = [[UIBarButtonItem alloc]initWithTitle:@"login" style:UIBarButtonItemStyleDone target:self action:@selector(login)];
navItem.leftBarButtonItem = btnlogin;

UIBarButtonItem有如下初始化方法:



第一种可以设置系统自带风格按钮UIBarButtonSystemItem。

比如:

1.使用代码方式创建导航

步骤:

  • 初始化UINavigationController
  • 设置UIWindow的rootViewController为UINavigationController
  • 通过push添加对应子控制器

1.friend视图控制器,设置左右导航栏按钮

#import <UIKit/UIKit.h>
@interface FriendViewController : UIViewController
@end

#import "FriendViewController.h"
#import "ContactViewController.h"
@interface FriendViewController ()

@end

@implementation FriendViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //根据实际运行结果看来(视图不断切换,但下面这句话只会打印一次):
    //因为是根视图控制器,永远不会被销毁,所以viewdidload只会执行一次。
    NSLog(@"childviewcontroller:%@",self.navigationController.childViewControllers);
    
    NSLog(@"%i",self.navigationController == self.parentViewController);//true
    
    self.navigationItem.title = @"friend";
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"edit" style:UIBarButtonItemStyleDone target:nil action:nil];
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"User_normal"] style:UIBarButtonItemStyleDone target:self action:@selector(addPeople)];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)addPeople{
    ContactViewController *contactVC = [[ContactViewController alloc]init];
    [self.navigationController pushViewController:contactVC animated:YES];
}
@end
  • 不设置渲染模式,默认情况下系统会把导航栏上按钮的图片渲染成蓝色

2.contact视图控制器,添加右导航按钮,左导航按钮不设置默认显示上级视图控制器名称作为返回按钮

#import <UIKit/UIKit.h>
@interface ContactViewController : UIViewController
@end

#import "ContactViewController.h"
#import "AccountViewController.h"
@interface ContactViewController ()

@end

@implementation ContactViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 是子控制器,在视图不断切换的过程中,反复被创建和销毁;下面这句话多次打印出来。
    NSLog(@"childviewcontroller:%@",self.navigationController.childViewControllers);
    
    NSLog(@"%i",self.navigationController == self.parentViewController);
    
    self.view.backgroundColor = [UIColor whiteColor];//***注意***不设置切换会有卡顿
    [self setTitle:@"contact"];
    
    //设置下一级视图控制器导航返回按钮
    UIBarButtonItem *back = [[UIBarButtonItem alloc]initWithTitle:@"my contact" style:UIBarButtonItemStyleDone target:nil action:nil];
    self.navigationItem.backBarButtonItem = back;
    
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"account" style:UIBarButtonItemStyleDone target:self action:@selector(goToAccount)];
}

- (void)goToAccount{
    AccountViewController *accountVC = [[AccountViewController alloc]init];
    [self.navigationController pushViewController:accountVC animated:YES];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
@end
#import <UIKit/UIKit.h>
@interface AccountViewController : UIViewController
@end

#import "AccountViewController.h"
#import "FriendViewController.h"
@interface AccountViewController ()

@end

@implementation AccountViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"account";
    
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"toFriend" style:UIBarButtonItemStyleDone target:self action:@selector(goToFriend)];
}

- (void)goToFriend{
    //直接跳转到根控制器
    [self.navigationController popToRootViewControllerAnimated:YES];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
@end

4.初始化导航控制器并设置根视图控制器

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    [[UINavigationBar appearance]setBarTintColor:[UIColor yellowColor]];
    [[UINavigationBar appearance]setBarStyle:UIBarStyleBlack];    
    FriendViewController *friendVC = [[FriendViewController alloc]init];
    UINavigationController *navController = [[UINavigationController alloc]initWithRootViewController:friendVC];
    self.window.rootViewController = navController;
    [self.window makeKeyAndVisible];
    return YES;
}


*需要注意的地方:在使用UINavigationController的pushViewController:animated:执行入栈一个子控制器操作时,会出现"卡顿"现象。
原因:这是因为从iOS7开始, UIViewController的根view的背景颜色默认为透明色(即clearColor),所谓"卡顿"其实就是由于透明色重叠后,造成视觉上的错觉,所以这并不是真正的"卡顿"。
解决方法:只要在该UINavigationController所push的那个子控制器中设置背景颜色,即取缔默认的透明色 (即clearColor)
如:在viewDidLoad中写上 self.view.backgroundColor = [UIColor whiteColor];

  • 跳转到指定控制器:
// 注意:跳转到指定控制器的时候,要跳转到的目标控制器必须是在当前导航控制器的栈内的,不能是新创建的控制器
    NSArray *vcs = self.navigationController.childViewControllers;
    OneViewController *oneVc = (OneViewController *)vcs[1];
    [self.navigationController popToViewController:oneVc animated:YES];
}

** 注意:返回到指定控制器的时候,要跳转到的目标控制器必须是在当前导航控制器的栈内的,不能是新创建的控制器**

2.使用storyboard创建导航

1.在storyboard中拖拽一个UINavigationController。UINavigationController默认会带一个UITableViewController作为其根控制器。设置UITableViewController的标题为“Testing”,同时设置为静态表格并且包含两行,分别在单元格中放置一个UILabel命名为“A”和“B”
2.新建两个UITableViewController,标题分别设置为“A”、“B”
按住Ctrl拖拽“ Testing”的第一个表单元格到视图控制器“A”,同时选择segue为“show”,拖拽第二个表单元格到视图控制器“B”,同时选择segue为“show”。



到这里为止,可以通过点击两个单元格导航到A、B视图。

这里通过storyboard创建导航的关键是"segue"
Segue的工作方式分为以下几个步骤:
1.创建目标视图控制器(即A、B视图控制器)
2.创建Segue对象
3.调用源视图对象的prepareForSegue:sender:方法
4.调用Segue对象的perform方法将目标视图控制器推送到屏幕
5.释放Segue对象

先给Testing控制器绑定一个控制器文件(继承于UITableViewController)。然后选中连接A控制器的segue,设置它的identifier为"Segue",B同理



在Testing控制器对应文件中添加:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
  //  获得源视图控制器
    UITableViewController *testingVC = segue.sourceViewController;
  //获得目标视图控制器
    UITableViewController *desVC = segue.destinationViewController;
    NSLog(@"sourceController:%@,destinationController:%@",testingVC.navigationItem.title,desVC.navigationItem.title);
}

点击两个单元格,出现打印结果如下:对应上述第3步


在Testing视图控制器中放上左右导航按钮,并添加对应点击事件

- (IBAction)toA:(id)sender {
    [self performSegueWithIdentifier:@"ASegue" sender:self];
}
- (IBAction)toB:(id)sender {
    [self performSegueWithIdentifier:@"BSegue" sender:self];

}

运行程序,点击左右两个导航按钮,同样可以跳转到对应的A、B视图控制器,对应上述第4步。

什么是Segue(延伸)
Storyboard上每一根用来界面跳转的线,都是一个UIStoryboardSegue对象(简称Segue)

  • segue的类型
    根据Segue的执行(跳转)时刻,可分为2大类型
    1.自动型:点击某个控件后(比如按钮),自动执行Segue,自动完成界面跳转(按住Control键,直接从控件拖线到目标控制器)
    2.手动型:需要通过写代码手动执行Segue,才能完成界面跳转(按住Control键,从来源控制器拖线到目标控制器)

    创建手动型segue

    手动型的Segue需设置标识identifier,在需要的时刻,由来源控制器执行perform方法调用对应的Segue[self performSegueWithIdentifier:@"填入segue的标识" sender:nil];

  • performSegueWithIdentifier:sender:的执行过程
    [self performSegueWithIdentifier:@"xxx" sender:nil];
    1.self是来源控制器,只能通过来源控制器来调该方法。
    2.根据identifier去storyboard中找到对应的线,新建UIStoryboardSegue对象
    3.设置Segue对象的sourceViewController(来源控制器)
    4.新建并且设置Segue对象的destinationViewController(目标控制器)
    5.调用- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender方法。(prepare方法里的sender参数是调用perform方法时sender传入的对象)
    6.执行跳转。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容