|
| 1 | +// |
| 2 | +// BLMultiColorLoader.m |
| 3 | +// Demo |
| 4 | +// |
| 5 | +// Created by Poonia on 12/09/15. |
| 6 | +// Copyright (c) 2015 Babulal Poonia. All rights reserved. |
| 7 | +// |
| 8 | + |
| 9 | +#import "BLMultiColorLoader.h" |
| 10 | + |
| 11 | +#define ROUND_TIME 1.5 |
| 12 | +#define DEFAULT_LINE_WIDTH 2.0 |
| 13 | +#define DEFAULT_COLOR [UIColor orangeColor] |
| 14 | + |
| 15 | +@interface BLMultiColorLoader () |
| 16 | + |
| 17 | +@property (nonatomic, strong) CAShapeLayer *circleLayer; |
| 18 | +@property (nonatomic, strong) CAAnimationGroup *strokeLineAnimation; |
| 19 | +@property (nonatomic, strong) CAAnimation *rotationAnimation; |
| 20 | +@property (nonatomic, strong) CAAnimation *strokeColorAnimation; |
| 21 | +@property (nonatomic) BOOL animating; |
| 22 | + |
| 23 | +@end |
| 24 | + |
| 25 | +@implementation BLMultiColorLoader |
| 26 | + |
| 27 | +#pragma mark - Life Cycle |
| 28 | +- (instancetype)init { |
| 29 | + self = [super init]; |
| 30 | + if(self) { |
| 31 | + [self initialSetup]; |
| 32 | + } |
| 33 | + return self; |
| 34 | +} |
| 35 | + |
| 36 | +- (instancetype)initWithFrame:(CGRect)frame { |
| 37 | + self = [super initWithFrame:frame]; |
| 38 | + if(self) { |
| 39 | + [self initialSetup]; |
| 40 | + } |
| 41 | + return self; |
| 42 | +} |
| 43 | + |
| 44 | +- (instancetype)initWithCoder:(NSCoder *)aDecoder { |
| 45 | + self = [super initWithCoder:aDecoder]; |
| 46 | + if(self) { |
| 47 | + [self initialSetup]; |
| 48 | + } |
| 49 | + return self; |
| 50 | +} |
| 51 | + |
| 52 | +#pragma mark - Initial Setup |
| 53 | + |
| 54 | +- (void)initialSetup { |
| 55 | + self.circleLayer = [CAShapeLayer layer]; |
| 56 | + [self.layer addSublayer:self.circleLayer]; |
| 57 | + |
| 58 | + self.backgroundColor = [UIColor clearColor]; |
| 59 | + self.circleLayer.fillColor = nil; |
| 60 | + self.circleLayer.lineWidth = DEFAULT_LINE_WIDTH; |
| 61 | + self.circleLayer.lineCap = kCALineCapRound; |
| 62 | + |
| 63 | + _colorArray = @[DEFAULT_COLOR]; |
| 64 | + |
| 65 | + [self updateAnimations]; |
| 66 | +} |
| 67 | + |
| 68 | +#pragma mark - Layout |
| 69 | +- (void)layoutSubviews { |
| 70 | + [super layoutSubviews]; |
| 71 | + |
| 72 | + CGPoint center = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0); |
| 73 | + CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height)/2.0 - self.circleLayer.lineWidth / 2.0; |
| 74 | + CGFloat startAngle = 0; |
| 75 | + CGFloat endAngle = 2*M_PI; |
| 76 | + |
| 77 | + UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center |
| 78 | + radius:radius |
| 79 | + startAngle:startAngle |
| 80 | + endAngle:endAngle |
| 81 | + clockwise:YES]; |
| 82 | + self.circleLayer.path = path.CGPath; |
| 83 | + self.circleLayer.frame = self.bounds; |
| 84 | +} |
| 85 | + |
| 86 | +#pragma mark - |
| 87 | + |
| 88 | +- (void)setColorArray:(NSArray *)colorArray{ |
| 89 | + if(colorArray.count>0){ |
| 90 | + _colorArray = colorArray; |
| 91 | + } |
| 92 | + [self updateAnimations]; |
| 93 | +} |
| 94 | + |
| 95 | +- (void)setLineWidth:(CGFloat)lineWidth { |
| 96 | + _lineWidth = lineWidth; |
| 97 | + self.circleLayer.lineWidth = _lineWidth; |
| 98 | +} |
| 99 | + |
| 100 | +#pragma mark - |
| 101 | + |
| 102 | +- (void)startAnimation { |
| 103 | + _animating = YES; |
| 104 | + [self.circleLayer addAnimation:self.strokeLineAnimation forKey:@"strokeLineAnimation"]; |
| 105 | + [self.circleLayer addAnimation:self.rotationAnimation forKey:@"rotationAnimation"]; |
| 106 | + [self.circleLayer addAnimation:self.strokeColorAnimation forKey:@"strokeColorAnimation"]; |
| 107 | +} |
| 108 | + |
| 109 | +- (void)stopAnimation { |
| 110 | + _animating = NO; |
| 111 | + [self.circleLayer removeAnimationForKey:@"strokeLineAnimation"]; |
| 112 | + [self.circleLayer removeAnimationForKey:@"rotationAnimation"]; |
| 113 | + [self.circleLayer removeAnimationForKey:@"strokeColorAnimation"]; |
| 114 | +} |
| 115 | + |
| 116 | +- (void)stopAnimationAfter:(NSTimeInterval)timeInterval |
| 117 | +{ |
| 118 | + [self performSelector:@selector(stopAnimation) withObject:nil afterDelay:timeInterval]; |
| 119 | +} |
| 120 | + |
| 121 | +- (BOOL)isAnimating { |
| 122 | + return _animating; |
| 123 | +} |
| 124 | + |
| 125 | +#pragma mark - |
| 126 | + |
| 127 | +- (void)updateAnimations { |
| 128 | + // Stroke Head |
| 129 | + CABasicAnimation *headAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; |
| 130 | + headAnimation.beginTime = ROUND_TIME/3.0; |
| 131 | + headAnimation.fromValue = @0; |
| 132 | + headAnimation.toValue = @1; |
| 133 | + headAnimation.duration = 2*ROUND_TIME/3.0; |
| 134 | + headAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; |
| 135 | + |
| 136 | + // Stroke Tail |
| 137 | + CABasicAnimation *tailAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; |
| 138 | + tailAnimation.fromValue = @0; |
| 139 | + tailAnimation.toValue = @1; |
| 140 | + tailAnimation.duration = 2*ROUND_TIME/3.0; |
| 141 | + tailAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; |
| 142 | + |
| 143 | + // Stroke Line Group |
| 144 | + CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; |
| 145 | + animationGroup.duration = ROUND_TIME; |
| 146 | + animationGroup.repeatCount = INFINITY; |
| 147 | + animationGroup.animations = @[headAnimation, tailAnimation]; |
| 148 | + self.strokeLineAnimation = animationGroup; |
| 149 | + |
| 150 | + // Rotation |
| 151 | + CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; |
| 152 | + rotationAnimation.fromValue = @0; |
| 153 | + rotationAnimation.toValue = @(2*M_PI); |
| 154 | + rotationAnimation.duration = ROUND_TIME; |
| 155 | + rotationAnimation.repeatCount = INFINITY; |
| 156 | + self.rotationAnimation = rotationAnimation; |
| 157 | + |
| 158 | + CAKeyframeAnimation *strokeColorAnimation = [CAKeyframeAnimation animationWithKeyPath:@"strokeColor"]; |
| 159 | + strokeColorAnimation.values = [self prepareColorValues]; |
| 160 | + strokeColorAnimation.keyTimes = [self prepareKeyTimes]; |
| 161 | + strokeColorAnimation.calculationMode = kCAAnimationDiscrete; |
| 162 | + strokeColorAnimation.duration = self.colorArray.count * ROUND_TIME; |
| 163 | + strokeColorAnimation.repeatCount = INFINITY; |
| 164 | + self.strokeColorAnimation = strokeColorAnimation; |
| 165 | +} |
| 166 | + |
| 167 | +#pragma mark - Animation Data Preparation |
| 168 | + |
| 169 | +- (NSArray*)prepareColorValues { |
| 170 | + NSMutableArray *cgColorArray = [[NSMutableArray alloc] init]; |
| 171 | + for(UIColor *color in self.colorArray){ |
| 172 | + [cgColorArray addObject:(id)color.CGColor]; |
| 173 | + } |
| 174 | + return cgColorArray; |
| 175 | +} |
| 176 | + |
| 177 | +- (NSArray*)prepareKeyTimes { |
| 178 | + NSMutableArray *keyTimesArray = [[NSMutableArray alloc] init]; |
| 179 | + for(NSUInteger i=0; i<self.colorArray.count+1; i++){ |
| 180 | + [keyTimesArray addObject:[NSNumber numberWithFloat:i*1.0/self.colorArray.count]]; |
| 181 | + } |
| 182 | + return keyTimesArray; |
| 183 | +} |
| 184 | + |
| 185 | +#pragma mark - Dealloc |
| 186 | + |
| 187 | +- (void)dealloc { |
| 188 | + [self stopAnimation]; |
| 189 | + [self.circleLayer removeFromSuperlayer]; |
| 190 | + self.circleLayer = nil; |
| 191 | + self.strokeLineAnimation = nil; |
| 192 | + self.rotationAnimation = nil; |
| 193 | + self.strokeColorAnimation = nil; |
| 194 | + self.colorArray = nil; |
| 195 | +} |
| 196 | + |
| 197 | +@end |
0 commit comments