11day-导航控制器

来源:互联网 发布:国外优化软件 编辑:程序博客网 时间:2024/06/06 07:39

前言

控制器管理(控制器之间的切换)

掌握:
1、控制器、view的多种创建方式
2、UINavigationController 的简单使用(添加、移除子控制器)
3、UINavigationBar内容的设置
4、控制器的生命周期

一、如何创建一个ViewController

控制器常见的创建方式有以下几种
1.通过storyboard创建
1》先加载storyboard文件(Test是storyboard的文件名)

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Test" bundle:nil];

2》接着初始化storyboard中的控制器

HMViewController *HM = [storyboard instantiateInitialViewController];//初始化“初始控制器”(箭头所指的控制器)//通过一个标识初始化对应的控制器HMViewController *HM = [storyboard instantiateViewControllerWithIdentifier:@”HM"];

2、直接创建

HMViewController *hm = [[HMViewController alloc] init];

3、指定xib文件来创建

- (void) settingsetRootViewControllerFromXib{    //    [self.window setRootViewController:[[HSPurpleViewController alloc]initWithNibName:@"HSPurpleView" bundle:nil]];    /** 如果没有指定xib,就会默认查找HSPurpleView(没有Controller之前的字符串HSPurpleView的xib,接着会查找HSPurpleViewController的xib) ,如果都没找到对应的xib描述文件,系统就自己创建一个View对象进行展示*/    [self.window setRootViewController:[[HSPurpleViewController alloc]init]];}

ps:指定xib文件的fileOwner,以及控制器的View (在xib 界面进行操作)

<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="HSPurpleViewController">            <connections>                <outlet property="view" destination="iN0-l3-epB" id="fkF-S7-TYo"/>            </connections> </placeholder>

二、ViewController的view创建

这里写图片描述

p s:父类的loadView 会自动完成上图的右侧逻辑

/** 自定义View  custom view */#if 1- (void)loadView{ // This is where subclasses should create their custom view hierarchy if they aren't using a nib. Should never be called directly.//    [super loadView];//父类的loadView 会自动完成上图的右侧逻辑    //实例化View    self.view = [[UITableView alloc]init];    [self.view setBackgroundColor:[UIColor lightGrayColor]];        NSLog(@"%s",__func__);}#endif

这里写图片描述

1、控制器的view是延迟加载的:用到时再加载

//window要显示到屏幕,必须设置它为主窗口,并且设置为可见[self.window makeKeyAndVisible];//Makes the receiver the key window and visible.--这个时候会调用控制器的loadView 方法

1》可以用isViewLoaded方法判断一个UIViewController的view是否已经被加载
2》控制器的view加载完毕就会调用viewDidLoad方法
2、内存警告处理:

这里写图片描述

3、生命周期方法

这里写图片描述

三、多控制器(用一个控制器去管理其他多个控制器)
为了便于管理控制器,iOS提供了2个比较特殊的控制器:UINavigationController、UITabBarController

四、UINavigationController

利用UINavigationController,可以轻松地管理多个控制器,轻松完成控制器之间的切换,典型例子就是系统自带的“设置”应用。

这里写图片描述

1、 UINavigationController的简单使用

1》UINavigationController的使用步骤

*初始化UINavigationController

*设置UIWindow的rootViewController为UINavigationController

*根据具体情况,通过push方法添加对应个数的子控制器
2、UINavigationController的子控制器(以栈的形式保存)

/UINavigationController以栈的形式保存子控制器@property(nonatomic,copy) NSArray *viewControllers;@property(nonatomic,readonly) NSArray *childViewControllers;//使用push方法能将某个控制器压入栈- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;//使用pop方法可以移除控制器、将栈顶的控制器移除- (UIViewController *)popViewControllerAnimated:(BOOL)animated;//回到指定的子控制器- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated;//回到根控制器(栈底控制器)- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated;

4、如何修改导航栏的内容
导航栏的内容由栈顶的navigationItem属性决定
1、navigationItem 的属性

@property(nullable, nonatomic,copy)   NSString        *title;             // Title when topmost on the stack. default is nil 中间的标题文字@property(nullable, nonatomic,strong) UIView          *titleView;         // Custom view to use in lieu of a title. May be sized horizontally. Only used when item is topmost on the stack.中间的标题视图@property(nullable,nonatomic,strong) UIBarButtonItem *backBarButtonItem __TVOS_PROHIBITED; // Bar button item to use for the back button in the child navigation item. 左上角的返回按钮/* Use these properties to set multiple items in a navigation bar. The older single properties (leftBarButtonItem and rightBarButtonItem) now refer to the first item in the respective array of items. NOTE: You'll achieve the best results if you use either the singular properties or the plural properties consistently and don't try to mix them.   leftBarButtonItems are placed in the navigation bar left to right with the first item in the list at the left outside edge and left aligned.   rightBarButtonItems are placed right to left with the first item in the list at the right outside edge and right aligned. */@property(nullable,nonatomic,copy) NSArray<UIBarButtonItem *> *leftBarButtonItems NS_AVAILABLE_IOS(5_0);//左上角视图@property(nullable,nonatomic,copy) NSArray<UIBarButtonItem *> *rightBarButtonItems NS_AVAILABLE_IOS(5_0);//右上角视图

五、什么是segue

这里写图片描述

storyboard上的每一根用来界面跳转的线,都是一个UIStoryboardSegue对象

1、segue的属性
1》每一个segue对象都有三个属性

@property (nullable, nonatomic, copy, readonly) NSString *identifier;//唯一标识@property (nonatomic, readonly) __kindof UIViewController *sourceViewController;//来源控制器@property (nonatomic, readonly) __kindof UIViewController *destinationViewController;//目标控制器

2、segue的类型

根据segue的执行时刻,可分为自动型、manual(手动)型
1》自动型(点击控件之后,自动执行segue,完成界面跳转)
适用于:用户点击控件之后,不需要任何的判断,直接跳到下一个界面

这里写图片描述

2》manual (通过代码执行segue)
适用于:点击控件之后,需要进行判断操作,满足一定条件之后才跳转到下一个页面

这里写图片描述

3、 performSegueWithIdentifier:sender: 方法可以执行某个Segue,完成界面跳转

[self performSegueWithIdentifier:@“login2contacts” sender:nil];/** 这个self是来源控制器1.根据identifier去storyboard中找到对应的线,新建UIStoryboardSegue对象设置Segue对象的sourceViewController(来源控制器)新建并且设置Segue对象的destinationViewController(目标控制器)2.调用sourceViewController的下面方法,做一些跳转前的准备工作并且传入创建好的Segue对象(控制器间的数据传递:根据segue获取destinationViewController,进行数据传递)- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;// 这个sender是当初performSegueWithIdentifier:sender:中传入的sender3.调用Segue对象的- (void)perform;方法开始执行界面跳转操作取得sourceViewController所在的UINavigationController调用UINavigationController的push方法将destinationViewController压入栈中,完成跳转*/#pragma mark 调转到下一个界面- (void) jumpSecondVc{    //显示蒙板    [MBProgressHUD showMessage:@"正在登录"];    //GCD    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{        [MBProgressHUD  hideHUD];        //方式一:pushViewController        //通过绑定标识获取Contacts        //    UIStoryboard *story = [UIStoryboard storyboardWithName:@"Main" bundle:nil];        //    HSContactsViewController *contacts = [storyboard instantiateViewControllerWithIdentifier:@"Contacts"];        //    [self.navigationController pushViewController:contacts animated:YES];        //方式二:执行segue        [self performSegueWithIdentifier:@"pushSegueToContacts" sender:self.account];//sender可以用于消息传递    });}#pragma mark - Notifies the view controller that a segue is about to be performed./** 执行segue之前调用,通常给下一个控制器穿值 */- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{        NSLog(@"%@",sender);    [segue.destinationViewController.navigationItem setTitle:[NSString stringWithFormat:@"%@ 的通讯录",sender]];}

ps:执行perform的方法补充说明

这里写图片描述

六、控制器的数据传递(代理、通知、block)--本质上都是方法调用
控制器之间的数据传输主要有顺传、逆传
1、顺传
在控制器A 的preformForSegue: sender: 方法中,根据segue参数获取destinationViewController C,直接给C传递数据(属性设置);--C的viewDidLoad方法中取得数据装配控件

2、逆传
代理,让A成为C的代理;在C中调用代理对象A的方法

七、UITabBarController

典型例子:QQ、webchat

正文

一、问题排查setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key
1.使用xib自定义View的设置customClass的时候,用使用view本身的customClass,而不要使用filerOwner的customClass(自定义控制器的时候,采用filerOwner的customClass)
2.要检查这一问题,可以从两个方面入手。
1》第一种出错的原因:可能是错误的IBOutlet连接,即xib中定义了某个IBOutlet,但是在对应的头文件中,该IBOutlet已经被修改或删除。要检查这一问题,可以打开xib文件,在File’s Owner上点击右键,然后在弹出的窗口中检查是否有“惊叹号”提示的IBOutlet连接,基本上可以找到问题所在。
2》第二种:出错的原因:可能是在xib文件中没有正确指定对象的类。要检查这一问题,可以打开xib文件,查看自定义视图控制器或者视图的Custom Class是否定义正确即可。

总结

键盘和当前第一响应者的布局处理

////  ViewController.m//  20160401-datePickerView(键盘处理)////  Created by devzkn on 4/1/16.//  Copyright © 2016 hisun. All rights reserved.//#import "ViewController.h"#import "HSKeyboardTool.h"#import "Constant.h"@interface ViewController () <HSKeyboardToolDelegate>{    NSArray *_textFields;//利用全局变量,存储所有的textField}@property (weak, nonatomic) IBOutlet UITextField *birthdayTextFiled;//生日输入框@property (nonatomic,strong) UIDatePicker *datePickerView;//datePicker@property (weak, nonatomic) IBOutlet UIView *inputerViewContainer;//输入框容器@end@implementation ViewController/**   实例化datepicker   */- (UIDatePicker *)datePickerView{    if (nil == _datePickerView) {        _datePickerView = [[UIDatePicker alloc]init];        //设置本地化        //本地化语言数组[NSLocale availableLocaleIdentifiers]        [_datePickerView setLocale:[NSLocale localeWithLocaleIdentifier:@"zh"]];        //设置日期模型--只显示日期        [_datePickerView setDatePickerMode:UIDatePickerModeDate];    }    return _datePickerView;}- (void)viewDidLoad {    [super viewDidLoad];    //1.初始化自定义键盘    [self setupCustomKeyboard];    //2.代码创建UIToolBar键盘工具条//    [self setupKeyBoardToolBar];    //3.使用xib描述键盘工具条    [self setupKeyBoardToolBarForXib];    //4.监听键盘的系统通知keyboardWillChangeFrame    //注册通知    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];}/** 处理键盘和当前响应者的布局关系 **/- (void) keyboardWillChangeFrame:(NSNotification *) notification{        NSLog(@"%s",__func__);    //0>设置UIWindow的背景颜色-避免平移过程,造成的UIWindow露出的部分是黑色    [self.view.window setBackgroundColor:self.inputerViewContainer.backgroundColor];    //设置setTransform 的平移    //1>(CGAffineTransform) 的y计算    //键盘的弹出完成时的frame    UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 227}, {320, 253}}";    //键盘隐藏结束之后的frame   UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 480}, {320, 253}}";    CGRect keyBoardFrame  = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];    //2>获得键盘工具条的实时y值=键盘实时y-工具条自身的height,判断是否需要平移    CGFloat keyboardY = keyBoardFrame.origin.y-KNormalHeight;    //****平移的y值计算****    CGFloat y ;    //获取inputerViewContainer的当前第一响应者子控件的最大y值    UITextField *currentResponderTextField = _textFields [[self getCurrentResponder]];    CGFloat currentResponderTextFieldY = CGRectGetMaxY(currentResponderTextField.frame)+CGRectGetMinY(self.inputerViewContainer.frame);//当前响应者的Y+当前响应者的所在容器到View的Y值    if (currentResponderTextFieldY < keyboardY) {//此时不需要移动inputerViewContainer        y = 0;//即无需移动(返回原来位置)      可以采用常量  [self.view setTransform:CGAffineTransformIdentity];        NSLog(@"currentResponderTextField = %f ,keyboardY = %f ",currentResponderTextFieldY,keyboardY);    }else{        y = keyboardY - currentResponderTextFieldY;        NSLog(@"currentResponderTextField = %f ,keyboardY = %f ",currentResponderTextFieldY,keyboardY);    }    //3>  UIKeyboardAnimationDurationUserInfoKey = "0.25";    //获取动画的持续时间    CGFloat keyBoardAnimationDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];    //4>动画平移输入框容器控件    [UIView animateWithDuration:keyBoardAnimationDuration animations:^{        //平移view        [self.view setTransform:(CGAffineTransformMakeTranslation(0, y))];    }];}/**  设置文本框的工具条 1》存储textField数组 2》设置工具条到textField对应的键盘 */- (void) setupKeyBoardToolBarForXib{    HSKeyboardTool *keyboardTool = [HSKeyboardTool toolbarView];    [keyboardTool setDelegate:self];//设置代理    NSMutableArray *tmpTextFiledsArray = [NSMutableArray array];    NSArray *subviewsArray = [self.inputerViewContainer subviews];    for (UIView *child in subviewsArray) {        //判断为UITextField的时候就设置setInputAccessoryView        if ([child isKindOfClass:[UITextField class]]) {            UITextField *textField = (UITextField *)child;            [textField setInputAccessoryView: keyboardTool];//设置工具条            [textField setEnablesReturnKeyAutomatically:YES];// indicating whether the Return key is automatically enabled when the user is entering text只有当文本框有内容的时候,return按钮才是可以点击的//            [textField setReturnKeyType:UIReturnKeySend];//设置return按钮的现实的样式            //添加textField            [tmpTextFiledsArray addObject:child];        }    }    _textFields = tmpTextFiledsArray;        NSLog(@"%@",_textFields);}#pragma mark -HSKeyboardToolDelegate的协议方法- (void)keyboardTool:(HSKeyboardTool *)keyboardTool didClickItemType:(KeyboardItemType)keyboardItemType{    //获取当前第一响应者的下标    int index =[self getCurrentResponder];    if (index == -1) {        return;//没有找到第一响应者    }    //根据KeyboardItemType 进行点击事件的细节处理    switch (keyboardItemType) {        case KeyboardItemTypePrevious:            //焦点移到上一个textField            [self KeyboardItemTypePreviousWithKeyboardTool:keyboardTool currentResponderIndex:index];            break;        case KeyboardItemTypeNext:            //焦点移到下一个textField            [self KeyboardItemTypeNextsWithKeyboardTool:keyboardTool currentResponderIndex:index];            break;        case KeyboardItemTypeDone:            //完成文本信息的读取,关闭键盘            [self KeyboardItemTypeDoneWithKeyboardTool:keyboardTool currentResponderIndex:index];            break;    }}#pragma mark - 获取当前textField响应者的索引- (int) getCurrentResponder{    int index = -1;    for (int i =0; i<_textFields.count; i++) {        if ([_textFields[i] isFirstResponder]) {            index = i;            break;        }    }    return index;}-(void) KeyboardItemTypePreviousWithKeyboardTool:(HSKeyboardTool *)keyboardTool currentResponderIndex:(int) index{    // 获取当前的响应者索引    int previousResponderIndex= (index >0) ? index-1: index;//    [_textFields[index] resignFirstResponder];//失去响应,此时会关闭键盘,会产生键盘通知    [_textFields[previousResponderIndex] becomeFirstResponder];}-(void) KeyboardItemTypeNextsWithKeyboardTool:(HSKeyboardTool *)keyboardTool currentResponderIndex:(int) index{    int nextResponderIndex= (index < _textFields.count-1) ? index+1: index;//    [_textFields[index] resignFirstResponder];//失去响应,此时会关闭键盘,会产生键盘通知--产生两个连续的平移动画    [_textFields[nextResponderIndex] becomeFirstResponder];}-(void) KeyboardItemTypeDoneWithKeyboardTool:(HSKeyboardTool *)keyboardTool currentResponderIndex:(int) index{    //得到当前的文本框    UITextField *currentFirstResponder = _textFields[index];    if (currentFirstResponder == self.birthdayTextFiled) {        [self done];//日期处理        return;    }//    [ currentFirstResponder resignFirstResponder];//关闭键盘    [self touchesBegan:nil withEvent:nil];//关闭键盘,恢复View到原来位置}/** 代码创建工具条,通常也可以采用xib 进行布局 */- (void) setupKeyBoardToolBar{    UIToolbar *toolBarView = [[UIToolbar alloc]init];#warning   设置setBounds,设置了之后,才可以进行UIBarButtonItem的点击操作    [toolBarView setBounds:CGRectMake(0, 0, 320, 44)];    [toolBarView setBackgroundColor:[UIColor lightGrayColor]];    //上一个按钮    UIBarButtonItem *previousButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"previous" style: UIBarButtonItemStylePlain target:nil action:nil ];    //下一个按钮    UIBarButtonItem *nextButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"next" style: UIBarButtonItemStylePlain target:nil action:nil ];    //完成按钮    UIBarButtonItem *doneButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"done" style: UIBarButtonItemStylePlain target:self action:@selector(done)];    //弹簧按钮    UIBarButtonItem *springButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem: UIBarButtonSystemItemFlexibleSpace  target:nil action:nil];    [toolBarView setItems:@[previousButtonItem,nextButtonItem,springButtonItem,doneButtonItem]];    //设置键盘工具条--谁触发键盘,谁去设置键盘属性信息    [self.birthdayTextFiled setInputAccessoryView:toolBarView];   }- (void) setupCustomKeyboard{    //设置textFiled的自定义键盘    [self.birthdayTextFiled setInputView:self.datePickerView];}#pragma mark - 工具条的完成按钮设置/**  关闭键盘,设置时间到textField*/- (void)done {    NSLog(@"%@",self.datePickerView.date);    //格式化日期类    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];    [dateFormatter setDateFormat:@"yyMMdd"];    [dateFormatter setDateStyle:NSDateFormatterLongStyle];    //设置信息到文本框    [self.birthdayTextFiled setText: [dateFormatter stringFromDate:self.datePickerView.date]];    //关闭键盘//    [self.birthdayTextFiled endEditing:YES];    [self.birthdayTextFiled resignFirstResponder];}/**  设置键盘隐藏  touchesBegan: Tells the responder when one or more fingers touch down in a view or window.*/- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{    [self.view endEditing:YES];    //动画恢复view到源位置    [UIView animateWithDuration:0.25 animations:^{        [self.view setTransform:CGAffineTransformIdentity];    }];}- (void)dealloc{    //移除通知    [[NSNotificationCenter defaultCenter] removeObserver:self];}@end

补充知识点:ARC转非ARC

非ARC-》ARC的转化过程:1.去掉dealloc 方法dealloc  Expand source2、retain 修改为strong 

这里写图片描述