Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ FOUNDATION_EXPORT const unsigned char MONActivityIndicatorViewVersionString[];
/** The assigned delegate */
@property (weak, nonatomic) id<MONActivityIndicatorViewDelegate> delegate;

/** Indicates whether the activity indicator view is animating. */
@property (readonly, nonatomic, getter=isAnimating) BOOL animating;

/**
Starts the animation of the activity indicator.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,38 @@
#import <QuartzCore/QuartzCore.h>
#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
Expand All @@ -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.
Expand Down Expand Up @@ -79,14 +109,32 @@ - (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;
CGFloat height = self.radius * 2;
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

Expand Down Expand Up @@ -130,36 +178,45 @@ - (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"];
}];
}

#pragma mark -
#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;
}
}

Expand Down