Source-SCCamera/Features/TapToFocus/SCTapAnimationView.m

179 lines
6.9 KiB
Objective-C

//
// SCTapAnimationView.m
// SCCamera
//
// Created by Alexander Grytsiuk on 8/26/15.
// Copyright (c) 2015 Snapchat, Inc. All rights reserved.
//
#import "SCTapAnimationView.h"
#import <SCBase/SCMacros.h>
@import QuartzCore;
static const CGFloat kSCAnimationStep = 0.167;
static const CGFloat kSCInnerCirclePadding = 2.5;
static const CGFloat kSCTapAnimationViewWidth = 55;
static const CGFloat kSCOuterRingBorderWidth = 1;
static NSString *const kSCOpacityAnimationKey = @"opacity";
static NSString *const kSCScaleAnimationKey = @"scale";
@implementation SCTapAnimationView {
CALayer *_outerRing;
CALayer *_innerCircle;
}
#pragma mark Class Methods
+ (instancetype)tapAnimationView
{
return [[self alloc] initWithFrame:CGRectMake(0, 0, kSCTapAnimationViewWidth, kSCTapAnimationViewWidth)];
}
#pragma mark Life Cycle
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.userInteractionEnabled = NO;
_outerRing = [CALayer layer];
_outerRing.backgroundColor = [UIColor clearColor].CGColor;
_outerRing.borderColor = [UIColor whiteColor].CGColor;
_outerRing.borderWidth = kSCOuterRingBorderWidth;
_outerRing.shadowColor = [UIColor blackColor].CGColor;
_outerRing.shadowOpacity = 0.4;
_outerRing.shadowOffset = CGSizeMake(0.5, 0.5);
_outerRing.opacity = 0.0;
_outerRing.frame = self.bounds;
_outerRing.cornerRadius = CGRectGetMidX(_outerRing.bounds);
[self.layer addSublayer:_outerRing];
_innerCircle = [CALayer layer];
_innerCircle.backgroundColor = [UIColor whiteColor].CGColor;
_innerCircle.opacity = 0.0;
_innerCircle.frame = CGRectInset(self.bounds, kSCInnerCirclePadding, kSCInnerCirclePadding);
_innerCircle.cornerRadius = CGRectGetMidX(_innerCircle.bounds);
[self.layer addSublayer:_innerCircle];
}
return self;
}
#pragma mark Public
- (void)showWithCompletion:(SCTapAnimationViewCompletion)completion
{
[_outerRing removeAllAnimations];
[_innerCircle removeAllAnimations];
[CATransaction begin];
[CATransaction setCompletionBlock:^{
if (completion) {
completion(self);
}
}];
[self addOuterRingOpacityAnimation];
[self addOuterRingScaleAnimation];
[self addInnerCircleOpacityAnimation];
[self addInnerCircleScaleAnimation];
[CATransaction commit];
}
#pragma mark Private
- (CAKeyframeAnimation *)keyFrameAnimationWithKeyPath:(NSString *)keyPath
duration:(CGFloat)duration
values:(NSArray *)values
keyTimes:(NSArray *)keyTimes
timingFunctions:(NSArray *)timingFunctions
{
CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:keyPath];
keyframeAnimation.duration = duration;
keyframeAnimation.values = values;
keyframeAnimation.keyTimes = keyTimes;
keyframeAnimation.timingFunctions = timingFunctions;
keyframeAnimation.fillMode = kCAFillModeForwards;
keyframeAnimation.removedOnCompletion = NO;
return keyframeAnimation;
}
- (CABasicAnimation *)animationWithKeyPath:(NSString *)keyPath
duration:(CGFloat)duration
fromValue:(NSValue *)fromValue
toValue:(NSValue *)toValue
timingFunction:(CAMediaTimingFunction *)timingFunction
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath];
animation.duration = duration;
animation.fromValue = fromValue;
animation.toValue = toValue;
animation.timingFunction = timingFunction;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
- (void)addOuterRingOpacityAnimation
{
CAKeyframeAnimation *animation =
[self keyFrameAnimationWithKeyPath:@keypath(_outerRing, opacity)
duration:kSCAnimationStep * 5
values:@[ @0.0, @1.0, @1.0, @0.0 ]
keyTimes:@[ @0.0, @0.2, @0.8, @1.0 ]
timingFunctions:@[
[CAMediaTimingFunction functionWithControlPoints:0.0:0.0:0.0:1.0],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
]];
[_outerRing addAnimation:animation forKey:kSCOpacityAnimationKey];
}
- (void)addOuterRingScaleAnimation
{
CAKeyframeAnimation *animation =
[self keyFrameAnimationWithKeyPath:@keypath(_innerCircle, transform)
duration:kSCAnimationStep * 3
values:@[
[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.50, 0.50, 1.0)],
[NSValue valueWithCATransform3D:CATransform3DIdentity],
[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.83, 0.83, 1.0)],
]
keyTimes:@[ @0.0, @0.66, @1.0 ]
timingFunctions:@[
[CAMediaTimingFunction functionWithControlPoints:0.0:0.0:0.0:1.0],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
]];
[_outerRing addAnimation:animation forKey:kSCScaleAnimationKey];
}
- (void)addInnerCircleOpacityAnimation
{
CAKeyframeAnimation *animation =
[self keyFrameAnimationWithKeyPath:@keypath(_innerCircle, opacity)
duration:kSCAnimationStep * 3
values:@[ @0.0, @0.40, @0.0 ]
keyTimes:@[ @0.0, @0.33, @1.0 ]
timingFunctions:@[
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
]];
[_innerCircle addAnimation:animation forKey:kSCOpacityAnimationKey];
}
- (void)addInnerCircleScaleAnimation
{
CABasicAnimation *animation =
[self animationWithKeyPath:@keypath(_innerCircle, transform)
duration:kSCAnimationStep * 2
fromValue:[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.0, 0.0, 1.0)]
toValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]
timingFunction:[CAMediaTimingFunction functionWithControlPoints:0.0:0.0:0.0:1.0]];
[_innerCircle addAnimation:animation forKey:kSCScaleAnimationKey];
}
@end