UIView 和 UIViewController
2016-06-23 本文已影响354人
_凉风_
1. UIViewController
一个控制器,管理一个大界面
负责界面的创建、事件处理等
I. 创建控制器
直接创建
MyViewController *vc = [[MyViewController alloc] init];
通过 xib 创建
- 将创建的 xib 的 File's Owner 属性设为自定的控制器类
- 将其中的一个 view 设为 File's Owner 的 view
- 使用以下代码创建控制器
MyViewController *vc = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
通过 storyboard 创建
// 1. 先加载storyboard文件
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"storyboardName" bundle:nil];
// 2. 初始化storyboard中的控制器
// 2.1 初始化 箭头所指的控制器
MyViewController *vc = [storyboard instantiateInitialViewController];
// 2.2 初始化 标识my对应的控制器
MyViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"my"];
II. 控制器 MyViewController 以及 其 view 的加载过程
- 看 loadView 方法是否实现,是,则 使用 loadView 自定义 view,到步骤 7
- 若 1 没有实现,看是否从 storyboard 加载,是,则 根据 storyboard 描述创建,到步骤 7
- 若 2 没有实现,看是否给 nibName 赋值,是,则 根据 nibName 的值加载 xib 文件,到步骤 7
- 若 3 没有实现,看是否存在 MyView.xib 文件「控制器名去掉 Controller」,是,则 加载文件,到步骤 7
- 若 4 没有实现,看是否存在 MyViewController.xib 文件,是,则 加载文件,到步骤 7
- 若 5 没有实现,创建一个空的 view,到步骤 7
- 执行 viewDidLoad 函数后,返回一个 view 给控制器
// nibName 为 MyViewController
MyViewController *vc = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
// nibName 为 nil
MyViewController *vc = [[MyViewController alloc] init];
注:
- 先在 loadView 方法里给 控制器的 view 赋值
- 还可以在 viewDidLoad 方法里等 view 加载完毕后
重新给 控制器的 view 赋新值,不过这是第二次赋值相对耗费性能
III. 控制器 view 的加载方式
- 控制器的 view 是延迟加载的:用到时再加载
- 可以用 isViewLoaded 方法判断一个 UIViewController 的 view 是否已经被加载
- 控制器的 view 加载完毕就会调用 viewDidLoad 方法
2. 多控制器管理
当控制器有多个的时候,建议控制器之间建立层级关系
I. UINavigationController
作用:管理多个控制器
使用步骤
- 初始化 UINavigationController 对象
- 设置 UIWindow 的
rootViewController
是 UINavigationController - 通过
push
方法添加 子控制器
UINavigationController 以栈
的形式保存子控制器
- 常用方法
// 栈中的所有子控制器,可以赋值
@property(nonatomic,copy) NSArray *viewControllers;
// 栈中的所有子控制器,不可以赋值
@property(nonatomic,readonly) NSArray *childViewControllers;
// 使用push方法能将某个控制器压入栈
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
// 使用add方法同样能将某个控制器压入栈,只不过没有动画
- (void)addChildViewController:(UIViewController *)viewController;
// 使用pop方法可以将栈顶的控制器移除,出栈的控制器会被销毁
- (UIViewController *)popViewControllerAnimated:(BOOL)animated;
// 回到指定的子控制器
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated;
// 回到根控制器(栈底控制器)
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated;
控制器的加载步骤
// 1. view加载完毕「一个控制器只调用一次」
- (void)viewDidLoad;
// 2. view即将显示到window上
- (void)viewWillAppear:(BOOL)animated;
// 3. view显示完毕(已经显示到窗口)
- (void)viewDidAppear:(BOOL)animated;
// 4. view即将从window上移除(即将看不见)
- (void)viewWillDisappear:(BOOL)animated;
// 5. view从window上完全移除(完全看不见)
- (void)viewDidDisappear:(BOOL)animated;
// 6. 如果接收到内存警告的时候「应用代理对象先接收到警告,然后传给根控制器内存警告」
- (void)didReceiveMemoryWarning
/* 控制器警告方法执行步骤
1. 判断控制器内有无 View,有,到 1
2. 判断控制器内的 View 能否销毁「是否显示在窗口上」,能,到 2
3. 执行 viewWillUnload
4. 控制器的 View 被销毁
5. 执行 viewDidUnload
*/
// --------- 和内存有关的方法 ------------------------------
// 7. view即将销毁的时候调用
- (void)viewWillUnload{
// 「ARC模式下」控制器内的view对象即将销毁,在它销毁前清空下View中的内容
self.valueName = nil; // 写nil ARC和MRC 都会清空一下对应的属性
// 仅在 ARC 下 还可以使用 [self.valueName release];
}
// 8. view销毁完毕的时候调用
- (void)viewDidUnload;
II. UITabBarController
使用步骤
- 初始化 UITabBarController 对象
- 设置 UIWindow 的
rootViewController
为 UITabBarController - 通过 addChildViewController方法添加对应个数的子控制器
添加控制器
- 添加单个子控制器
- (void)addChildViewController:(UIViewController *)childController;
- 设置子控制器数组
@property(nonatomic,copy) NSArray *viewControllers;
特性
- 如果 UITabBarController 有 N 个子控制器,那么 UITabBar 内部就会有 N 个 UITabBarButton 作为子控件
- UITabBarButton 里面显示什么内容,由对应子控制器的 tabBarItem 属性决定
III. Segue
简介:操作界面跳转的 UIStoryBoardSegue对象
属性
// 表示是哪个 Segue 的唯一标识
@property (nonatomic, readonly) NSString *identifier;
// 来源控制器「从哪里开始跳转界面」
@property (nonatomic, readonly) id sourceViewController;
// 目标控制器「跳转到那个界面」
@property (nonatomic, readonly) id destinationViewController;
使用步骤
- 在 StoryBoard 上拖线,表明来源控制器和目标控制器
手动拖线
Paste_Image.png- 给拖好的线 Segue 设置一个 唯一标识
- 使用perform方法执行对应的Segue
// Segue必须由来源控制器来执行,也就是说,这个perform方法必须由来源控制器来调用
[self performSegueWithIdentifier:@"Segue的唯一标识名" sender:nil];
IV. 补充
1)performSegueWithIdentifier:sender: 方法的完整执行过程
-
根据identifier去storyboard中找到对应的线,新建UIStoryboardSegue对象
- 设置Segue对象的sourceViewController(来源控制器)
- 新建并且设置Segue对象的destinationViewController(目标控制器)
-
调用sourceViewController的下面方法
- 做一些跳转前的准备工作并且传入创建好的Segue对象
这个sender是当初 performSegueWithIdentifier:sender:中的sender
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
- 做一些跳转前的准备工作并且传入创建好的Segue对象
-
调用Segue对象的- (void)perform;方法开始执行界面跳转操作
- 如果segue的style是push
取得sourceViewController所在的UINavigationController
调用UINavigationController的push方法将destinationViewController压入栈中,完成跳转 - 如果segue的style是modal
调用sourceViewController的presentViewController方法将destinationViewController展示出来
- 如果segue的style是push
2)Modal 简介
- 默认效果:从屏幕底部往上钻,直到盖住之前的控制器为止
- 以Modal的形式展示控制器
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion
- 关闭当初Modal出来的控制器
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion;
3. UIView
I. UIView 的显示步骤
- view 内部的 Layer 会准备好一个
CGContextRef「图层类型上下文」
- 调用 delegate「这里就是 UIView」的
drawLayer:inContext:
方法
传入已经准备好的CGContextRef 对象
- view 在
drawLayer:inContext:
方法中又会调用自己的drawRect:
方法 - view 就可以在
drawRect:
方法中实现绘图代码, 所有东西最终都绘制到 view.layer 上面 - 系统再将 view.layer 的内容拷贝到屏幕, 于是完成了 view 的显示
II. 系统自动调用的方法「留给子类去实现」
// 控件的frame发生改变的时候就会调用
// 一般在这里重写布局子控件的位置和尺寸
// 重写了这个写方法后,一定调用 [super layoutSubviews];
- (void)layoutSubviews;
- (void)didAddSubview:(UIView *)subview;
- (void)willRemoveSubview:(UIView *)subview;
- (void)willMoveToSuperview:(UIView *)newSuperview;
- (void)didMoveToSuperview;
- (void)willMoveToWindow:(UIWindow *)newWindow;
- (void)didMoveToWindow;
III. 响应接口
@interface UIView : UIResponder<NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem>
// 能否和用户进行交互「默认 能」
@property(nonatomic,getter=isUserInteractionEnabled) BOOL userInteractionEnabled;
// 控件的一个标记:父控件可以通过tag找到对应的子控件
@property(nonatomic) NSInteger tag;
// 图层:可以用来设置 圆角\阴影 效果
@property(nonatomic,readonly,retain) CALayer *layer;
// 通过一个frame来初始化一个UI控件
- (id)initWithFrame:(CGRect)frame;
@end
IV. 几何图形接口
@interface UIView(UIViewGeometry)
// 位置和尺寸:以「自己」的左上角为坐标原点(0, 0)
@property(nonatomic) CGRect bounds;
// 位置和尺寸:以「父控件」的左上角为坐标原点(0, 0)
@property(nonatomic) CGRect frame;
// 中点:以「父控件」的左上角为坐标原点(0, 0)
@property(nonatomic) CGPoint center;
// 形变属性「平移\缩放\旋转」
@property(nonatomic) CGAffineTransform transform;
// 是否支持多点触摸「默认 不支持」
@property(nonatomic,getter=isMultipleTouchEnabled) BOOL multipleTouchEnabled;
@end
V. View 结构接口
@interface UIView(UIViewHierarchy)
// 父控件
@property(nonatomic,readonly) UIView *superview;
// 子控件(新添加的控件默认都在subviews数组的后面, 新添加的控件默认都显示在最上面\最顶部)
@property(nonatomic,readonly,copy) NSArray *subviews;
// 获得当前控件所在的window
@property(nonatomic,readonly) UIWindow *window;
// 从父控件中移除一个控件
- (void)removeFromSuperview;
// 添加一个子控件(可以将子控件插入到subviews数组中index这个位置)
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;
// 交换subviews数组中所存放子控件的位置
- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2;
// 添加一个子控件(新添加的控件默认都在subviews数组的后面, 新添加的控件默认都显示在最上面\最顶部)
- (void)addSubview:(UIView *)view;
// 添加一个子控件view(被挡在siblingSubview的下面)
- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview;
// 添加一个子控件view(盖在siblingSubview的上面)
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview;
// 将某个子控件拉到最上面(最顶部)来显示
- (void)bringSubviewToFront:(UIView *)view;
// 将某个子控件拉到最下面(最底部)来显示
- (void)sendSubviewToBack:(UIView *)view;
// 判断view的子控件 或者 子控件的子控件 是否为view的后代,默认自己也是自己的后代
- (BOOL)isDescendantOfView:(UIView *)view; // returns YES for self.
// 通过tag获得对应的子控件
// 也可以或者子控件的子控件,循环遍历的方式,也遍历自己,性能不好
- (UIView *)viewWithTag:(NSInteger)tag;
@end
VI. 渲染接口
@interface UIView(UIViewRendering)
// 超出控件边框范围的内容是否都剪掉
@property(nonatomic) BOOL clipsToBounds;
// 背景色「默认 nil」
@property(nonatomic,copy) UIColor *backgroundColor;
// 透明度「0.0~1.0,默认 1.0」
@property(nonatomic) CGFloat alpha;
// 是否不透明,是 则 不透明「默认 不透明」
@property(nonatomic,getter=isOpaque) BOOL opaque;
// 是否隐藏,是 则 隐藏
@property(nonatomic,getter=isHidden) BOOL hidden;
// 内容模式(居中/填充/拉伸)「默认 填充,UIViewContentModeScaleToFill」
@property(nonatomic) UIViewContentMode contentMode;
@end
VII. Block 动画接口
@interface UIView(UIViewAnimationWithBlocks)
+ (void)animateWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations;
+ (void)animateWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
usingSpringWithDamping:(CGFloat)dampingRatio
initialSpringVelocity:(CGFloat)velocity
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
@end