我们已经初步了解核心动画并且可以通过优秀的JNWSpringAnimation框架来模仿一些弹簧系统效果,现在该实际演练了!
复制一个iOS的Alert View 效果
复制一个熟悉的提示窗是一个很好地熟悉动画实现过程的方法。接下来让我们来创建属于自己的提示框吧。
下面是一个内置提示框的一个效果。
之前的部分已经解释过,将动画效果分解为各个组成部分的重要性,只有将他们进行分解,才能更好地去实现它。但是,这并不是简单的说“提示框显示到屏幕上”,你必须要知道每一步或者每个时间点发生了什么。让我们开始分解这个动画效果吧。
- 屏幕开始的时候有一个渐入的半透明的遮盖
- 提示框从完全透明并且1.x尺寸变化到完全不透明并且是原始尺寸。
- 在消失的时候重新变回完全透明并且缩放比例变为小于原始比例。
- 移除黑色遮盖
在我们开始写代码之前,让我们来看一下我们所要完成的最终效果。
首先,我们应该创建一个白色背景的系统window,只要应用一启动,应用的代理文件就是运行这段代码。你可以把这段代码写在Alert View 1 Xcode项目中。
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Construct the main window for this application
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// All additional code in this example will go right here
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
现在,我们已经有了一个正好覆盖整个屏幕的View了,并且backgroundColor
被设置为白色。如果我们现在运行这段代码,只会在模拟器或者是手机上(如果你连接了手机的话)加载一个空白的额View。接着,我们需要往window上添加一个遮盖,将透明度设置为完全透明,因为我们现在还不想显示它。
UIView *overlayView = [[UIView alloc] initWithFrame:self.window.bounds];
overlayView.backgroundColor = [UIColor blackColor];
overlayView.alpha = 0.0f;
[self.window addSubview:overlayView];
这个overlayView
只是一个覆盖整个屏幕的一个普通的UIView对象而已。为了展示我们现在所实现的一些效果,我们暂时将遮盖设置为完全不透明,下面就是现在的成果喽。
【黑色遮盖】
我们接着刚才的代码开始继续我们的提示框,但本例为了简化操作,使用了图片image而不是纯代码构建的文本框和按钮。
CGFloat alertDimension = 250;
CGRect alertViewFrame = CGRectMake(
self.window.bounds.size.width/2 - alertDimension/2,
self.window.bounds.size.height/2 - alertDimension/2,
alertDimension, alertDimension);
UIView *alertView = [[UIView alloc] initWithFrame:alertViewFrame];
首先,我们需要创建的UIView对象会展示在屏幕的中央。这样可以通过屏幕的宽度和高度除以2,然后减去提示框一半的宽度和高度来得到提示框的位置。相对来说,我更喜欢通过一次性的设置动画的最终位置,然后根据transform
属性来修改不同位置的不同大小。
alertView.backgroundColor = [UIColor colorWithPatternImage:
[UIImage imageNamed:@"alert_box"]];
alertView.alpha = 0.0f;
alertView.transform = CGAffineTransformMakeScale(1.2, 1.2);
alertView.layer.cornerRadius = 10;
上面这段代码里,我们总共做了4件事:
- 将提示框的背景色
backgroundColor
设置为图片。 - 将透明度
alpha
设置为0,这样就会知道我们将它完全显示出来的时候他才会出现 - 通过
CGAffineTransformMakeScale()
将transform
属性设置为大于原始尺寸的大小。 - 通过
cornerRadius
来给提示框设置圆角属性。
接下来我们需要给它设置一些细微的阴影效果。
alertView.layer.shadowColor = [UIColor blackColor].CGColor;
alertView.layer.shadowOffset = CGSizeMake(0, 5);
alertView.layer.shadowOpacity = 0.3f;
alertView.layer.shadowRadius = 10.0f;
[self.window addSubview:alertView];
如果将遮盖的透明度设置为完全不透明,并且移除缩放的话,现在我们得到的结果是这样的:
是时候添加一些动画效果了。对于遮盖来说,我们希望从不可见的完全透明到半透明。我们希望给提示框添加两个动画效果:opacity属性从0.0 变到 1.0,并且缩放比例从>1.0x的比例变化到1.0x大小。这就是我们要模仿的iOS7的提示框效果。
我们先来处理这两个动画效果(遮盖和提示框View),因为透明opacity属性的变化并不需要弹簧动画效果,我们使用一个简单的block来实现:
// Fade in the grey overlay and alert view
[UIView animateWithDuration:.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut
animations:^{
overlayView.alpha = 0.3f;
alertView.alpha = 1.0f;
} completion:NULL];
我们在一个block中实现了遮盖和提示框的透明度的变化。因为我想让他们同时以同样的方式出现在用户面前,但是为什么不一起使他们发生变化呢?我将动画持续时间设置为0.3s,当给用户展示重要信息的时候,就像iOS7里的提示框那样,一个放松的效果会使用户觉得更重要。相比较一个快速的动画效果而言,一个缓慢的动画效果会使用户觉得这个信息更加的重要并且需要多加注意。
现在我们该对提示框的尺寸进行改变了,我想用弹簧效果给这个提示框的出现增加一些趣味,而不仅仅是上面的那种简单的block动画方式。在标准的iOS提示框中,苹果并没有使用震荡效果达到最终的值而是使用了一种缓慢衰减的方法达到最终的位置和效果。我们将通过设置damping
和stiffness
的属性值来达到类似的效果。
// Scale-animate in the alert view
JNWSpringAnimation *scale = [JNWSpringAnimation animationWithKeyPath:@"transform.scale"];
scale.damping = 14;
scale.stiffness = 14;
scale.mass = 1;
scale.fromValue = @(1.2);
scale.toValue = @(1.0);
[alertView.layer addAnimation:scale forKey:scale.keyPath];
alertView.transform = CGAffineTransformMakeScale(1.0, 1.0);
大家可以运行一下看看,还不错吧?接下来我们需要处理消失的时候的动画效果。
像我们之前说过的,我们想让一个提示框在出现的时候是以一种缓慢的方式出现的,但是在提示框消失的时候,我不知道大家是怎么样的,反正我特希望提示框快速消失,回到我之前被打断的地方。
// Fade out the grey overlay and alert view
[UIView animateWithDuration:.15 delay:0 options:UIViewAnimationOptionCurveEaseInOut
animations:^{
overlayView.alpha = 0.0f;
alertView.alpha = 0.0f;
} completion:NULL];
既然我们回到了最初的动画,现在我们需要遮盖和提示框的透明度都设置为完全透明的状态。又因为我想这两个一起进行动画效果,我将它们放在同一个block中。值得注意的是,提示框的淡出动画持续时间要比淡入动画的持续时间缩短了一半。我们希望这个提示框能够轻快的淡出屏幕。
接下来我们需要在透明度变为完全透明的时候将提示框缩小为更小。
// Scale-animate out the alert view
JNWSpringAnimation *scaleOut = [JNWSpringAnimation
animationWithKeyPath:@"transform.scale"];
scaleOut.damping = 11;
scaleOut.stiffness = 11;
scaleOut.mass = 1;
scaleOut.fromValue = @(1.0);
scaleOut.toValue = @(0.7);
[alertView.layer addAnimation:scaleOut forKey:scaleOut.keyPath];
alertView.transform = CGAffineTransformMakeScale(0.7, 0.7);
创建更加高级的提示框
既然我们已经复制了系统自带的提示框的效果,那么我们要不要开始做一些自定义的具有更好效果的提示框呢?我们可以开启一个新的Alert View 2Xcode 项目。
为了实现这个动画效果,UIView和之前的例子中的属性有着相似的地方,但是这次我们要同时更新transform
属性的translation
和scale
.
alertView.transform = CGAffineTransformMakeScale(.25, .25);
alertView.transform = CGAffineTransformTranslate(alertView.transform, 0, 600);
这样会让提示框的大小缩放为原来的0.25倍大小,而且同时下移到屏幕底部。你也可以这样来同时实现上面的效果:
CGAffineTransform viewTransform = CGAffineTransformConcat(
CGAffineTransformMakeScale(.25, .25), CGAffineTransformMakeTranslation(0, 600));
alertView.transform = viewTransform;
现在UIView对象已经缩放为原来的0.25倍,并且移动到屏幕底部。我们可以在将提示框变为1.0的同时移动到开始的屏幕中央的位置。我们仍然可以遮盖以同样的状态淡出。
// Fade the overlay and alert view together
UIView animateWithDuration:.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut
animations:^{
overlayView.alpha = 0.2f;
alertView.alpha = 1.0f;
} completion:NULL];
// Animate the alert’s scale from .25 up to 1.0
JNWSpringAnimation *scale =
[JNWSpringAnimation animationWithKeyPath:@"transform.scale"];
scale.damping = 12;
scale.stiffness = 12;
scale.mass = 1;
scale.fromValue = @(.25);
scale.toValue = @(1.0);
[alertView.layer addAnimation:scale forKey:scale.keyPath];
alertView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0);
// Animate the position from the starting Y position of 600 back up to 0
// which puts it back at the original position
JNWSpringAnimation *translate = [JNWSpringAnimation
animationWithKeyPath:@"transform.translation.y"];
translate.damping = 15;
translate.stiffness = 15;
translate.mass = 1;
translate.fromValue = @(600);
translate.toValue = @(0);
[alertView.layer addAnimation:translate forKey:translate.keyPath];
alertView.transform = CGAffineTransformTranslate(alertView.transform, 0, 0);