Created
June 30, 2015 22:34
-
-
Save imownbey/0a7039fc5c1c648f5c84 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// UIView+TimedAnimations.m | |
// Bumper | |
// | |
// Created by Ian Ownbey on 6/30/15. | |
// Copyright © 2015 Bumpers Media Inc. All rights reserved. | |
// | |
#import "UIView+TimedAnimations.h" | |
#import <objc/runtime.h> | |
@interface BPSavedAnimationState: NSObject | |
@property (strong) CALayer *layer; | |
@property (copy) NSString *keyPath; | |
@property (strong) id oldValue; | |
+ (instancetype)savedStateWithLayer:(CALayer *)layer | |
keyPath:(NSString *)keyPath; | |
@end | |
@implementation BPSavedAnimationState | |
+ (instancetype)savedStateWithLayer:(CALayer *)layer | |
keyPath:(NSString *)keyPath | |
{ | |
BPSavedAnimationState *savedState = [BPSavedAnimationState new]; | |
savedState.layer = layer; | |
savedState.keyPath = keyPath; | |
savedState.oldValue = [layer valueForKeyPath:keyPath]; | |
return savedState; | |
} | |
@end | |
@implementation UIView (TimedAnimations) | |
static NSMutableArray *BP_savedAnimationStates = NULL; | |
+ (void)load | |
{ | |
BP_savedAnimationStates = [NSMutableArray array]; | |
SEL originalSelector = @selector(actionForLayer:forKey:); | |
SEL extendedSelector = @selector(BP_actionForLayer:forKey:); | |
Method originalMethod = class_getInstanceMethod(self, originalSelector); | |
Method extendedMethod = class_getInstanceMethod(self, extendedSelector); | |
NSAssert(originalMethod, @"original method should exist"); | |
NSAssert(extendedMethod, @"exchanged method should exist"); | |
if(class_addMethod(self, originalSelector, method_getImplementation(extendedMethod), method_getTypeEncoding(extendedMethod))) { | |
class_replaceMethod(self, extendedSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); | |
} else { | |
method_exchangeImplementations(originalMethod, extendedMethod); | |
} | |
} | |
static void *BP_currentAnimationContext = NULL; | |
static void *BP_animationContext = &BP_animationContext; | |
- (id<CAAction>)BP_actionForLayer:(CALayer *)layer forKey:(NSString *)event | |
{ | |
if (BP_currentAnimationContext == BP_animationContext) { | |
[BP_savedAnimationStates addObject:[BPSavedAnimationState savedStateWithLayer:layer keyPath:event]]; | |
return [NSNull null]; | |
} | |
return [self BP_actionForLayer:layer forKey:event]; | |
} | |
+ (void)BP_cubicTimedAnimateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay group:(dispatch_group_t)group animations:(void (^)(void))animations complete:(void (^)(BOOL))complete | |
{ | |
[self BP_timedAnimateWithDuration:duration delay:delay timingFunction:[[CAMediaTimingFunction alloc] initWithControlPoints:0.2 : 0.7 : 0.5 : 1] group:group animations:animations complete:complete]; | |
} | |
+ (void)BP_timedAnimateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay timingFunction:(CAMediaTimingFunction *)timingFunction group:(dispatch_group_t)group animations:(void (^)(void))animations complete:(void (^)(BOOL))complete | |
{ | |
BP_currentAnimationContext = BP_animationContext; | |
animations(); | |
[BP_savedAnimationStates enumerateObjectsUsingBlock:^(BPSavedAnimationState *savedState, NSUInteger idx, BOOL *stop) { | |
CALayer *layer = savedState.layer; | |
NSString *keyPath = savedState.keyPath; | |
id oldValue = savedState.oldValue; | |
id newValue = [layer valueForKeyPath:keyPath]; | |
layer.masksToBounds = NO; | |
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:keyPath]; | |
anim.beginTime = CACurrentMediaTime() + delay; | |
anim.duration = duration; | |
anim.fromValue = oldValue; | |
anim.toValue = newValue; | |
anim.timingFunction = timingFunction; | |
// back to old value without an animation | |
[CATransaction begin]; | |
[CATransaction setDisableActions:YES]; | |
[layer setValue:oldValue forKeyPath:keyPath]; | |
[CATransaction commit]; | |
// animate the "pop" | |
if (group) { | |
dispatch_group_enter(group); | |
} | |
[CATransaction begin]; | |
[CATransaction setCompletionBlock:^{ | |
[layer setValue:newValue forKey:keyPath]; | |
[layer removeAnimationForKey:keyPath]; | |
if (complete) { | |
complete(YES); | |
} | |
if (group) { | |
dispatch_group_leave(group); | |
} | |
}]; | |
[layer addAnimation:anim forKey:keyPath]; | |
[CATransaction commit]; | |
}]; | |
// clean up (remove all the stored state) | |
[BP_savedAnimationStates removeAllObjects]; | |
BP_currentAnimationContext = nil; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment