一、程序启动过程
main —>UIApplicationMain
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([CXAppDelegate class]));
}
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([CXAppDelegate class]));
}
}
程序启动的入口,如果传入nil参数,默认为UIApplication类名字的字符串,可以用 NSStringFromClass([UIApplication class]) 和 @“UIApplication"来代替nil
UIApplicationMain底层的加载过程
1、创建UIApplication对象
2、创建UIApplicationDelegate对象,并且赋值给UIApplication对象的代理属性
3、开启主运行循环
4、加载into.plist文件,判断是否指定main.storyboard,如果指定,就去加载。
二、UIWindow
1、常见的窗口:键盘,UIActionSheet,UIAlertView
特殊的窗口:状态栏
2、窗口的层级(windowLevel)
UIWindowLevelNormal<UIWindowLevelStatusBar<UIWindowLevelAlert
窗口默认都是UIWindowLevelNormal层级,键盘的层级永远是最高的,在显示之前会获取当前最高层级数,然后键盘的层级+1
一个应用程序只能有一个主窗口
3、窗口中底层的实现方法
// 1、创建窗口,创建的window需要用强指针来引用以避免刚创建就被销毁
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 2、创建窗口的根控制器
UIViewController *rootVc = [[UIViewController alloc] init];
rootVc.view.backgroundColor = [UIColor redColor];
// 3、把控制器作为窗口的根控制器
// rootViewController 的好处:1、可以让窗口旋转;2.让代码结构清晰(自定义跟控制器,控制器的事情写在控制器里面)
window.rootViewController = rootVc;
// 4、把窗口显示在屏幕上,并且成为应用程序的主窗口
[window makeKeyAndVisible];
makeKeyAndVisible底层实现:
1、成为application的主窗口 application.keyWindow = self.window;
2、让窗口显示出来 self.window.hidden = NO;
3、把窗口根控制器的view添加到窗口上
[self.window addSubview:window.rootViewController.view];
三、三种创建控制器的方法
1、直接创建
UIViewController *vc = [[UIViewController alloc] init];
2、通过storyboard创建
// 1.创建窗口
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 2.1 加载main.storyboard,并且实例化一个控制器
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
// 2.2 实例化控制器(instantiateInitialViewController:会去加载箭头指向的控制器)
ViewController *vc = [storyboard instantiateInitialViewController];
// ViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"vc"];
// instantiateViewControllerWithIdentifier:会去storyboard中有木有vc这个标识的控制器
// 3.设置窗口的跟控制器
self.window.rootViewController = vc;
// 4.显示窗口
// 3.设置窗口的跟控制器
self.window.rootViewController = vc;
// 4.显示窗口
[self.window makeKeyAndVisible];
3、通过xib文件创建
// 1、设置xib的file’owner的class属性为控制器Class
2、加载xib需要file’owner和xib的view连线
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// Nib = xib
// 通过xib创建控制器
// 创建控制器,必须要有view,没有view就会报错
ViewController *vc = [[ViewController alloc] initWithNibName:@"VC" bundle:nil];
self.window.rootViewController = vc;
// Nib = xib
// 通过xib创建控制器
// 创建控制器,必须要有view,没有view就会报错
ViewController *vc = [[ViewController alloc] initWithNibName:@"VC" bundle:nil];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
四、控制器view的创建
1、loadView()
loadView作用:创建控制器的view
loadView什么时候去调用:第一次使用控制器的view的时候就会调用loadView
只要重写了loadView这个方法,就必须自己创建控制器的view,系统就不会帮你创建控制器view
2、loadView底层做法
0、判断控制器是否有重写loadView方法,如果有就只会加载loadView
1、判断下有没有指定storyboard,如果有,就去加载storyboard描述的控制器的view
2、判断下有没有指定nibName,如果有,就去加载nibName描述的控制器的view
2.0如何判断有没有指定nibName,[self nibName]
2.1判断下nibName是否为空,如果为空,他会尝试找下有没有跟控制器同名,但是不带Controller的xib
2.2跟控制器同名的xib,nibName = ViewController,但是这一步有条件,前提条件你没有重写loadView
CXViewController -> CXView.xib -> CXViewController.xib
2.3如果都没有找到,直接创建几乎透明的view(clear color)
[ViewController alloc] init]; 的底层会去调用 [ViewController alloc] initWithNibName:nil bundle:nil] 方法,从步骤2开始逐步执行判断
3、view为懒加载,当需要在屏幕上显示view的时候才会去调用loadView方法加载view
[[ViewController alloc] init].view.backgroundColor = [UIColor redColor];
当程序执行该代码时会先去判断view是否为空,如果不为空则直接返回已创建的view,如果为空,则会调用loadView方法加载view
/********* 伪代码 *********/
- (UIView *)view
{
if (_view == nil) {
[self loadView];
[self viewDidLoad];
}
return _view;
{
if (_view == nil) {
[self loadView];
[self viewDidLoad];
}
return _view;
}
/********* 伪代码 *********/
当view为空时,且重写了loadView方法并为view的背景赋值时
- (void)loadView
{
// 因为重写了loadView方法,所以程序不会自动加载view,必须手动创建一个view给控制器
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.view.backgroundColor = [UIColor yellowColor];
self.view.backgroundColor = [UIColor yellowColor];
}
在viewDidLoad()方法里重新为控制器的view赋值,该方法会在控制器加载完所有的控件时候调用
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
NSLog(@"控制器的view加载完成了");
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
NSLog(@"控制器的view加载完成了");
}
以上方法执行完,view的颜色将会是redColor
五、导航控制器
UINavigationController的view结构
导航控制器的view 在最底层,导航子控制器的view在第二层,导航条在最上面
当有控制器入栈时,控制器的view会添加到导航子控制器的view上
1、创建导航控制器必须要有一个导航控制器的根控制器,因为导航条的内容必须要依赖栈顶控制器,默认第一个根控制器就是栈顶控制器
// 创建导航控制器的跟控制器
UIViewController *vc = [[ViewController alloc] init];
vc.view.backgroundColor = [UIColor redColor];
// 创建导航控制器view,提示:导航控制器也需要一个跟控制器
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
// 导航控制器成为window的根控制器
self.window.rootViewController = nav;
[self.window makeKeyAndVisible];
2、initWithRootViewController:底层会调用pushViewController。
pushViewController:底层会把控制器压入栈,并且把控制器的view添加到导航子控制器的view上面。
如果导航子控制器上已有其他控制器的view,这时会推出其他控制器的view,并把自己的view添加上去(推出的view暂时不会被销毁,只有控制器出栈的时候才会销毁)
3、等需要push控制器的view完全显示的时候,会拿到push控制的navigationController属性赋值导航控制器
vc.navigationController = nav;
所以在被push的控制器里面可以访问到导航控制器
self.navigationController
4、导航控制器的viewControllers用来保存导航控制器里面所有的子控制器,让属性是个NSArray类型,0号索引永远是栈底
5、导航控制器永远显示出来的界面是栈顶控制器的view
6、 [self.navigationController popViewControllerAnimated:YES];
调用这个方法,并不会马上出栈,等控制器的view完全移除父控件,才会出栈,控制器出栈后,就会被销毁
六、导航条的内容
1、导航条的内容由栈顶控制器的UINavigationItem决定,为什么要这样设计,因为导航控制器只有一个导航条,如果每个子控制器都能设置,就不知道听谁的了,所以由显示出来的控制器决定。
2、UINavigationItem:是一个模型,用来决定导航条的内容。
3、UIBarButtonItem:是一个模型,用来决定导航条上按钮的内容。
4、导航条上面的子控件的位置由系统决定,但是尺寸是由我们自己决定
4.1、[button sizeToFit]默认计算按钮尺寸,根据按钮的内容计算出最合适的尺寸
5、在ios7之后,默认就会导航条上的按钮的图片渲染成蓝色
5.1、如何保持图片最原始的效果,不要渲染
[image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
5.2、Item就是模型,MVC,修改模型就能改界面