diff --git a/MONActivityIndicatorViewDemo/MONActivityIndicatorViewDemo/Source/Views/MONActivityIndicatorView/MONActivityIndicatorView.h b/MONActivityIndicatorViewDemo/MONActivityIndicatorViewDemo/Source/Views/MONActivityIndicatorView/MONActivityIndicatorView.h index 47903a0..dda898a 100644 --- a/MONActivityIndicatorViewDemo/MONActivityIndicatorViewDemo/Source/Views/MONActivityIndicatorView/MONActivityIndicatorView.h +++ b/MONActivityIndicatorViewDemo/MONActivityIndicatorViewDemo/Source/Views/MONActivityIndicatorView/MONActivityIndicatorView.h @@ -37,6 +37,8 @@ FOUNDATION_EXPORT const unsigned char MONActivityIndicatorViewVersionString[]; /** The assigned delegate */ @property (weak, nonatomic) id delegate; +/** Indicates whether the activity indicator view is animating. */ +@property (readonly, nonatomic, getter=isAnimating) BOOL animating; /** Starts the animation of the activity indicator. diff --git a/MONActivityIndicatorViewDemo/MONActivityIndicatorViewDemo/Source/Views/MONActivityIndicatorView/MONActivityIndicatorView.m b/MONActivityIndicatorViewDemo/MONActivityIndicatorViewDemo/Source/Views/MONActivityIndicatorView/MONActivityIndicatorView.m index 364719f..81401ae 100644 --- a/MONActivityIndicatorViewDemo/MONActivityIndicatorViewDemo/Source/Views/MONActivityIndicatorView/MONActivityIndicatorView.m +++ b/MONActivityIndicatorViewDemo/MONActivityIndicatorViewDemo/Source/Views/MONActivityIndicatorView/MONActivityIndicatorView.m @@ -7,13 +7,38 @@ #import #import "MONActivityIndicatorView.h" +@interface MONActivityIndicatorLayer : CALayer + +@property (nonatomic, weak, readonly) MONActivityIndicatorView *view; + +@end + +@implementation MONActivityIndicatorLayer + +- (MONActivityIndicatorView *)view { + return (MONActivityIndicatorView *)self.delegate; +} + +- (void)removeAllAnimations { + [super removeAllAnimations]; + + // `-[UITableViewCell prepareForReuse]` and `-[UICollectionViewCell prepareForReuse]` remove all animations from + // child views. There is no way to restart animations automatically (as we do it in `-didMoveToWindow`). + // So, we have to stop animating. + // Note: We can't do it in `-animationDidStop:finished:` animation delegate, because the delegate method is called + // asynchronously. + [self.view stopAnimating]; +} + +@end + @interface MONActivityIndicatorView () /** The default color of each circle. */ @property (strong, nonatomic) UIColor *defaultColor; -/** An indicator whether the activity indicator view is animating. */ -@property (readwrite, nonatomic) BOOL isAnimating; +/** Indicates whether the activity indicator view is animating. */ +@property (readwrite, nonatomic, getter=isAnimating) BOOL animating; /** Sets up default values @@ -30,6 +55,11 @@ - (void)addCircles; */ - (void)removeCircles; +/** + Adds animations to the circle layers. + */ +- (void)addCircleAnimations; + /** Creates the circle view. @param radius The radius of the circle. @@ -79,7 +109,11 @@ - (id)initWithCoder:(NSCoder *)aDecoder { } #pragma mark - -#pragma mark - Intrinsic Content Size +#pragma mark - UIViews + ++ (Class)layerClass { + return [MONActivityIndicatorLayer class]; +} - (CGSize)intrinsicContentSize { CGFloat width = (self.numberOfCircles * ((2 * self.radius) + self.internalSpacing)) - self.internalSpacing; @@ -87,6 +121,20 @@ - (CGSize)intrinsicContentSize { return CGSizeMake(width, height); } +- (CGSize)sizeThatFits:(CGSize)size { + return self.intrinsicContentSize; +} + +- (void)didMoveToWindow { + [super didMoveToWindow]; + + // Core Animation animations are removed when the view is remove from a window. + // So, we have to add the animations again when the view is added to a window. + if (self.window && self.animating) { + [self addCircleAnimations]; + } +} + #pragma mark - #pragma mark - Private Methods @@ -130,17 +178,26 @@ - (void)addCircles { color = [self.delegate activityIndicatorView:self circleBackgroundColorAtIndex:i]; } UIView *circle = [self createCircleWithRadius:self.radius - color:(color == nil) ? self.defaultColor : color + color:color ?: self.defaultColor positionX:(i * ((2 * self.radius) + self.internalSpacing))]; - [circle setTransform:CGAffineTransformMakeScale(0, 0)]; - [circle.layer addAnimation:[self createAnimationWithDuration:self.duration delay:(i * self.delay)] forKey:@"scale"]; + circle.transform = CGAffineTransformMakeScale(0, 0); [self addSubview:circle]; } + + if (self.window) { + [self addCircleAnimations]; + } } - (void)removeCircles { - [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [obj removeFromSuperview]; + [self.subviews enumerateObjectsUsingBlock:^(UIView *circle, NSUInteger index, BOOL *stop) { + [circle removeFromSuperview]; + }]; +} + +- (void)addCircleAnimations { + [self.subviews enumerateObjectsUsingBlock:^(UIView *circle, NSUInteger index, BOOL *stop) { + [circle.layer addAnimation:[self createAnimationWithDuration:self.duration delay:(index * self.delay)] forKey:@"scale"]; }]; } @@ -148,18 +205,18 @@ - (void)removeCircles { #pragma mark - Public Methods - (void)startAnimating { - if (!self.isAnimating) { + if (!self.animating) { [self addCircles]; self.hidden = NO; - self.isAnimating = YES; + self.animating = YES; } } - (void)stopAnimating { - if (self.isAnimating) { + if (self.animating) { [self removeCircles]; self.hidden = YES; - self.isAnimating = NO; + self.animating = NO; } }