contributors | originalUrl |
---|---|
antonio081014 |
Spring Animation is the base of almost every animation in the iOS 8 system, which means almost all of the animations in iOS 8 are using spring animation. It was introduced in iOS 7 via one single function:
+animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:
This function is very similar with ordinary animation functions we used to using a lot. The only difference here is two additional parameters: Damping Ratio and Initial Spring Velocity.
- The damping ratio defines how much the oscillation oscillate at the end state of the animation, ranging from
0.0
to1.0
.0.0
defines high frequency oscillation, while1.0
defines no oscillation at all. This is place where can help us make a bouncing effect. - The initial spring velocity defines how fast the animation starts. The smaller the volecity is, the sooner the animation object will move to the end state, verse vice.
The following demo code imitates the Animation of Opening Folder on iOS Home Screen:
const static CGFloat BOXSIZE = 100.f;
- (void)tapped:(UITapGestureRecognizer *)gesture
{
[self initBox];
[UIView animateWithDuration:1.f
delay:0.f
usingSpringWithDamping:.75f
initialSpringVelocity:.5f
options:0
animations:^{
[self endBox];
} completion:^(BOOL finished) {
//
}];
}
- (void)initBox
{
self.movingBox.frame = CGRectMake(0.f, self.view.bounds.size.height - BOXSIZE, BOXSIZE, BOXSIZE);
}
- (void)endBox
{
self.movingBox.frame = CGRectMake(CGRectGetMidX(self.view.bounds) - BOXSIZE * 3.f / 2.f, CGRectGetMidY(self.view.bounds) - BOXSIZE * 3.f / 2.f, BOXSIZE * 3.f, BOXSIZE * 3.f);
}
In iOS 8, UIBlurEffect
and UIVibrancyEffect
are introduced for developers have Dynamic Blur Effect more easily. In the talk, Apple still recommends using static blur effect code if dynamic blur effect views are not actually what you need, since dynamic blur effect takes a lot of computing power.
The UIBlurEffect
gives blur effect to an associated UIView
, while UIVibrancyEffect
gives the clear part which is not blurred on the blurred view. So, this is a three layer structure. UIView
at the bottom, then comes the UIBlurEffect
, at last the UIVibrancyEffect
comes. The UIVibrancyEffect
consumes the most computing power. The UIBlurEffect
gives three styles for the developer to use, UIBlurEffectStyleExtraLight
, UIBlurEffectStyleLight
, UIBlurEffectStyleDark
.
The example could be found at Siri User Interface, where user could clearly see the app icons on the Home Screen of the iPhone.
This section covers CAShapeLayer
.
How to use CAShapeLayer
back up the UIView
as the base layer. In UIView
subclass,
+ (Class)layerClass {
return [CAShapeLayer Class];
}
- (void)awakeFromNib {
CAShapeLayer *layer = (CAShapeLayer *)self.layer;
// init layer properties here.
// Also, everytime to update layer property, convert it to CAShaperLayer and use a local variable for it.
}
How to draw part of the path for CAShapeLayer
, by setting strokeStart
and strokeEnd
.
This is probably the most challenging part for me, so many sample code on CABasicAnimation
. I think have a good use of CAAction
Protocol and CALayerDelegate
method could greatly help your custom UIView
subclass have great animation effect when UIView
’s properties are changed. Also, the developer could define customized property, which could be very powerful. By using this technique, developers could not only customize animation behavior, but also disable system’s default animation behaviors.
Here is a simple piece of demo:
#import "CustomAnimationView.h"
@interface CustomAnimationAction : NSObject <CAAction>
@end
@implementation CustomAnimationAction
- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict
{
if ([event isEqualToString:@"opacity"]) {
CALayer *layer = (CALayer *)anObject;
CFTimeInterval duration = .75;
CABasicAnimation *bgAnimation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
bgAnimation.duration = duration;
bgAnimation.fromValue = (id)[layer.presentationLayer backgroundColor];
bgAnimation.toValue = (id)([UIColor colorWithHue:layer.opacity saturation:1.f brightness:1.f alpha:1.f].CGColor);
bgAnimation.fillMode = kCAFillModeForwards;
bgAnimation.removedOnCompletion = NO;
[layer addAnimation:bgAnimation forKey:@"bgColorAnim"];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.duration = duration;
animation.fromValue = @([layer.presentationLayer opacity]);
animation.toValue = @(layer.opacity);
animation.removedOnCompletion = NO;
[layer addAnimation:animation forKey:@"customKey"];
}
}
@end
@implementation CustomAnimationView
+ (Class)layerClass
{
return [CAShapeLayer class];
}
- (void)awakeFromNib
{
[self setup];
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
- (void)setup
{
CAShapeLayer *layer = (CAShapeLayer *)self.layer;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
[layer setPath:path.CGPath];
[layer setStrokeColor:[UIColor purpleColor].CGColor];
[layer setLineWidth:6];
[layer setFillColor:nil];
[layer setLineCap:kCALineCapRound];
[layer setLineJoin:kCALineJoinBevel];
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
[self addGestureRecognizer:gesture];
}
- (void)tapped:(UITapGestureRecognizer *)gesture
{
[UIView animateWithDuration:1.0 animations:^{
[self setAlpha:(CGFloat)(arc4random() % 10) / 10.f];
}];
}
- (id<CAAction>)actionForLayer:(CALayer *)layer
forKey:(NSString *)key
{
if ([key isEqualToString:@"opacity"]) {
return [[CustomAnimationAction alloc] init];
}
return [super actionForLayer:layer forKey:key];
}
@end
Worth to mention here: when Custom View's frame origin changed, the Key for the change will be "Position", rather than "Frame". This is because UIView
is backed by CALayer
, where essentially all the animations added to. My guess would be: any animation of changing property of UIView
will be "mirrored" to some action of changing property of CALayer
.
For more information, here is a link to the Apple Developer Official Page.