首页 > 代码库 > View Programming Guide for iOS 视图编程指南 - iOS

View Programming Guide for iOS 视图编程指南 - iOS

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 内容模式

当发生下列两种情况时,内容模式就会应用:

  • 改变视图的 frame 或者 bounds 矩形的宽或高。
  • 指定一个转换,比如视图的 transform 属性的缩放比例
@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 的局部坐标系中转换坐标

  • convertPoint:fromView:
  • convertRect:fromView:
  • convertPoint:toView:
  • convertRect:toView:

类似,UIWindow也定义了几个转换方法:

  • convertPoint:fromWindow:
  • convertRect:fromWindow:
  • convertPoint:toWindow:
  • convertRect:toWindow:


技术分享

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

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    View Programming Guide for iOS 视图编程指南 - iOS