1212
1313@interface JWWaveView ()
1414@property (nonatomic , strong ) CAShapeLayer *waveShapeLayer;
15+ @property (nonatomic , assign ) BOOL shouldRestart; // if animation was started once; set this flat to YES;
1516@end
1617
1718@implementation JWWaveView
@@ -20,19 +21,14 @@ @implementation JWWaveView
2021- (void )startWavingIfNeeded {
2122 CAReplicatorLayer *replicatorLayer = [self replicatorLayer ];
2223 CGRect replicatorRect = replicatorLayer.bounds ;
23- if (CGRectIsEmpty (replicatorRect)) {
24- return ;
25- }
26-
2724 self.waveShapeLayer .frame = replicatorRect;
28- CGFloat instanceWidth = CGRectGetWidth (replicatorRect);
29- replicatorLayer.instanceTransform = CATransform3DMakeTranslation (instanceWidth, 0 , 0 );
3025
3126 // setup shape
32- [self setupWavePath ];
33-
27+ [self renewWavePathWithForceRenew: NO animated: NO ];
28+
3429 // restart animtion
3530 [self restartWaveShapeTranslation ];
31+ _shouldRestart = YES ;
3632}
3733
3834- (void )pauseWavingIfNeeded {
@@ -81,19 +77,36 @@ - (CAReplicatorLayer *)replicatorLayer {
8177}
8278
8379- (void )willMoveToSuperview : (UIView *)newSuperview {
80+ [super willMoveToSuperview: newSuperview];
8481 if (!newSuperview) {
8582 [self pauseWavingIfNeeded ];
8683 }
8784 else {
88- [self restartWaveShapeTranslation ];
85+ // has started before by calling the public api
86+ if (self.shouldRestart ) {
87+ [self restartWaveShapeTranslation ];
88+ }
8989 }
90- [super willMoveToSuperview: newSuperview];
90+ }
91+
92+ - (void )layoutSubviews {
93+ [super layoutSubviews ];
94+ CAReplicatorLayer *replicatorLayer = [self replicatorLayer ];
95+ CGRect replicatorRect = replicatorLayer.bounds ;
96+ self.waveShapeLayer .frame = replicatorRect;
9197}
9298
9399#pragma mark - Private
94100- (void )restartWaveShapeTranslation {
101+ // layer setup
95102 CAReplicatorLayer *replicatorLayer = [self replicatorLayer ];
96103 CGRect replicatorRect = replicatorLayer.bounds ;
104+ CGFloat instanceWidth = CGRectGetWidth (replicatorRect);
105+ replicatorLayer.instanceTransform = CATransform3DMakeTranslation (instanceWidth, 0 , 0 );
106+ if (CGRectIsEmpty (replicatorRect)) {
107+ return ;
108+ }
109+
97110 if (![self .waveShapeLayer animationForKey: kWaveShapeTranslationAnimationKey ]) {
98111 // add translation animation to shape layer;
99112 CABasicAnimation *transAnim = [CABasicAnimation animation ];
@@ -125,44 +138,49 @@ - (void)configureWaveShapes {
125138 CAReplicatorLayer *replicatorLayer = [self replicatorLayer ];
126139
127140 // add two shape layers
141+ CGRect replicatorRect = replicatorLayer.bounds ;
142+ self.waveShapeLayer .frame = replicatorRect;
128143 [replicatorLayer addSublayer: self .waveShapeLayer];
129144 replicatorLayer.instanceCount = 2 ;
130145 replicatorLayer.instanceDelay = 0 ;
131146}
132147
133- - (void )setupWavePath {
148+ - (void )renewWavePathWithForceRenew : ( BOOL ) forceRenew animated : ( BOOL ) animated {
134149 CGRect sinPathCanvasRect = self.waveShapeLayer .bounds ;
135150 sinPathCanvasRect = CGRectIntegral (sinPathCanvasRect);
136151 if (CGRectIsEmpty (sinPathCanvasRect)) {
137152 return ;
138153 }
139154
140- [CATransaction begin ];
141- [CATransaction setDisableActions: YES ];
142- CGFloat canvasWidth = sinPathCanvasRect.size .width ;
143- CGFloat canvasHeight = sinPathCanvasRect.size .height ;
144- CGFloat canvasMidY = CGRectGetMidY (sinPathCanvasRect);
145- UIBezierPath *sinPath = [UIBezierPath bezierPath ];
146- sinPath.lineWidth = 1 .f ;
147- CGFloat yPos = 0 ;
148- for (CGFloat xPos = 0 .f ; xPos <= canvasWidth; xPos += 1 .f ) {
149- // wave path starts at the canvasMidY and uses (canvasHeight * 0.5) as amplitude
150- CGFloat halfAmplitude = canvasHeight / 4 .f ;
151- yPos = canvasMidY + sin ((xPos)/canvasWidth * M_PI * 2 * _waveCycles) * halfAmplitude;
152- if (fpclassify (xPos) == FP_ZERO) {
153- [sinPath moveToPoint: (CGPoint){xPos, yPos}];
154- }
155- else {
156- [sinPath addLineToPoint: (CGPoint){xPos, yPos}];
155+ // use default implicit animation
156+ if (!self.waveShapeLayer .path || forceRenew) {
157+ [CATransaction begin ];
158+ [CATransaction setDisableActions: !animated];
159+ CGFloat canvasWidth = sinPathCanvasRect.size .width ;
160+ CGFloat canvasHeight = sinPathCanvasRect.size .height ;
161+ CGFloat canvasMidY = CGRectGetMidY (sinPathCanvasRect);
162+ UIBezierPath *sinPath = [UIBezierPath bezierPath ];
163+ sinPath.lineWidth = 1 .f ;
164+ CGFloat yPos = 0 ;
165+ for (CGFloat xPos = 0 .f ; xPos <= canvasWidth; xPos += 1 .f ) {
166+ // wave path starts at the canvasMidY and uses (canvasHeight * 0.5) as amplitude
167+ CGFloat halfAmplitude = canvasHeight / 4 .f ;
168+ yPos = canvasMidY + sin ((xPos)/canvasWidth * M_PI * 2 * _waveCycles) * halfAmplitude;
169+ if (fpclassify (xPos) == FP_ZERO) {
170+ [sinPath moveToPoint: (CGPoint){xPos, yPos}];
171+ }
172+ else {
173+ [sinPath addLineToPoint: (CGPoint){xPos, yPos}];
174+ }
157175 }
176+
177+ // close path
178+ [sinPath addLineToPoint: (CGPoint){canvasWidth, canvasHeight}];
179+ [sinPath addLineToPoint: (CGPoint){0 , canvasHeight}];
180+ [sinPath closePath ];
181+ self.waveShapeLayer .path = sinPath.CGPath ;
182+ [CATransaction commit ];
158183 }
159-
160- // close path
161- [sinPath addLineToPoint: (CGPoint){canvasWidth, canvasHeight}];
162- [sinPath addLineToPoint: (CGPoint){0 , canvasHeight}];
163- [sinPath closePath ];
164- self.waveShapeLayer .path = sinPath.CGPath ;
165- [CATransaction commit ];
166184}
167185
168186#pragma mark - Accessors
@@ -173,7 +191,11 @@ - (void)setWaveColor:(UIColor *)waveColor {
173191
174192- (void )setWaveCycles : (NSInteger )waveCycles {
175193 if (_waveCycles != waveCycles) {
176- _waveCycles = MAX (1 , waveCycles);
194+ NSInteger adjustedCycles = MAX (1 , waveCycles);
195+ if (adjustedCycles != _waveCycles) {
196+ _waveCycles = adjustedCycles;
197+ [self renewWavePathWithForceRenew: YES animated: YES ];
198+ }
177199 }
178200}
179201
0 commit comments