🔏
iOS
  • 前言
  • View编程参考
    • View/Window架构
    • Window
    • View
    • Animations
  • ViewController编程参考
    • 定义
    • 呈现视图控制器
  • UIKit
    • 关于应用开发
    • 保护用户隐私
    • 应用和环境
      • 应用声明周期
      • 应用启动
      • UIApplication
      • UIApplicationDelegate
    • Windows and Screens
      • Window
      • Scene
      • Screen
    • 视图和控制
      • UIView
Powered by GitBook
On this page
  • 什么可以设置动画
  • 在视图中动画化属性更改
  • 使用基于块的方法开始动画
  • 配置开始/提交动画的参数
  • 配置动画委托
  • 嵌套动画块
  • 实现颠倒自己的动画
  • 在视图之间创建动画过渡
  • 更改视图的子视图
  • 用其他视图替换视图
  • 将多个动画链接在一起
  • 一起对视图和图层更改进行动画处理

Was this helpful?

  1. View编程参考

Animations

动画在用户界面的不同状态之间提供了流畅的视觉过渡。在iOS中,动画被广泛用于重新放置视图,更改其大小,将其从视图层次结构中移除以及隐藏它们。您可能会使用动画将反馈传达给用户或实现有趣的视觉效果。

在iOS中,创建复杂的动画不需要您编写任何绘图代码。本章中描述的所有动画技术都使用Core Animation提供的内置支持。您要做的就是触发动画,然后让Core Animation处理单个帧的渲染。这使得仅需几行代码即可轻松创建复杂的动画。

什么可以设置动画

UIKit和Core Animation都提供了对动画的支持,但是每种技术提供的支持级别各不相同。在UIKit中,动画是使用UIView对象执行的。视图支持一组涵盖许多常见任务的基本动画。例如,您可以为视图属性设置动画效果,或使用过渡动画将一组视图替换为另一组视图。

下表列出了UIView类的可设置动画的属性(具有内置动画支持的属性)。具有动画效果并不意味着动画会自动发生。更改这些属性的值通常只需要立即更新属性(和视图),没有动画。要对此类更改进行动画处理,必须从动画块内部更改属性的值,这在“对视图中的属性更改进行动画处理”中进行了介绍。

属性

动作

frame

修改此属性以更改视图相对于其父视图坐标系的大小和位置。 (如果transform属性不包含恒等变换,请改为修改边界或center属性。)

bounds

修改此属性以更改视图的大小

center

修改此属性以更改视图相对于其父视图坐标系的位置。

transform

修改此属性以相对于其中心点缩放,旋转或平移视图。使用此属性的变换始终在2D空间中执行。 (要执行3D转换,您必须使用Core Animation为视图的图层对象设置动画。)

alpha

修改此属性以逐渐更改视图的透明度。

backgroundColor

修改此属性以更改视图的背景色。

contentStretch

修改此属性可更改视图内容被拉伸以填充可用空间的方式。

动画视图过渡是您对视图层次进行更改的一种方法,可以超越视图控制器提供的那些方法。尽管您应该使用视图控制器来管理简洁的视图层次结构,但是有时您可能希望替换全部或部分视图层次结构。在这种情况下,您可以使用基于视图的过渡来为视图的添加和删除添加动画效果。

在您想要执行更复杂的动画或UIView类不支持的动画的地方,可以使用Core Animation和视图的基础层来创建动画。由于视图和图层对象错综复杂地链接在一起,因此对视图图层的更改会影响视图本身。使用Core Animation,可以为视图的图层设置以下类型的动画:

  • 图层的大小和位置

  • 执行转换时使用的中心点

  • 转换为3D空间中的图层或其子图层

  • 从图层层次结构中添加或删除图层

  • 图层相对于其他同级图层的Z顺序

  • 图层的阴影

  • 图层的边框(包括图层的角是否圆角)

  • 调整大小操作期间拉伸的层部分

  • 图层的不透明度

  • 超出图层范围的子图层的裁剪行为

  • 图层的当前内容

  • 图层的栅格化行为

如果视图承载自定义图层对象(即没有关联视图的图层对象),则必须使用“核心动画”为它们进行任何动画处理。

在视图中动画化属性更改

为了使对UIView类的属性所做的更改具有动画效果,必须将这些更改包装在动画块中。术语动画块在一般意义上用于指代指定可动画更改的任何代码。在iOS 4和更高版本中,您可以使用块对象创建动画块。在iOS的早期版本中,您可以使用UIView类的特殊类方法标记动画块的开始和结束。两种技术都支持相同的配置选项,并对动画执行提供相同数量的控制。但是,尽可能使用基于块的方法。

使用基于块的方法开始动画

在iOS 4和更高版本中,您使用基于块的类方法来启动动画。有几种基于块的方法可为动画块提供不同级别的配置。这些方法是:

  • 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:方法来执行动画。此方法使您可以自定义以下动画参数:

  • 开始动画之前的延迟时间

  • 动画期间要使用的时序曲线的类型

  • 动画应重复的次数

  • 动画到达终点时是否应自动反转自身

  • 动画进行期间是否将触摸事件传递给视图

  • 动画是应该中断正在进行的动画,还是等到完成后再开始播放

animateWithDuration:animations:completion:和animateWithDuration:delay:options:animations:completion:方法支持的另一件事是可以指定完成处理程序块。您可以使用完成处理程序来向您的应用程序发出特定动画已完成的信号。完成处理程序也是将单独的动画链接在一起的方法。

以下代码显示了一个动画块的示例,该动画块在第一个动画完成后使用完成处理程序来启动新动画。第一步调用animateWithDuration:delay:options:animations:completion:设置淡出动画并使用一些自定义选项对其进行配置。该动画完成后,其完成处理程序将运行并设置动画的后半部分,这将在延迟后使视图淡入。

使用完成处理程序是链接多个动画的主要方式。

- (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];
        }];
}

在涉及该属性的动画已经在进行中时更改属性的值不会停止当前动画。取而代之的是,当前动画将继续并设置为刚刚分配给该属性的新值的动画。

配置开始/提交动画的参数

要为开始/提交动画块配置动画参数,请使用几种UIView类方法中的任何一种。表4-2列出了这些方法,并描述了如何使用它们来配置动画。这些方法中的大多数仅应从开始/提交动画块内部调用,但有些方法也可以与基于块的动画一起使用。如果您没有从动画块中调用这些方法之一,则使用相应属性的默认值。

方法

使用

setAnimationStartDate: setAnimationDelay:

使用这两种方法之一指定执行应该何时开始执行。如果指定的开始日期是过去的时间(或延迟为0),则动画将尽快开始。

setAnimationDuration:

使用此方法可以设置执行动画的时间。

setAnimationCurve:

使用此方法可以设置动画的时间曲线。这控制动画是线性执行还是在特定时间更改速度。

setAnimationRepeatCount: setAnimationRepeatAutoreverses:

使用这些方法可以设置动画重复的次数,以及动画在每个完整循环结束时是否反向运行。

setAnimationDelegate: setAnimationWillStartSelector: setAnimationDidStopSelector:

使用这些方法在动画之前或之后立即执行代码。

setAnimationBeginsFromCurrentState:

使用此方法可以立即停止所有先前的动画,并从停止点开始新的动画。如果将NO传递给此方法,而不是YES,则新动画直到之前的动画停止后才开始执行。

和以前一样,此代码淡出视图,等待一秒钟,然后淡入。为了实现动画的第二部分,该代码设置了动画委托并实现了did-stop处理程序方法。然后,该处理程序方法将设置动画的后半部分并运行它们。

配置动画委托

如果要在动画之前或之后立即执行代码,则必须将一个委托对象和一个开始或停止选择器与您的开始/提交动画块关联。您可以使用UIView的setAnimationDelegate:类方法设置委托对象,并使用setAnimationWillStartSelector:和setAnimationDidStopSelector:类方法设置开始和停止选择器。在动画期间,动画系统会在适当的时间调用您的委托方法,以使您有机会执行代码。

动画委托方法的签名必须类似于以下内容:

- (void)animationWillStart:(NSString *)animationID context:(void *)context;
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context;

这两种方法的animationID和上下文参数与您在动画块开始处传递给beginAnimations:context:方法的参数相同:

  • animationID - 应用程序提供的字符串,用于标识动画。

  • context - 应用程序提供的对象,可用于将其他信息传递给委托。

setAnimationDidStopSelector:选择器方法还有一个附加参数-一个布尔值,如果动画运行完成则为YES。如果此参数的值为NO,则该动画已被另一个动画取消或过早停止。

尽管可以在基于块的方法中使用动画委托,但通常无需在其中使用它们。取而代之的是,将要运行的所有代码放置在块开头的动画之前,并将要在动画结束后运行的所有代码放置在完成处理程序中。

嵌套动画块

您可以通过嵌套其他动画块来为动画块的各个部分分配不同的时序和配置选项。顾名思义,嵌套动画块是在现有动画块内创建的新动画块。嵌套动画将与任何父动画同时启动,但(在大多数情况下)将使用其自己的配置选项运行。默认情况下,嵌套动画的确会继承父级的持续时间和动画曲线,但是即使需要,这些选项也可以被覆盖。

以下代码显示了一个示例,说明如何使用嵌套动画更改整个组中某些动画的时间,持续时间和行为。在这种情况下,两个视图被淡化为完全透明,但是anotherView对象的透明性在最终被隐藏之前来回更改了几次。嵌套动画块中使用的UIViewAnimationOptionOverrideInheritedCurve和UIViewAnimationOptionOverrideInheritedDuration键可为第二个动画修改第一个动画的曲线和持续时间值。如果不存在这些关键点,则将使用外部动画块的持续时间和曲线。

[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];

在已经打开的动画块中对beginAnimations:context:的每个后续调用都会创建一个新的嵌套动画块,您可以根据需要进行配置。您所做的任何配置更改都将应用于最近打开的动画块。在提交和执行动画之前,必须通过调用commitAnimations关闭所有动画块。

实现颠倒自己的动画

与重复计数一起创建可逆动画时,请考虑为重复计数指定非整数值。对于自动反转动画,动画的每个完整周期都涉及从原始值到新值的动画设置,然后再次返回。如果您希望动画以新值结束,则将重复计数增加0.5将使动画完成以新值结束所需的额外半周期。如果不包括此半步,则动画将设置为原始值的动画,然后迅速捕捉为新值,这可能不是您想要的视觉效果。

在视图之间创建动画过渡

视图过渡可帮助您隐藏与在视图层次结构中添加,删除,隐藏或显示视图相关的突然更改。您可以使用视图转换来实现以下类型的更改:

  • 更改现有视图的可见子视图。当您想对现有视图进行相对较小的更改时,通常选择此选项。

  • 将视图层次结构中的一个视图替换为另一视图。当您要替换横跨整个屏幕或大部分屏幕的视图层次结构时,通常选择此选项。

视图过渡不应与视图控制器启动的过渡相混淆,例如模式视图控制器的表示或将新视图控制器推送到导航堆栈上。视图过渡仅影响视图层次结构,而视图控制器过渡也会更改活动视图控制器。因此,对于视图过渡,启动过渡时处于活动状态的视图控制器在过渡结束时仍处于活动状态。

更改视图的子视图

更改视图的子视图可让您对视图进行适度的更改。例如,您可以添加或删除子视图以在两个不同状态之间切换超级视图。动画结束时,将显示相同的视图,但现在其内容有所不同。

在iOS 4和更高版本中,您可以使用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;
        }];
}

用其他视图替换视图

当您希望界面有很大不同时,您可以执行替换视图的操作。由于此技术仅交换视图(而不交换视图控制器),因此您有责任适当地设计应用程序的控制器对象。这项技术只是使用某些标准转换快速呈现新视图的一种方式。

在iOS 4及更高版本中,您可以使用transitionFromView:toView:duration:options:completion:方法在两个视图之间进行转换。此方法实际上是从层次结构中删除第一个视图,然后插入另一个视图,因此,如果要保留它,则应确保对第一个视图具有引用。如果要隐藏视图而不是将其从视图层次结构中删除,请传递UIViewAnimationOptionShowHideTransitionViews键作为选项之一。

以下代码显示了在由一个视图控制器管理的两个主视图之间交换所需的代码。在此示例中,视图控制器的根视图始终显示两个子视图(primaryView或secondaryView)之一。每个视图呈现相同的内容,但呈现方式不同。视图控制器使用displayingPrimary成员变量(一个布尔值)来跟踪在任何给定时间显示哪个视图。翻转方向根据所显示的视图而变化。

- (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;
            }
    }];
}

除了换出视图外,视图控制器代码还需要管理主视图和辅助视图的加载和卸载。

将多个动画链接在一起

UIView动画界面提供了对链接单独的动画块的支持,以便它们顺序执行而不是同时执行。使用animateWithDuration:animations:completion:和animateWithDuration:delay:options:animations:completion:方法支持的完成处理程序来执行任何后续动画。

将动画链接在一起的另一种方法是使用具有不同延迟因子的嵌套动画,以便在不同时间启动动画。

一起对视图和图层更改进行动画处理

应用程序可以根据需要自由混合基于视图的动画和基于图层的动画代码,但是配置动画参数的过程取决于谁拥有该图层。更改视图拥有的图层与更改视图本身相同,并且您应用于该图层的属性的任何动画均会遵守当前基于视图的动画块的动画参数。对于您自己创建的图层,情况并非如此。自定义图层对象将忽略基于视图的动画块参数,而使用默认的“核心动画”参数。

如果要为创建的图层自定义动画参数,则必须直接使用Core Animation。通常,使用Core Animation设置动画层涉及创建CABasicAnimation对象或CAAnimation的其他一些具体子类。然后,将该动画添加到相应的图层。您可以在基于视图的动画块的内部或外部应用动画。

以下代码显示了一个动画,该动画可以同时修改视图和自定义层。此示例中的视图在其边界的中心包含一个自定义CALayer对象。动画将视图逆时针旋转,同时顺时针旋转图层。由于旋转方向相反,因此该层相对于屏幕保持其原始方向,并且看起来没有明显旋转。但是,该层下面的视图旋转360度并返回其原始方向。给出此示例的主要目的是演示如何混合使用视图动画和图层动画。在需要精确定时的情况下,不应使用这种类型的混合。

[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;
         }];
}];

您还可以在基于视图的动画块之外创建并应用CABasicAnimation对象,以实现相同的结果。所有动画最终都依赖Core Animation来执行。因此,如果它们大约同时提交,则它们将一起运行。

如果需要在基于视图的动画和基于图层的动画之间进行精确计时,则建议您使用“核心动画”创建所有动画。您可能会发现,无论如何,使用Core Animation更容易执行某些动画。例如,以上代码的基于视图的旋转需要一序列步骤才能旋转180度以上,而“核心动画”部分使用的旋转值函数从开始到结束一直旋转到中间值。

PreviousViewNextViewController编程参考

Last updated 4 years ago

Was this helpful?