控制器及View的多种创建方式
- 创建控制器的3种方式?
- 直接通过alloc + init的方式创建。
- 创建一个“Single View Application”,删除默认控制器、Main.storyboard、Main Interface设置为空。
- 创意一个自定义的控制器,FirstViewController。
- 在AppDelegate的application:didFinishLaunchingWithOptions:方法中创建UIWindow对象。
- 通过alloc + init的方式创建该控制器的对象,并设置该控制器view的背景色。
- 设置UIWindow的根控制器为该控制器对象。
- 通过加载storyboard文件来创建一个控制器。
- 删除了Main.storyboard文件,也清空了Main Interface的配置,那么现在就需要自己创建一个新的storyboard文件(Two.storyboard), 然后拖一个控制器上去,在AppDelegate中自己写代码来加载Two.storyboard文件。
- 通过指定的xib文件来创建控制器。
- 创建一个自己的控制器类。
- 创建一个xib文件,在xib文件中拽一个UIView。
- 选中xib文件中的“File's Owner”,在属性中设置对应的类(Class)是刚才创建的控制器类。“File's Owner”可以理解为,当前xib文件是为谁服务的。
- 然后选中“File's Owner”->“右击”选中“view”->拖线到对应的view上。(设置该xib文件中哪个视图是控制器默认要创建的视图)
- 通过alloc + initWithNibName来创建。
- 直接通过alloc + init的方式创建。
// 一、直接通过alloc+init的方式创建
// 1.创建UIWindow
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 2.创建控制器
FirstViewController *vc1 = [[FirstViewController alloc] init];
vc1.view.backgroundColor = [UIColor blueColor];
// 3.设置控制器为UIWindow对象的根控器"rootViewController"
self.window.rootViewController = vc1;
// 4.显示UIWindow对象
[self.window makeKeyAndVisible];
// 二、通过加载storyboard文件创建
// 1.加载对应的storyboard文件
UIStoryboard *myStoryboard = [UIStoryboard storyboardWithName:@"My" bundle:nil];
// 2.创建该storyboard中的被设置为"初始化控制器"(箭头所指向的控制器)的对象。
UIViewController *vc2 = [myStoryboard instantiateInitialViewController];
// 3.根据storyboard中的控制器的storyboard ID来创建对应的控制器
UIViewController *vc3 = [myStoryboard instantiateViewControllerWithIdentifier:@"redVwController"];
// 4.设置控制器为UIWindow对象的根控制器"rootViewController"
self.window.rootViewController = vc2;
// 三、通过指定的 xib 文件来创建
// 1.通过xib文件, 创建控制器
FirstViewController *vc1 = [[FirstViewController alloc] initWithNibName:@"First" bundle:nil];
// 2.设置控制器为UIWindow对象的根控制器"rootViewController"
self.window.rootViewController = vc1;
- 创建控制器中的UIView?
- 控制器创建好以后,控制器所管理的view是如何创建的?
- 控制器的view是通过调用控制器的loadView方法来创建的。这个方法是UIViewController中的一个对象方法,只要控制器的loadView方法被调用了,那么证明控制器的view创建好了。
- 当调用完毕控制器的loadView方法以后,从这时开始,才创建好了控制器的view。
- 当控制器的view被创建好以后,紧接着就调用控制器的viewDidLoad方法。
- 控制器的view采用了懒加载的方式,也就是说,在需要的时候(在用到的时候)才会创建(调用loadView方法)。
- 注意:无论控制器是如何创建的,只要在控制器中,重写了loadView方法,并且没有调用
[super loadView];
方法,那么最终控制器的view就必须完全在loadView方法中自己来创建了,所以loadView方法可以用来自定义view。
- 控制器中的view创建:当第一次从self.view中获取view的时候(第一次使用self.view的时候),控制器会调用它自己的loadView方法先创建一个view,并且把这个新创建的view设置给self.view。
- 如果是直接通过alloc + init方法创建的控制器,则
self.view = [[UIView alloc] init];
。 - 如果是通过storyboard创建的控制器,加载storyboard中的控制器,并把storyboard中的控制器中的view设置给self.view。
- 如果是通过加载xib创建的控制器,加载xib中的view,并把xib中的view设置给self.view。
- 如果是直接通过alloc + init方法创建的控制器,则
- 通过storyboard创建完控制器后,自动调用loadView方法,创建控制器的view。此时自定义的控制器,因为没有“重写”loadView方法,所以loadView方法内部就是根据storyboard文件中的view来创建view的。
- 通过xib文件创建完控制器后,自动调用loadView创建控制器的view。此时自定义的控制器,因为没有“重写”loadView方法,所以loadView方法内部就是根据xib文件中的view来创建view的。
- 如果创建控制器的时候,通过initWithNibName指定了xib的名字,那么就根据该xib创建view。
- 如果创建控制器的时候,没有指定任何xib文件,也没有相应的storyboard文件,那么会尝试查找“与控制器名字相同的,但是去除Controller的文件名的xib文件”,如果还是没有,会尝试查找与控制器名字完全相同的xib文件,如果有,则使用该xib文件创建view。
- 建议:如果要使用某个xib文件名自动匹配的方式创建view,建议名字与控制器相同。
- 通过重写UIViewController的loadView方法,自己通过代码来创建控制器的view。
- 控制器的loadView方法就是用来自定义view的。如果要为控制器自定义view,要写在loadView中,不要写在viewDidLoad中。
- 如果重写loadView方法中调用了
[super loadView];
,那么依然会使用默认的方式来加载。想自定义,应该不调用此方法。
- 无论是通过加载xib创建view,storyboard创建view,最终都依赖于loadView方法。
- 控制器的loadView方法什么时候调用?
- 在需要用到控制器的view的时候才调用,当调用UIWindow对象的makeKeyAndVisible方法时,就需要显示该view了,此时就表示用到view了,这个就叫做“延迟加载”。
- 控制器创建好以后,控制器所管理的view是如何创建的?
多控制器管理
- 多控制器管理?
- 多控制器管理的思路:使用一个控制器来管理所有其他控制器,类似于使用一个UIView来管理其他的子view。
- 父控制器:管理其他控制器的控制器,就可以看做是“父控制器”、子控制器:被管理的控制器就是子控制器。
- iOS提供了两个比较特殊的控制器(用来管理“控制器”的控制器):
- UINavigationController导航控制器,轻松管理多个控制器,并且控制不同控制器之间的跳转和切换。
- UITabBarController也可以轻松地管理多个控制器,轻松完成控制器之间的切换。
- UINavigationController导航控制器?
- 使用步骤:
- 创建、初始化一个导航控制器:UINavigationController。
- 设置UIWindow的rootController为UINavigationController。
- 通过调用push方法添加子控制器到UINavigationController。
- 通过pop方法可以返回到上一个控制器。
- 注意:谁是最后一个push进来的,当前显示的就是哪个viewController。
- 导航控制器的导航栏是从Y坐标20开始的,高度是44,导航栏上面的20高度是“状态栏”。导航栏的内容由栈顶控制器的navigationItem属性决定。
- 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;
// 左上角的返回按钮
@property(nonatomic,retain) UIBarButtonItem *backBarButtonItem;
// 中间的标题视图
@property(nonatomic,retain) UIView *titleView;
// 中间的标题文字
@property(nonatomic,copy) NSString *title;
// 左上角的按钮
@property(nonatomic,retain) UIBarButtonItem *leftBarButtonItem;
// 右上角的按钮
@property(nonatomic,retain) UIBarButtonItem *rightBarButtonItem;
- UITabBarController?
- 使用步骤:
- 初始化UITabBarController。
- 设置UIWindow的rootViewController为UITabBarController。
- 根据具体情况,通过addChildViewController方法添加对应个数的子控制器。
- 如果UITabBarController有N个子控制器,那么UITabBar内部就会有N个UITabBarButton作为子控件。
- 使用步骤:
// 添加单个子控制器
- (void)addChildViewController:(UIViewController *)childController;
// 设置子控制器数组
@property (nonatomic,copy) NSArray *viewControllers;
// 标题文字
@property(nonatomic,copy) NSString *title;
// 图标
@property(nonatomic,retain) UIImage *image;
// 选中时的图标
@property(nonatomic,retain) UIImage *selectedImage;
// 提醒数字
@property(nonatomic,copy) NSString *badgeValue;
- App主流UI框架结构?
- Segue的介绍?
- storyboard上每一根用来界面跳转的线,都是一个UIStoryboardSegue对象,简称Segue,表示延续、继续的意思。
- 每一个segue对象,都有3个属性:①唯一标识;②来源控制器;③目标控制器。
- 根据segue的执行(跳转)时刻,segue可以分为2大类型:
- 自动型:点击某个控件后,自动执行segue,自动完成界面跳转。
- 手动型:需要通过写代码手动执行segue,才能完成界面跳转。
- 自动型segue:
- 按住control键,直接从控件拖线到目标控制器。
- 如果点击某个控件,不需要做任何判断,直接跳转到下一个界面,建议使用“自动型segue”。
- 手动型segue:
- 按住control键,从来源控制器拖线到目标控制器。
- 手动型的segue需要设置一个标识Identifier。
- 如果点击某个控件,需要做一些处理之后才能跳转到下一个界面,建议使用“手动型segue”。
- 利用某个控制器的
performSegueWithIdentifier:
对象方法可以执行当前控制器的某个Segue,跳转界面:- self是来源控制器,只能通过来源控制器来调用该方法。
- 根据Identifier去storyboard中找到对应的线,新建UIStoryboardSegue对象。
- 设置Segue对象的sourceViewController(来源控制器)。
- 新建并且设置Segue对象的destinationViewController(目标控制器)。
- 调用sourceViewController来源控制器的
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
方法,做跳转前的准备工作并传入创建好的Segue对象。- sender就是调用performSegueWithIdentifier:sender:方法时传入的对象。
- 调用Segue对象的
- (void)perform;
方法开始执行界面跳转操作。 - 如果Segue的style是push,则取得sourceViewController所在的UINavigationController,调用UINavigationController的push方法将destinationViewController压入栈中,完成跳转。
- 如果Segue的style是modal调用sourceViewController的presentViewController方法将destinationViewController展示出来。
- Modal介绍?
- 除了push之外,还有另一种控制器的切换方式,那就是Modal,任何控制器都能通过modal的形式展示出来。
- modal的默认效果:新控制器从屏幕的最底部往上钻,直到盖住之前的控制器为止。
// 以modal的形式展示控制器
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion;
// 关闭当初modal出来的控制器
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion;
- 控制器的数据传递
- 顺传:
- 控制器的跳转方向:A->B。
- 数据的传递方向:A->B。
- 数据的传递方式:在A控制器的
prepareForSegue:sender:
方法中根据Segue参数取得destinationViewController,也就是控制器B,直接给控制器B传递数据。 - 在B的
viewDidLoad
方法中取得数据,或者利用setter方法,设置界面上的UI控件。
- 逆传:
- 控制器的跳转方向:A->B。
- 数据的传递方向:B->A。
-
数据的传递方式:让A成为B的代理,在B中调用A的代理方法,通过代理方法的参数传递数据给A。
- 顺传: