diff --git a/Classes/CALayer+MBAnimationPersistence.h b/Classes/CALayer+MBAnimationPersistence.h new file mode 100755 index 0000000..f1f59fb --- /dev/null +++ b/Classes/CALayer+MBAnimationPersistence.h @@ -0,0 +1,27 @@ +// +// CALayer+MBAnimationPersistence.h +// +// Created by Matej Bukovinski on 19. 03. 14. +// Copyright (c) 2014 Matej Bukovinski. All rights reserved. +// + +#import + + +@interface CALayer (MBAnimationPersistence) + +/** + Animation keys for animations that should be persisted. + Inspect the `animationKeys` array to find valid keys for your layer. + + `CAAnimation` instances associated with the provided keys will be copied and held onto, + when the applications enters background mode and restored when exiting background mode. + + Set to `nil`to disable persistance. + */ +@property (nonatomic, strong) NSArray *MB_persistentAnimationKeys; + +/** Set all current `animationKeys` as persistent. */ +- (void)MB_setCurrentAnimationsPersistent; + +@end diff --git a/Classes/CALayer+MBAnimationPersistence.m b/Classes/CALayer+MBAnimationPersistence.m new file mode 100755 index 0000000..9970299 --- /dev/null +++ b/Classes/CALayer+MBAnimationPersistence.m @@ -0,0 +1,160 @@ +// +// CALayer+MBAnimationPersistence.m +// +// Created by Matej Bukovinski on 19. 03. 14. +// Copyright (c) 2014 Guerrilla Code. All rights reserved. +// + +#import +#import "CALayer+MBAnimationPersistence.h" + + +@interface MBPersistentAnimationContainer : NSObject +@property (nonatomic, weak) CALayer *layer; +@property (nonatomic, copy) NSArray *persistentAnimationKeys; +@property (nonatomic, copy) NSDictionary *persistedAnimations; +- (id)initWithLayer:(CALayer *)layer; +@end + + +@interface CALayer (MBAnimationPersistencePrivate) +@property (nonatomic, strong) MBPersistentAnimationContainer *MB_animationContainer; +@end + + +@implementation CALayer (MBAnimationPersistence) + +#pragma mark - Public + +- (NSArray *)MB_persistentAnimationKeys { + return self.MB_animationContainer.persistentAnimationKeys; +} + +- (void)setMB_persistentAnimationKeys:(NSArray *)persistentAnimationKeys { + MBPersistentAnimationContainer *container = [self MB_animationContainer]; + if (!container) { + container = [[MBPersistentAnimationContainer alloc] initWithLayer:self]; + [self MB_setAnimationContainer:container]; + } + container.persistentAnimationKeys = persistentAnimationKeys; +} + +- (void)MB_setCurrentAnimationsPersistent { + self.MB_persistentAnimationKeys = [self animationKeys]; +} + +#pragma mark - Associated objects + +- (void)MB_setAnimationContainer:(MBPersistentAnimationContainer *)animationContainer { + objc_setAssociatedObject(self, @selector(MB_animationContainer), animationContainer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (MBPersistentAnimationContainer *)MB_animationContainer { + return objc_getAssociatedObject(self, @selector(MB_animationContainer)); +} + +#pragma mark - Pause and resume + +// TechNote QA1673 - How to pause the animation of a layer tree +// @see https://developer.apple.com/library/ios/qa/qa1673/_index.html + +- (void)MB_pauseLayer { + CFTimeInterval pausedTime = [self convertTime:CACurrentMediaTime() fromLayer:nil]; + self.speed = 0.0; + self.timeOffset = pausedTime; +} + +- (void)MB_resumeLayer { + CFTimeInterval pausedTime = [self timeOffset]; + self.speed = 1.0; + self.timeOffset = 0.0; + self.beginTime = 0.0; + CFTimeInterval timeSincePause = [self convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; + self.beginTime = timeSincePause; +} + +@end + +@implementation MBPersistentAnimationContainer + +#pragma mark - Lifecycle + +- (id)initWithLayer:(CALayer *)layer { + self = [super init]; + if (self) { + _layer = layer; + } + return self; +} + +- (void)dealloc { + [self unregisterFromAppStateNotifications]; +} + +#pragma mark - Keys + +- (void)setPersistentAnimationKeys:(NSArray *)persistentAnimationKeys { + if (persistentAnimationKeys != _persistentAnimationKeys) { + if (!_persistentAnimationKeys) { + [self registerForAppStateNotifications]; + } else if (!persistentAnimationKeys) { + [self unregisterFromAppStateNotifications]; + } + _persistentAnimationKeys = persistentAnimationKeys; + } +} + +#pragma mark - Persistence + +- (void)persistLayerAnimationsAndPause { + CALayer *layer = self.layer; + if (!layer) { + return; + } + NSMutableDictionary *animations = [NSMutableDictionary new]; + for (NSString *key in self.persistentAnimationKeys) { + CAAnimation *animation = [layer animationForKey:key]; + if (animation) { + animations[key] = animation; + } + } + if (animations.count > 0) { + self.persistedAnimations = animations; + [layer MB_pauseLayer]; + } +} + +- (void)restoreLayerAnimationsAndResume { + CALayer *layer = self.layer; + if (!layer) { + return; + } + [self.persistedAnimations enumerateKeysAndObjectsUsingBlock:^(NSString *key, CAAnimation *animation, BOOL *stop) { + [layer addAnimation:animation forKey:key]; + }]; + if (self.persistedAnimations.count > 0) { + [layer MB_resumeLayer]; + } + self.persistedAnimations = nil; +} + +#pragma mark - Notifications + +- (void)registerForAppStateNotifications { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; +} + +- (void)unregisterFromAppStateNotifications { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)applicationDidEnterBackground { + [self persistLayerAnimationsAndPause]; +} + +- (void)applicationWillEnterForeground { + [self restoreLayerAnimationsAndResume]; +} + +@end diff --git a/Classes/GSIndeterminateProgressView.m b/Classes/GSIndeterminateProgressView.m index 1a4956d..8919577 100644 --- a/Classes/GSIndeterminateProgressView.m +++ b/Classes/GSIndeterminateProgressView.m @@ -7,6 +7,7 @@ // #import "GSIndeterminateProgressView.h" +#import "CALayer+MBAnimationPersistence.h" const CGFloat CHUNK_WIDTH = 36.0f; @@ -38,9 +39,9 @@ - (void)setTrackTintColor:(UIColor *)trackTintColor - (void)setProgressTintColor:(UIColor *)progressTintColor { _progressTintColor = progressTintColor; - for (UIView *v in self.progressChunks) { + [self.progressChunks enumerateObjectsUsingBlock:^(UIView* v, NSUInteger idx, BOOL *stop) { v.backgroundColor = progressTintColor; - } + }]; } - (void)startAnimating @@ -51,17 +52,15 @@ - (void)startAnimating self.hidden = NO; self.progressChunks = @[[[UIView alloc] initWithFrame:CGRectMake(-CHUNK_WIDTH, 0, CHUNK_WIDTH, self.frame.size.height)], - [[UIView alloc] initWithFrame:CGRectMake(-CHUNK_WIDTH, 0, CHUNK_WIDTH, self.frame.size.height)], [[UIView alloc] initWithFrame:CGRectMake(-CHUNK_WIDTH, 0, CHUNK_WIDTH, self.frame.size.height)]]; - NSTimeInterval delay = 0; - for (UIView *v in self.progressChunks) { - v.backgroundColor = self.progressTintColor; - - [self addSubview:v]; - - [self animateProgressChunk:v delay:(delay += 0.25)]; - } + __block float delay = 0.0f; + __weak GSIndeterminateProgressView* safeSelf = self; + [self.progressChunks enumerateObjectsUsingBlock:^(UIView* v, NSUInteger idx, BOOL *stop) { + v.backgroundColor = safeSelf.progressTintColor; + [safeSelf addSubview:v]; + [safeSelf animateProgressChunk:v delay:(delay += 0.35f)]; + }]; } - (void)stopAnimating @@ -71,26 +70,38 @@ - (void)stopAnimating self.hidden = self.hidesWhenStopped; - for (UIView *v in self.progressChunks) { - [v removeFromSuperview]; - } + [self.progressChunks enumerateObjectsUsingBlock:^(UIView* v, NSUInteger idx, BOOL *stop) { + [UIView animateWithDuration:(.3f*idx) + delay:0.0f + options:0 + animations:^{ + v.alpha = 0.0f; + } completion:^(BOOL finished) { + [v removeFromSuperview]; + }]; + }]; self.progressChunks = nil; } - (void)animateProgressChunk:(UIView *)chunk delay:(NSTimeInterval)delay { - [UIView animateWithDuration:0.75 delay:delay options:UIViewAnimationOptionCurveEaseInOut animations:^{ - CGRect chuckFrame = chunk.frame; - chuckFrame.origin.x = self.frame.size.width; - chunk.frame = chuckFrame; - } completion:^(BOOL finished) { - CGRect chuckFrame = chunk.frame; - chuckFrame.origin.x = -CHUNK_WIDTH; - chunk.frame = chuckFrame; - if (finished) - [self animateProgressChunk:chunk delay:0.4]; - }]; + dispatch_async(dispatch_get_main_queue(), ^{ + CGRect chuckFrame2 = chunk.frame; + chuckFrame2.origin.x = -CHUNK_WIDTH; + chunk.frame = chuckFrame2; + [UIView animateWithDuration:0.95f delay:delay options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionRepeat animations:^{ + CGRect chuckFrame3 = chunk.frame; + chuckFrame3.origin.x = self.frame.size.width; + chunk.frame = chuckFrame3; + } completion:^(BOOL finished) { + //if (finished) + }]; + // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((.75f+delay) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + // [self animateProgressChunk:chunk delay:0.4f]; + // }); + [chunk.layer MB_setCurrentAnimationsPersistent]; + }); } @end