UIView和UIWindow展示了应用的用户界面,同时负责界面的交互。其中UIView表示屏幕上的一块矩形区域,它在App中占有绝对重要的地位,因为IOS中几乎所有可视化控件都是UIView的子类。负责渲染区域的内容,并且响应该区域内发生的触摸事件。
一、UIView简介
1.UIView功能及视图层次
UIView的功能
1.管理矩形区域里的内容
2.处理矩形区域中的事件
3.子视图的管理
4.实现动画
UIView的视图层次
2.UIView生命周期
UIView初始化方法
- (instancetype)initWithFrame:(CGRect)frame通过frame初始化UIView,也是官方推荐初始化方法。
- (nullable instancetype)initWithCoder:(NSCoder*)aDecoder如果使用NIB文件,使用此方法初始化。
UIView生命周期
(loadView/nib)文件来加载view到内存-->viewDidLoad函数进一步初始化这些view-->内存不足时, 调用viewDidUnload函数释放views-->当需要使用view时又回到第一步
loadView:
永远不要主导调用这个函数。viewController 会在view的property被请求并且当前view值为nil时调用这个函数。如果你手动创建view, 你应该重载这个函数,切不要在重载的时候调用[super loadView]。
viewDidload:
开发者可以进一步的初始化其views。viewDidLoad通常负责的是view及其子view被加载进内存之后的数据初始化的工作,即视图的数据部分的初始化
viewDidUnLoad:
viewDidLoad的对立函数,在程序内存欠缺时,这个函数被controller调用,来释放他的view以及view相关的对象。由于controller通常保存着view以及相关的object的引用,所以你必须使用这个函数来放弃这些对象的所有权以便内存回收,但不要释放那些难以重建的数据
viewWillAppear:
视图即将可见时调用,默认情况下不执行任何操作。
viewDidAppear:
视图已完全过渡到屏幕上时调用
viewWillDisappear:
视图被驳回时调用,覆盖或以其他方式隐藏,默认情况下不执行任何操作
viewDidDisappear:
视图被驳回后调用,覆盖或以其他方式隐藏。默认情况下不执行任何操作
didReceiveMemoryWarning:
当程序内存过度时,系统会调用该方法
3.UIView常用属性
三个结构体
struct CGPoint{CGFloat x;CGFloat y;};
struct CGSize{CGFloat width;CGFloat height;};
struct CGRect{CGPoint origin;CGSize size;};
最基本属性
frame是CGRect frame的origin是相对于父视图的左上角原点(0,0)的位置,改变视图的frame会改变center
center是CGPoint指的就是整个视图的中心点,改变视图的center也会改变frame
bounds是CGRect是告诉子视图本视图的原点位置(通俗的说就是,子视图的frame的origin与父视图的bounds的origin的差,就是子视图相对于父视图左上角的位置,如果结果为负,则子视图在父视图外)
常见属性
alpha:液晶显示器是由一个个的像素点组成的,每个像素点都可以显示一个由RGBA颜色空间组成的一种色值。其中的A就表示透明度alpha,UIView中alpha是一个浮点值,取值范围0~1.0,表示从完全透明到完全不透明。
当把alpha的值设置成0以后:
1、当前的UIView和subview都会被隐藏,而不管subview的alpha值为多少
2、当前UIView会从响应者链中移除,而响应者链中的下一个会成为第一响应者
hidden:该属性为BOOL值,用来表示UIView是否隐藏,默认值是NO。
当值设为YES时:
1、当前的UIView和subview都会被隐藏,而不管subview的hidden值为多少。
2、当前UIView会从响应者链中移除,而响应者链中的下一个会成为第一响应者
opaque:表示当前UIView是否不透明,该属性为BOOL值,UIView的默认值是YES,但UIButton等子类的默认值都是NO。事实上它却决定不了当前UIView是不是不透明,比如你将opaque设为NO,该UIView照样是可见的。
显示器中的每个像素点都可以显示一个由RGBA颜色空间组成的色值,比如上图中有红色和绿色两个图层色块,对于没有交叉的部分,即纯红色和绿色部分来说,对应位置的像素点只需要简单的显示红或绿,对应的RGBA为(1,0,0,1)和(0,1,0,1)就行了,负责图形显示的GPU需要很小的计算量就可以确定像素点对应的显示内容。从图中我们可以看出,重叠的部分出现了黄色,这是因为GPU会通过图层一和图层二的颜色进行图层混合,计算出混合部分的颜色,最理想情况的计算公式如下:
R = S + D * ( 1 – Sa )
其中,R表示混合结果的颜色,S是源颜色(位于上层的红色图层一),D是目标颜色(位于下层的绿色图层二),Sa是源颜色的alpha值,即透明度。公式中所有的S和D颜色都假定已经预先乘以了他们的透明度。
当UIView的opaque属性被设为YES以后,按照上面的公式,也就是Sa的值为1,这个时候公式就变成了:R = S。即不管D为什么,结果都一样。因此GPU将不会做任何的计算合成,不需要考虑它下方的任何东西(因为都被它遮挡住了),而是简单从这个层拷贝。这节省了GPU相当大的工作量。按照前面的逻辑,当opaque属性被设为YES时,GPU就不会再利用图层颜色合成公式去合成真正的色值。因此,如果opaque被设置成YES,而对应UIView的alpha属性不为1.0的时候,就会有不可预料的情况发生。所以说,opaque的真实用处是给绘图系统提供一个性能优化开关!
4.UIView Animation
1.frame,bounds,center//改变View的frame属性
-(void)doChangeFrame
{
[UIView animateWithDuration:2.0 animations:^(void){
smallImage.frame = CGRectMake(150, 80, 30, 30);
} completion:^(BOOL finished) {
smallImage.alpha = 0;
}];
}
2.alpha //改变透明度
3.backgroundColor //改变背景颜色
4.contentStretch //拉伸变化
5.transform //仿射变换,其中又包括Rotate,Invert,Translate,Scale(旋转,反转,位移,缩放)
-(void)doRotate//旋转
{
CGAffineTransform transform= CGAffineTransformMakeRotation(M_PI/4);
[UIView beginAnimations:nil context:nil];
bigImage.transform = transform;
[UIView commitAnimations];
}
-(void)doInvert//反转
{
BOOL isSingle = seg.selectedSegmentIndex;
//如果单次 直接翻转到原始状态 如果连续 在以前基础上再次进行反转
CGAffineTransform transform = isSingle?CGAffineTransformInvert(bigImage.transform):CGAffineTransformIdentity;
[UIView beginAnimations:nil context:nil];
bigImage.transform = transform;
[UIView commitAnimations];
}
-(void)doTranslate//位移
{
BOOL isSingle = seg.selectedSegmentIndex;
//如果单次 只改变一次 如果连续 在以前基础上再次进行移位
CGAffineTransform transform = isSingle?CGAffineTransformMakeTranslation(10, 10):CGAffineTransformTranslate(bigImage.transform, 10, 10);
[UIView animateWithDuration:1 animations:^{
bigImage.transform = transform;
}];
}
-(void)doScale//缩放
{
BOOL isSingle = seg.selectedSegmentIndex;
//如果单次 只改变一次 如果连续 在以前基础上再次进行缩放
CGAffineTransform transform = isSingle?CGAffineTransformMakeScale(1.0, 1.1):CGAffineTransformScale(bigImage.transform, 1.0, 1.1);
[UIView beginAnimations:nil context:nil];
bigImage.transform = transform;
[UIView commitAnimations];
}
关于Transition Animation可以参见这篇博文:http://www.csdn.net/article/2015-07-07/2825139-ios-uiview-animation-3
5.CALayer 和 UIView 的区别和联系
UIView之所以能显示在屏幕上,完全是因为它内部的一个图层,在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个层
@property(nonatomic,readonly,retain) CALayer *layer;
当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的图层上,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示
换句话说,UIView本身不具备显示的功能,拥有显示功能的是它内部的图层。
a.UIView可以相应事件,Layer不可以
UIKit使用UIResponder作为响应对象,来响应系统传递过来的事件并进行处理。UIApplication、UIViewController、UIView、和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。
在 UIResponder中定义了处理各种事件和事件传递的接口, 而 CALayer直接继承 NSObject,并没有相应的处理事件的接口。
下面四个方法分别处理触摸开始事件,触摸移动事件,触摸终止事件,以及触摸跟踪取消事件。
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
关于UIResponder的事件处理和传递机制参见以下两篇博文:
1.http://yishuiliunian.gitbooks.io/implementate-tableview-to-understand-ios/content/uikit/1-1-2.html
2.http://blog.csdn.net/chun799/article/details/8223612
b.CALayer的创建
Layer 的 frame 是由它的 anchorPoint,position,bounds,和 transform 共同决定的,而一个 View 的 frame 只是简单的返回 Layer的 frame,同样 View 的 center和 bounds 也是返回 Layer 的一些属性。
CALayer的创建时机是在[view initWithFrame] 的时候调用私有方法【UIView _createLayerWithFrame】去创建的。
c.UIView主要是对显示内容的管理而 CALayer 主要侧重显示内容的绘制。
UIView 是 CALayer 的CALayerDelegate,在代理方法内部[UIView(CALayerDelegate) drawLayer:inContext]调用 UIView 的 DrawRect方法,从而绘制出了 UIView 的内容.
d.CALayer重用视觉效果
调整图层的大小和位置
调整图层的背景颜色
修改图层的内容 (一个图片,或者是用CoreGraphics绘制的东西)
图层是否圆角
添加黑色投影
添加描边的边框
二、UIWindow简介
在iOS App中,UIWindow是最顶层的界面内容,我们使用UIWindow和UIView来呈现界面。UIWindow并不包含任何默认的内容,但是它被当作UIView的容器,用于放置应用中所有的UIView。
从继承关系来看,UIWindow继承自UIView,所以UIWindow除了具有UIView的所有功能之外,还增加了一些特有的属性和方法,而我们最常用的方法,就是在App刚启动时,调用UIWindow的makeKeyAndVisible方法,代码如下:
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
self.window= [[UIWindowalloc] initWithFrame:[[UIScreen mainScreen] bounds]];
HomeNavigationController *viewController = [HomeNavigationController sharedNavigationController];
self.window.rootViewController= viewController;
[self.windowmakeKeyAndVisible];
returnYES;
}
总的来看,UIWindow的主要作用有:
1.作为UIView的最顶层容器,包含应用显示所有的UIView;
2.传递触摸消息和键盘事件给UIView;
UIWindow添加UIView
通常有两种方式向UIWindow中添加UIView:
1.通过调用addSubView方法,因为UIWindow是UIView的子类,所以它可以使用UIView的addSubView方法给自己增加子UIView,从而承担容器的作用;
2.通过设置其特有的rootViewController属性。设置该属性后,UIWindow会自动将view controller的view添加到当前window中,同时负责维护view controller和view的生命周期。上述在application:didFinishLaunchingWithOptions:中使用的就是这种办法;
UIWindow的使用
通常在一个程序中只会有一个UIWindow,但有些时候我们调用系统的控件(例如UIAlertView)时,iOS系统为了保证UIAlertView在所有的界面之上,它会临时创建一个新的UIWindow,通过将其UIWindowLevel设置更高,让UIAlertView盖在所有其他UI之上。
UIWindow的层级由一个UIWindowLevel类型属性windowLevel,该属性指示了UIWindow的层级,windowLevel有三种可取值:
UIKIT_EXTERNconstUIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERNconstUIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERNconstUIWindowLevel UIWindowLevelStatusBar;
从中能够看出,默认程序的UIWindow的层级是UIWindowLevelNormal,当系统需要覆盖在其上覆盖UIAlertView时,就会创建一个层级是UIWindowLevelAlert的UIWindow,因为其windowLevel值更高,所以就覆盖在上面了。
有些时候,我们也希望在应用开发中,将某些界面覆盖在所有界面的最上层。这个时候,我们就可以手工创建一个新的UIWindow。需要注意的是,和创建UIView不同,UIWindow一旦被创建,它就自动地被添加到整个界面上了(当然,其windowLevel要足够高)。
还有一点需要注意的是,如果我们创建的UIWindow需要处理键盘事件,那就需要合理地将其设置为keyWindow。keyWindow是被系统设计用来接收键盘和其他非触摸事件的UIWindow。我们可以通过makeKeyWindow和resignKeyWindow方法设置UIWindow实例的keyWindow与否。
唐巧在《iOS开发进阶》里的描述,支付宝钱包等App的密码保护页面是基于UIWindow实现的,当用户从应用的任何界面按Home键退出,过一段时间再从后台切换回来时,显示一个密码输入界面。只有用户输入了正确的密码,才能进入退出前的界面。因为这个密码输入界面可能从任何应用界面弹出,并且需要盖住所有界面的最上层,所以很合适做一个UIWindow来实现。
UIWindow的获取
1.通过[UIApplication sharedApplication].windows 获取在应用中打开的UIWindow列表,可以获取应用中的任何一个UIView对象,例如 当输入文字时,弹出的键盘就是UIWindow,如果需要改变键盘的样式,则可用更改当前Window。
2.通过[UIApplication sharedApplication].keyWindow获取应用程序的主窗口,用来接收键盘以及非触摸类的消息事件的UIWindow,而且程序中每个时刻只能有一个UIWindow是keyWindow。
3.通过view.window获得某个UIView所在的UIWindow