1 有关 Windows and Views
每个应用都至少有一个 window 和一个 view。
1.1 添加额外的 Window
一般在有外界显示设备的时候才需要添加额外的 window
下面的代码举了一个例子,这里假定对象实现了方法 externalWindow,externalWindow 存储一个 window 的引用
- (void)configureExternalDisplayAndShowWithContent:(UIViewController*)rootVC
{
// Configure the content only if a second screen is available.
if ([[UIScreen screens] count] > 1) {
UIScreen* externalScreen = [[UIScreen screens] objectAtIndex:1];
CGRect screenBounds = externalScreen.bounds;
// Configure the window
self.externalWindow = [[UIWindow alloc] initWithFrame:screenBounds];
self.externalWindow.windowLevel = UIWindowLevelNormal;
self.externalWindow.screen = externalScreen;
// Install the root view controller
self.externalWindow.rootViewController = rootVC;
// Show the window, but do not make it key.
self.externalWindow.hidden = NO;
}
else {
// No external display available for configuration.
}
}
2 View 和 Window 层级
2.1 View 层级原理
UIKit中的每个 view,底层都拥有一个 layer 对象,通常都是CALayer。大多数情况下都直接通过 UIView 操作;当需要更多控制的时候可以通过 layer 执行操作。
注意:bar button item 不是 view,所以不能直接访问它的 layer。实际上 bar button item 是直接继承自 NSObject,而 layer 是 UIView 中定义的,所以bar button item没有。
核心动画层会对 view对象绘制代码的内容进行缓存重用,尤其是在有动画的情况下,重用比重新创建一个新的内容资源消耗小很多。
2.1.1 视图层级和子视图管理
如果子视图是完全不透明的话,会遮盖父视图的内容。父视图将子视图存放在一个有序的数组中,所以后添加的(子视图数组最后的)会在最上面显示。
父视图改变大小会引起子视图的大小随之变化,可以自定义这种变化。还有父视图被隐藏、改变父视图的透明度(alpha),或者对父视图的坐标系统应用数学转换等,都会影响到子视图。
UIResponder 和它的子类可以响应事件,并处理事件,UIView就是继承自 UIResponder。
点击事件的响应是从上到下一层一层判断的,如果最后没有任何对象做出响应,通常就丢弃了。
2.1.2 视图绘图周期
第一次绘图的时候系统保存一个绘图内容的快拍(snapshot),如果之后再没有对内容改动,那么不会再调用绘图代码,都直接使用这个快拍;如果有了改动,就重新生成一个快拍,周而复始。
当视图的内容或者外观发生变化的时候可以使用 setNeedsDisplay 和 setNeedsDisplayInRect 方法进行重绘;注意如果改变了视图的几何形状,这个方法就失效了。这两个方法是等待当前 run loop 执行到最后的时候,再将刚才设置的所有重绘操作一次执行完成。
有关几何形状的变化,需要看下面的 Content Modes
UIView 的子类,通常重写 drawRect: 方法,在这个方法里面写绘图代码。这个方法不需要自己调用。
2.1.3 Content Modes 内容模式
当发生下列两种情况时,内容模式就会应用:
@property(nonatomic) UIViewContentMode contentMode; // default is UIViewContentModeScaleToFill
typedef NS_ENUM(NSInteger, UIViewContentMode) {
UIViewContentModeScaleToFill,
UIViewContentModeScaleAspectFit, // contents scaled to fit with fixed aspect. remainder is transparent
UIViewContentModeScaleAspectFill, // contents scaled to fill with fixed aspect. some portion of content may be clipped.
UIViewContentModeRedraw, // redraw on bounds change (calls -setNeedsDisplay)
UIViewContentModeCenter, // contents remain same size. positioned adjusted.
UIViewContentModeTop,
UIViewContentModeBottom,
UIViewContentModeLeft,
UIViewContentModeRight,
UIViewContentModeTopLeft,
UIViewContentModeTopRight,
UIViewContentModeBottomLeft,
UIViewContentModeBottomRight,
};
UIViewContentModeRedraw 通常都不需要使用这个值,尤其在标准系统视图中不要使用。
2.1.4 可伸缩的视图
可以指定一个区域为可伸缩的,可以沿着一个轴或者两个轴伸缩。下图显示了视图自身显示的失真
contentStretch 属性用来指定可伸缩的区域。但是这个属性iOS6之后被废弃了,通常这个属性都是用在 view 的背景 UIImage 对象,所以现在用 [UIImage resizableImageWithCapInsets:] 达到相同效果。
2.1.5 内嵌动画的支持
执行动画需要做两件事:
- 告诉UIKit 你想要执行动画
- 改变属性的值
下面这些 UIView 对象的属性都可以用作动画:
- frame 以动画形式改变视图的位置和大小
- bounds 以动画形式改变视图的大小
- center 以动画形式改变视图的位置
- transform 旋转或者伸缩视图
- alpha 改变视图的透明度
- backgroundColor 改变视图的背景颜色
比如通常可以用导航栏控制器控制两个视图的转换动画,这是提供的标准动画,当觉得达不到想要效果,就可以自定义。
还可以直接使用 Core Animation layers创建动画。
2.2 视图的几何(Geometry)和坐标(Coordinate)系统
每个视图和窗口都定义了自己的局部坐标系统。
2.2.1 Frame,Bounds 和 Center 属性的关系
- frame 属性包含矩形框架,指定视图的大小和在父视图坐标系中的位置
- bounds 属性包含了矩形边界,指定了在视图自己局部的坐标系中视图的大小(以及内容的边界)
- center 属性包含了父视图坐标系中视图的中点
下面3种情况,改变会有连锁反应:
- 改变frame 属性,bounds、center 属性也会随着改变
- 改变center 属性,frame 的原点会改变
- bounds 属性的大小改变,frame 属性也会改变
2.2.2 坐标系变换
利用仿射变换可以改变整个视图的大小、位置或者方向。
transform 属性可以修改变换方式,并且有动画。
2.2.3 点像素
One point does not necessarily correspond to one pixel on the screen.
一个 point 并不一定和 一个像素相等,千万不要有相等的假设。
2.2.4 视图的运行交互模式
考虑下面几种情况:
1.用户触摸屏幕
2.硬件给UIKit框架报告触摸事件
3.UIKit 框架把触摸事件包装为一个 UIEvent 对象,并且分配给适当的视图。
4.视图的 event-handling 代码响应事件。比如,你的代码可以:
- 改变视图或者子视图属性(frame,bounds,alpha 等等)
- 调用 setNeedsLayout 方法来标记需要布局更新的视图或者子视图。
- 调用 setNeedsDisplay 或者 setNeedsDisplayInRect: 方法来标记需要重绘的视图或它的子视图。
- 通知 controller 有关一些数据块的修改
上面这都是由你来决定做哪些事情。
如果使用了手势识别来处理事件,就不要重写任何手势识别的方法;同样,如果视图不包含任何子视图或者它的大小没有改变,不要重写 layoutSubviews 方法;当你的视图内容在运行时候需要改变,或者你正在使用比如Uikit或者Core Graphics的原生技术来进行绘图时,才需要重写 drawRect:
2.3 有效使用视图的要点
自定义视图需要考虑性能问题;优化绘图代码之前,先测量性能,然后定个性能标准再优化。
2.3.1 视图并不需要总是对应一个试图控制器
视图控制器提供的功能比如:协调视图在屏幕上的显示,协调视图在屏幕上的移动,释放内存,旋转视图等。
2.3.2 尽可能少的进行自定义绘图
2.3.3 内容模型的优势
应该避免使用 UIViewContentModeRedraw。总是使用 setNeedsDisplay 或者 setNeedsDisplayInRect:
2.3.4 尽可能的将视图声明为不透明的
对于透明的渲染会增加性能的损耗。
2.3.5 当滚动的时候调整视图的绘制行为
滚动的时候非常损耗性能,所以可以考虑在滚动的时候暂时降低内容的渲染质量。滚动停止的时候再恢复。
2.3.6 不同通过嵌入子视图来自定义控件
永远不要给系统控件自行添加视图,这样会导致很多错误发生。
3 Windows 窗口
这章节涉及内容不常用,用时再看 - Windows
窗口职责:
- 包含应用的可视化内容
- 在视图触摸事件和其他应用对象之间扮演递送者
- 通常和视图控制器协作,来适应方向的变化
3.1 涉及窗口的任务
- Use the window object to convert points and rectangles to or from the window’s local coordinate system.
- Use window notifications to track window-related changes.
3.2 创建和配置窗口
3.2.1 用IB创建窗口
3.2.2 代码创建窗口
3.2.3 给窗口添加内容
3.2.4 改变窗口层级
3.3 监视窗口的改变
- UIWindowDidBecomeVisibleNotification
- UIWindowDidBecomeHiddenNotification
- UIWindowDidBecomeKeyNotification
- UIWindowDidResignKeyNotification
3.4 在额外的设备上显示内容
3.4.1 处理屏幕连接和断开通知
3.4.2 为额外的设备配置一个窗口
3.4.3为额外的设备配置屏幕模式
4 Views 视图
view 的职责:
布局和子视图管理:
- view 定义它对于父视图的默认 resize 行为
- 视图可以管理一系列子视图
- 视图可以根据需要调整子视图的大小和位置
- 视图可以将它的坐标系转化为其他视图或者窗口的坐标系
绘制和动画:
- 视图在它的矩形区域绘制内容
- view 的某些属性可以以动画的形式变换到新值
事件处理:
- 视图可以收到触摸事件
- 视图可以参与到响应链
4.1 创建和配置视图对象
4.1.1 使用IB创建视图对象
可以查看 Resource Programming Guide
4.1.2 代码创建view对象
CGRect viewRect = CGRectMake(0, 0, 100, 100);
UIView* myView = [[UIView alloc] initWithFrame:viewRect];
4.1.3 设置view 的属性
view 的一些关键属性的用法
属性 | 用法 |
---|---|
aplha, hidden, opaque | 这些属性影响view 的透明度,alpha 和 hidden 属性直接改变 view 透明度;opaque 属性告诉系统是否应该混合视图的显示。设置为YES 可以提升性能。 |
bounds, frame, center, transform | center 和 frame 属性都和父视图相关,而bounds属性在自己的坐标系中定义了可视化内容区域。transform 属性常用语动画或者用复杂的方式移动 view。 |
autoresizingMask, autoresizesSubviews | 这些属性影响视图和子视图的自动 resieze 行为。autoresizingMask 属性控制视图在父视图中如何响应变化。autoresizesSubviews 属性控制是否当前视图的子视图要完全被 resize。 |
contentMode, contentStretch, contentScaleFactor | 这些属性影响 view 里面内容的渲染行为。 |
gestureRecognizers, userInteractionEnabled, multipleTouchEnabled, exclusiveTouch | 这些属性影响 view 如何处理触摸事件。 |
backgroundColor, subviews, drawRect:方法, layer, (layerClass 方法) | 这些属性和方法帮你管理 view 中的实际内容。 |
4.1.4 为以后的验证标记 view
tag 可以用一个整数值来标记特定的 view。默认这个属性为 0。
找到标记的view,可以使用 viewWithTag: 方法。
4.2 创建和管理 view 层级
4.2.1 添加和移除子视图
addSubview: 方法直接添加到最上面。
- (void)removeFromSuperview;
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;
- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2;
- (void)addSubview:(UIView *)view;
- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview;
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview;
- (void)bringSubviewToFront:(UIView *)view;
- (void)sendSubviewToBack:(UIView *)view;
4.2.2 隐藏 view
设置 hidden 属性为 YES,或者将alpha 属性设置为 0.0。隐藏view 之后就不能收到触摸事件了。
如果要以动画形式将view 从可视化到隐藏,必须使用 alpha 属性。hidden 属性不是可动画的
4.2.3 从 view 层级中找出视图
两种方式:
- 存储相应视图的指针,比如 view controller 拥有视图的方式
- 给 view 的 tag 属性赋值,但数字要独一无二;然后用 viewWithTag: 方法拿到
4.2.4 转变,伸缩,旋转 view
// M_PI/4.0 is one quarter of a half circle, or 45 degrees.
CGAffineTransform xform = CGAffineTransformMakeRotation(M_PI/4.0);
self.view.transform = xform;
4.2.5 在视图层级中转换坐标
UIView 定义了下面几种方法在view 的局部坐标系中转换坐标
类似,UIWindow也定义了几个转换方法:
4.3 在运行时调整 view 的大小和位置
UIView 支持自动和手动布局
4.3.1 为布局改变做准备
当 view 中有下面几种事件发生的时候,布局需要改变:
- view 的 bound 矩形大小发生变化
- 界面的方法发生变化,这通常会触发根视图的 bound 矩形变化
- 与 view layer相关的 Core Animation sublayer 的集合发生变化,并要求 layout
- 调用view 的 setNeedsLayout 或者 layoutIfNeeded 方法,让应用强制布局
- 调用view的 layer对象的 setNeedsLayout 方法,让应用强制布局
4.3.2 使用自动调整大小规则让布局自动变化
设置父视图 autoresizesSubviews 属性来决定子视图是否需要调整大小。如果这个属性为 YES,每个子视图的 autoresizingMask 属性决定如何变化。
Autoresizing mask | 描述 |
---|---|
UIViewAutoresizingNone | 默认值,view不会自动调整大小 |
UIViewAutoresizingFlexibleHeight | 当父视图的高变化的时候,视图的高也会变化。如果没有包含这个常量,视图的高不变 |
UIViewAutoresizingFlexibleWidth | 当父视图的宽变化的时候,视图的宽也会变化。如果没有包含这个常量,视图的宽不变 |
UIViewAutoresizingFlexibleLeftMargin | 视图左边界与父视图左边界的距离会按需要增大或减小。如果没有包含这个常量,将维持固定的距离 |
UIViewAutoresizingFlexibleRightMargin | 视图右边界与父视图右边界的距离会按需要增大或减小。如果没有包含这个常量,将维持固定的距离 |
UIViewAutoresizingFlexibleBottomMargin | 视图下边界与父视图下边界的距离会按需要增大或减小。如果没有包含这个常量,将维持固定的距离 |
UIViewAutoresizingFlexibleTopMargin | 视图上边界与父视图上边界的距离会按需要增大或减小。如果没有包含这个常量,将维持固定的距离 |
4.3.3 手动调整视图布局
在自定义 view 中,如果自动布局行为没有达到期望要求,可以实现 layoutSubviews ,可以下面几件事:
- 调整任何当前子视图的大小和位置
- 添加移除子视图或者核心动画层(Core Animation layers)
- 调用 setNeedsDisplay 或者 setNeedsDisplayInRect: 方法强制子视图重绘
有大片滚动区域的时候应用会经常手动布局子视图。
写布局代码的时候测试代码对于下面情况是否完善:
- view方向改变的时候,确保布局对于所有支持方向都是正确的
- 确保你的代码对于 status bar高度的变化有适当的响应。
4.4 运行时修改视图
在 view controller 中:
- view controller 在显示 view 之前创建它们,可以从 nib 文件 load view 或者用代码创建它们。但这些views 不在需要的时候,销毁它们
- 当设备方向改变的时候, view controller 可能调整view 的大小和位置来匹配。对于新的方向,可能会隐藏一些view并且显示另外一些view
- view controller 管理可编辑的内容,可能添加一下额外的按钮,使得编辑更加的方便
4.5 与 Core Animation Layers 交互
每个 view 的专属 layer 属性。
4.5.1 改变 Layer 类关联的视图
view 被创建之后 layer关联view 的类型就不能改变了,每个 view 使用 layerClass 类方法指定 layer 对象的类,这个方法默认实现返回的是 CALayer 类,只能在子类中改变这个值,重写方法,返回一个不同的值。
view 将自己设置为它的layer对象的代理;view 拥有它的layer。view 和 layer 之间的关系不能改变。
4.5.2 在 view 中 嵌入 Layer 对象
自定义 layer 对象可以是任何 CALayer 的实例,不被任何 view拥有。自定义 layer 不能接收时间,或者参与到响应链中,但是可以绘制自己,并且可以响应父视图的大小变化或者根据核心动画层规则响应。
给 view 添加自定义 layer 的实例代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Create the layer.
CALayer* myLayer = [[CALayer alloc] init];
// Set the contents of the layer to a fixed image. And set
// the size of the layer to match the image size.
UIImage layerContents = [[UIImage imageNamed:@"myImage"] retain];
CGSize imageSize = layerContents.size;
myLayer.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height);
myLayer = layerContents.CGImage;
// Add the layer to the view.
CALayer* viewLayer = self.view.layer;
[viewLayer addSublayer:myLayer];
// Center the layer in the view.
CGRect viewBounds = backingView.bounds;
myLayer.position = CGPointMake(CGRectGetMidX(viewBounds), CGRectGetMidY(viewBounds));
// Release the layer, since it is retained by the view‘s layer
[myLayer release];
}
4.6 定义自定义 view
4.6.1 自定义视图的实现清单
自定义视图需要注意下面这些情况:
- 为 view 定义适当的初始化方法:
- 对于想要用代码创建 view,需要重写 initWithFrame: 方法或者定义一个自定义初始化方法
- 对于想要从 nib 文件中 加载view,需要重写 initWithCoder: 方法。使用这个方法初始化你的view并把它放入一个已知状态。
- 实现dealloc 方法,来处理自定义数据的清理
- 为了处理自定义的绘制,重写 drawRect: 方法并且在方法中进行绘制
- 设置视图 autoresizingMask 属性来定义它的自动调整大小行为。
- 如果你的 view class 管理一个或多个必需的子视图:
- 视图的初始化过程中创建这些子视图。
- 创建的时候设置每个子视图的 autoresizingMask 属性
- 如果子视图要求自定义布局,重写 layoutSubviews 方法,并实现你的布局
- 为了处理基于触摸的事件:
- 使用 addGestureRecognizer 方法给view添加合适的手势识别
- 为了处理触摸事件,重写 touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, touchesCancelled:withEvent: 方法。(不管有没有重写其他触摸方法,应该总是要重写 touchesCancelled:withEvent: 方法)
- 如果想要view 的打印版本再不同的屏幕版本上看起来不同,需要实现 drawRect:forViewPrintFormatter: 方法。详情看 Drawing and Printing Guide for iOS.
4.6.2 初始化自定义 view
view 应该包含 initWithFrame: 方法。
- (id)initWithFrame:(CGRect)aRect {
self = [super initWithFrame:aRect];
if (self) {
// setup the initial properties of the view
...
}
return self;
}
4.6.3 实现自己的绘图代码
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect myFrame = self.bounds;
// Set the line width to 10 and inset the rectangle by
// 5 pixels on all sides to compensate for the wider line.
CGContextSetLineWidth(context, 10);
CGRectInset(myFrame, 5, 5);
[[UIColor redColor] set];
UIRectFrame(myFrame);
}
4.6.4 响应事件
4.6.5 使用view 之后清理
5 Animations 动画
5.1 什么可以变为动画?
下面是UIView中可以用于动画的一些属性:
- frame
- bounds
- center
- transform
- alpha
- backgroundColor
- contentStretch
使用 Core Animation 可以对view 的layer 做下面类型的变化:
- layer 大小和位置
- 当执行过渡时候,使用的中点
- 在 3D 空间中 layer或者 sublayer 的变化
- 从 layer 层级中添加或者移除 layer
- 对于其他同级layer 的Z轴顺序
- layer 的阴影
- layer 的边界(包含边角是否是圆角)
- resize操作时候 layer 的部分伸缩
- layer 的不透明度
- 在 layer边界外面部分的裁剪行为
- layer 的当前内容
- layer 的光栅线
5.2 视图中的动画属性变化
5.2.1 使用基于 block 的方法开始动画
下面有3个类方法:
- animateWithDuration:animations:
- animateWithDuration:animations:completion:
- animateWithDuration:delay:options:animations:completion:
这些方法都是开了新线程执行动画,以防阻塞当前线程或者主线程。
[UIView animateWithDuration:1.0 animations:^{
firstView.alpha = 0.0;
secondView.alpha = 1.0;
}];
上面方法只是慢进慢出的单一动画形式,想要复杂的,必须使用 animateWithDuration:delay:options:animations:completion:
方法,可以自定义下面动画参数:
- 开始动画前的延迟
- 动画期间所使用的时序曲线类型
- 动画应该重复的次数
- 动画达到末尾的时候是否应该自动反转
- 动画进行的时候时候view 是否接收触摸事件
- 当前动画是否可以中断正在进行的任何其他动画,或者是等到其他都完成再开始
下面代码设置了渐隐动画,并且使用 completion handler,这是连接多个动画的基本方式
- (IBAction)showHideView:(id)sender
{
// Fade out the view right away
[UIView animateWithDuration:1.0
delay: 0.0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
thirdView.alpha = 0.0;
}
completion:^(BOOL finished){
// Wait one second and then fade in the view
[UIView animateWithDuration:1.0
delay: 1.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
thirdView.alpha = 1.0;
}
completion:nil];
}];
}
5.2.2 使用 Begin/Commit 方法开始动画
这是 iOS 3.2 之前使用的方法。。。
执行简单的 begin/commit 动画
[UIView beginAnimations:@"ToggleViews" context:nil];
[UIView setAnimationDuration:1.0];
// Make the animatable changes.
firstView.alpha = 0.0;
secondView.alpha = 1.0;
// Commit the changes and perform the animation.
[UIView commitAnimations];
配置动画参数:
// This method begins the first animation.
- (IBAction)showHideView:(id)sender
{
[UIView beginAnimations:@"ShowHideView" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(showHideDidStop:finished:context:)];
// Make the animatable changes.
thirdView.alpha = 0.0;
// Commit the changes and perform the animation.
[UIView commitAnimations];
}
// Called at the end of the preceding animation.
- (void)showHideDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
[UIView beginAnimations:@"ShowHideView2" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelay:1.0];
thirdView.alpha = 1.0;
[UIView commitAnimations];
}
5.2.3 配置动画代理
如果想要在动画开始前或者结束后立即执行代码,就需要将代理对象和 start or stop selector,与 begin/commit 动画块联结起来。使用UIView的类方法 setAnimationDelegate: 设置代理对象。使用 setAnimationWillStartSelector: 和 setAnimationDidStopSelector: 类方法设置开始和结束的 selector。
类似下面的代码:
- (void)animationWillStart:(NSString *)animationID context:(void *)context;
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context;
在基于 block 的动画方法中不需要使用上面这种形式。直接在动画block之前放置想要在动画前执行的代码,在 completion handler 里面放置想要在动画结束后执行的代码。
5.2.4 嵌套动画 block
被嵌套的动画与父动画同一时间开始,但可以以自己的配置选项运行。默认被嵌套的动画继承了父动画的持续时间和动画曲线。
有不同配置的嵌套动画
[UIView animateWithDuration:1.0
delay: 1.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
aView.alpha = 0.0;
// Create a nested animation that has a different
// duration, timing curve, and configuration.
[UIView animateWithDuration:0.2
delay:0.0
options: UIViewAnimationOptionOverrideInheritedCurve |
UIViewAnimationOptionCurveLinear |
UIViewAnimationOptionOverrideInheritedDuration |
UIViewAnimationOptionRepeat |
UIViewAnimationOptionAutoreverse
animations:^{
[UIView setAnimationRepeatCount:2.5];
anotherView.alpha = 0.0;
}
completion:nil];
}
completion:nil];
5.2.5 实现动画的逆行
5.3 在view 之间创建动画过渡
不要把 view transition 和 view controller 的变换搞混,view transition 只是影响 view 层级
5.3.1 改变视图的子视图
transitionWithView:duration:options:animations:completion: 方法。UIViewAnimationOptionAllowAnimatedContent 设置选项。
将空的文本视图与现有的进行交换
- (IBAction)displayNewPage:(id)sender
{
[UIView transitionWithView:self.view
duration:1.0
options:UIViewAnimationOptionTransitionCurlUp
animations:^{
currentTextView.hidden = YES;
swapTextView.hidden = NO;
}
completion:^(BOOL finished){
// Save the old text and then swap the views.
[self saveNotes:temp];
UIView* temp = currentTextView;
currentTextView = swapTextView;
swapTextView = temp;
}];
}
5.3.2 替换view
只是交换两个 view,不是 view controllers。
一个 view controller 中两个 view 之间的开关
- (IBAction)toggleMainViews:(id)sender {
[UIView transitionFromView:(displayingPrimary ? primaryView : secondaryView)
toView:(displayingPrimary ? secondaryView : primaryView)
duration:1.0
options:(displayingPrimary ? UIViewAnimationOptionTransitionFlipFromRight :
UIViewAnimationOptionTransitionFlipFromLeft)
completion:^(BOOL finished) {
if (finished) {
displayingPrimary = !displayingPrimary;
}
}];
}
5.4 将多个动画连接在一起
5.5 Animating View and Layer Changes Together
下面代码显示了同时修改view 和 自定义 layer 的动画。例子中的 view 包含了一个自定义 CALayer ,在view 的 bounds 中央。当顺时针旋转 layer 时,逆时针旋转 view。
Mixing view and layer animations
[UIView animateWithDuration:1.0
delay:0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
// Animate the first half of the view rotation.
CGAffineTransform xform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-180));
backingView.transform = xform;
// Rotate the embedded CALayer in the opposite direction.
CABasicAnimation* layerAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
layerAnimation.duration = 2.0;
layerAnimation.beginTime = 0; //CACurrentMediaTime() + 1;
layerAnimation.valueFunction = [CAValueFunction functionWithName:kCAValueFunctionRotateZ];
layerAnimation.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear];
layerAnimation.fromValue = [NSNumber numberWithFloat:0.0];
layerAnimation.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(360.0)];
layerAnimation.byValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(180.0)];
[manLayer addAnimation:layerAnimation forKey:@"layerAnimation"];
}
completion:^(BOOL finished){
// Now do the second half of the view rotation.
[UIView animateWithDuration:1.0
delay: 0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
CGAffineTransform xform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-359));
backingView.transform = xform;
}
completion:^(BOOL finished){
backingView.transform = CGAffineTransformIdentity;
}];
}];
苹果官方文档地址:View Programming Guide for iOS
新博客文章地址:View Programming Guide for iOS