UINavigationController 视图控制器

1. UINavigationController 视图控制器

视图层次结构

UINavigationController 视图控制器_第1张图片

目录结构

UINavigationController 视图控制器_第2张图片

功能

  • UINavigationController 视图控制器;
  • 显示/隐藏导航栏和工具栏;
  • 视图入栈和出栈;
  • 导航栏样式;
  • 自定义导航按钮;

功能代码

  • AppDelegate.m 文件:

    初始化 UINavigationController 对象时,传入UIVIewController 实例对象作为根视图控制器。再将 UINavigationController 对象设置为 UIWindow 对象的根视图控制器。

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        
        // 实例化第一个视图控制器对象
        ViewController1 *firstViewController = [[ViewController1 alloc] initWithNibName:NSStringFromClass([ViewController1 class]) bundle:nil];
        // 初始化导航视图控制器对象
        _navigationController = [[UINavigationController alloc] initWithRootViewController:firstViewController];
        // 将导航视图控制器对象,设置为当前窗口的根视图控制器
        self.window.rootViewController = _navigationController;
        
        self.window.backgroundColor = [UIColor whiteColor];
        [self.window makeKeyAndVisible];
        
        return YES;
    }
    
  • ViewController1.m 文件

    - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self) {
            // 设置当前视图的标题
            self.title = @"the first View";
            // 设置当前视图右上角的导航按钮标题,以及点击事件
            self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
                                                      initWithTitle:@"SecondView"
                                                              style:UIBarButtonItemStylePlain
                                                             target:self
                                                             action:@selector(gotoSecondView)];
        }
        return self;
    }
    
    // 添加监听点击事件方法
    - (void)gotoSecondView {
        // 初始化第二个视图控制器对象
        ViewController2 *secondViewController = [[ViewController2 alloc]
                                                 initWithNibName:NSStringFromClass([ViewController2 class])
                                                 bundle:nil];
        // 将第二个视图控制器,压入导航视图控制器中,实现页面的跳转。
        [self.navigationController pushViewController:secondViewController animated:YES];
    }
    

执行结果

UINavigationController 视图控制器_第3张图片

显示/隐藏导航栏、工具栏

// 隐藏当前视图控制器的顶部导航栏
[self.navigationController setNavigationBarHidden:YES];

// 隐藏底部工具栏
[self.navigationController setToolbarHidden:YES];

视图入栈和出栈;

- (void)pushView {
    // 实例化第二个视图控制器
    ViewController2 *secondViewController = [[ViewController2 alloc] initWithNibName:NSStringFromClass([ViewController2 class]) bundle:nil];
    // 把视图控制器,push到导航视图里,相当于入栈操作
    [self.navigationController pushViewController:secondViewController animated:YES];
}

- (void)popView {
    // 当前视图控制器,将从导航视图控制器堆栈中移除,并返回至上一视图,相当于出栈操作
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)gotoIndexView {
    // 根据导航视图控制器中的全局序号,查找堆栈中指定序号的视图控制器
    UIViewController *viewController = [[self.navigationController viewControllers] objectAtIndex:2];
    // 然后跳转至该视图控制器
    [self.navigationController popToViewController:viewController animated:YES];
}

- (void)gotoRootView {
    // 导航视图控制器中的所有子视图控制器,都将全部出栈,从而跳转到根视图控制器。
    [self.navigationController popToRootViewControllerAnimated:YES];
}
  • 子视图退出到某个指定的父视图
for (UIViewController *controller in self.navigationController.viewControllers) {
            BOOL isKindOfClass = [controller isKindOfClass:[FisrtViewController class]];
            if (isKindOfClass) {
                [self.navigationController popToViewController:controller animated:YES];
            }
        }

导航栏样式

  • 设置导航栏样式
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    // 设置顶部导航区的提示文字
    self.navigationItem.prompt = @"Loading";
    // 设置导航栏背景是否透明
    self.navigationController.navigationBar.translucent = NO;
    // 设置导航栏系统样式
    self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
    // 设置导航按钮文本颜色。
    self.navigationController.navigationBar.tintColor = [UIColor greenColor];
    
    // 显示底部工具栏
    [self.navigationController setToolbarHidden:NO];
}
  • 设置导航栏标题&颜色
self.navigationItem.title = @"首页";
[self.navigationController.navigationBar setTitleTextAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f],NSForegroundColorAttributeName:ThemeColor}];
  • 自定义导航按钮:左侧按钮、右侧按钮、中间标题;
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // --------------------------------------------
    // 实例化一个工具条按钮对象,它将作为我们新的导航按钮
    UIBarButtonItem *leftButton = [[UIBarButtonItem alloc]
                                 initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
                                                      target:self
                                                      action:@selector(showAlert)];
    // 将导航栏左侧按钮,设置为新的工具条按钮对象
    self.navigationItem.leftBarButtonItem = leftButton;
    
    // --------------------------------------------
    // 同样为导航栏右侧的导航按钮,设置新的样式
    UIBarButtonItem *rightButton = [[UIBarButtonItem alloc]
                                   initWithBarButtonSystemItem:UIBarButtonSystemItemPlay
                                                        target:self
                                                        action:@selector(gotoSecondView)];
    self.navigationItem.rightBarButtonItem = rightButton;
    
    // --------------------------------------------
    // 创建一个视图对象,它将作为我们导航栏的标题区
    UIView *titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
    [titleView setBackgroundColor:[UIColor brownColor]];
    // 新建一个标签对象,它将显示标题区的标题文字
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(70, 0, 100, 30)];
    label.text = @"Title";
    [titleView addSubview:label];
    // 将视图对象设置为导航栏的标题区
    self.navigationItem.titleView = titleView;
    
}
  • 调整左上角返回按钮的边框距离
/// 直接设置
@property(nullable, nonatomic,strong) UIBarButtonItem *leftBarButtonItem;

大部分情况下,我们需要指定左边返回按钮距离左边框的距离,可以如下设定:

// 【方法一】把系统返回按钮替换成了 UIButton
UIButton *backBt = [UIButton buttonWithType:UIButtonTypeSystem];
backBt.frame = CGRectMake(0, 0, 20, 20);
[backBt setBackgroundImage:[UIImage imageNamed:@"back"]
                  forState:UIControlStateNormal];
[backBt addTarget:self
           action:@selector(backToRootViewController)
 forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backBt];

// 【方法二】在系统的返回按钮左侧加了一个带宽度的 Item
UIBarButtonItem *leftItem = [[UIBarButtonItem alloc]
                             initWithImage:[UIImage imageNamed:@"back"]
                                     style:UIBarButtonItemStylePlain
                                    target:self
                                    action:@selector(backToRootViewController)];
UIBarButtonItem *fixedItem = [[UIBarButtonItem alloc]
                      initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
                                           target:nil
                                           action:nil];
// 设置边框距离,可以根据需要调节
fixedItem.width = -16;
self.navigationItem.leftBarButtonItems = @[fixedItem, leftItem];
  • 全局属性设置
// 设置导航栏背景色
[[UINavigationBar appearance] setBarTintColor:[UIColor whiteColor]];
// 设置导航项颜色
[[UINavigationBar appearance] setTintColor:ThemeColor];
// 隐藏返回按钮文字
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];

UINavigationControllerDelegate

// 一般用于传递参数,或者做一些其它处理
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

隐藏/去掉导航栏返回按钮文字,只显示一个左箭头

// 方法一:全局设置
// 该方法就是把标题向上移动 60 px
// 设置/获取标题栏竖直位置偏移,UIBarMetricsDefault(竖屏)
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];
// 方法二:
// 注意此法需要在前一界面内设置,而且不是全局的,但是下一个界面标题会居中   
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]
                                       initWithTitle:@""
                                               style:UIBarButtonItemStylePlain
                                              target:self
                                              action:nil];
  • 用方法一隐藏返回按钮的文字以后,当上一个视图控制器的标题太长,会导致顶层视图控制器标题不居中显示的问题,修复的方法如下(建议做成 UIViewController 范畴(category)类):
// 如果有上个界面,将上个界面的title置为空,还是绕到方法二来了
- (void)resetBackButtonItem {
    
    NSArray *viewControllerArray = [self.navigationController viewControllers];
    
    long previousViewControllerIndex = [viewControllerArray indexOfObject:self] - 1;
    UIViewController *previous;
    
    if (previousViewControllerIndex >= 0) {
        previous = [viewControllerArray objectAtIndex:previousViewControllerIndex];
        previous.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]
                                                     initWithTitle:@""
                                                     style:UIBarButtonItemStylePlain
                                                     target:self
                                                     action:nil];
    }
}
  • 方法二也可以把返回按钮的文字替换为自定义文字,上面是替换为空了。
UIBarButtonItem *leftItem = [[UIBarButtonItem alloc] initWithTitle:@"返回"
                                            style:UIBarButtonItemStylePlain
                                                            target:self
                                                            action:nil];
self.navigationItem.backBarButtonItem = leftItem;

以上代码可以嵌入自定义的 UINavigationController 基类中:

/*
 删除导航栏底部线条
 
 推荐替代方法:位于 ChameleonDemo:该方法会把所有页面的底部线条删除
 self.navigationController.hidesNavigationBarHairline = YES;
 */
- (void)removeUnderline {
    [self.navigationBar setShadowImage:[UIImage new]];
}

// 执行此方法时,统一设置下一个视图控制器的返回按钮
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    // 第一个controller左button不确定, 其他controller左button为特定样式
    if (self.viewControllers.count > 0) {
        UIBarButtonItem *backBarButtonItem =
        [[UIBarButtonItem alloc] initWithTitle:@"返回"
                                         style:UIBarButtonItemStylePlain
                                        target:self
                                        action:nil];
        viewController.navigationItem.backBarButtonItem = backBarButtonItem;
        viewController.hidesBottomBarWhenPushed = YES; // 推入下一个视图控制器时隐藏TabBar
    }
    
    [super pushViewController:viewController animated:animated];
}
UINavigationController 视图控制器_第4张图片

在导航栏上添加多个按钮

// 设置导航栏返回按钮
UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc]
                                            initWithTitle:@"返回"
                                                    style:UIBarButtonItemStylePlain
                                                   target:self
                                                   action:nil];
self.navigationItem.backBarButtonItem = backBarButtonItem;
// 设置导航栏其他按钮
UIBarButtonItem *closeBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"关闭" style:UIBarButtonItemStylePlain target:self action:@selector(backToRootViewController)];
self.navigationItem.leftBarButtonItem = closeBarButtonItem;
// 设置左侧自定义按钮是否与返回按钮共同存在
self.navigationItem.leftItemsSupplementBackButton = YES;

参考

  • 超简单!!! iOS 设置状态栏、导航栏按钮、标题、颜色、透明度,偏移等

你可能感兴趣的:(UINavigationController 视图控制器)