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),如果之后再没有对内容改动,那么不会再调用绘图代码,都直接使用这个快拍;如果有了改动,就重新生成一个快拍,周而复始。

当视图的内容或者外观发生变化的时候可以使用 setNeedsDisplaysetNeedsDisplayInRect 方法进行重绘;注意如果改变了视图的几何形状,这个方法就失效了。这两个方法是等待当前 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 监视窗口的改变

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, transformcenter 和 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 自定义视图的实现清单

自定义视图需要注意下面这些情况:

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个类方法:

这些方法都是开了新线程执行动画,以防阻塞当前线程或者主线程。

[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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值