4

I wanted to make a circular Slider which is draggable and animatable. So far I've managed to build the slider and use the drag handle to move it around and even animate it. Sometimes animation goes wrong (wrong direction or shortest direction. I've subclassed a UIView (Will be a UIControl soon, just wanted to get the animation right first) added a PanGestureRecognizer and several layers for the drawing.

enter image description here

So how do I fix this weird behaviour? I've someone could help me here, I'd be thankful :)

Here's the sample project -> http://cl.ly/2l0O3b1I3U0X

Thanks a lot!

EDIT:

Here's the drawing code:

CALayer *aLayer = [CALayer layer];
aLayer.bounds = CGRectMake(0, 0, 170, 170);
aLayer.position = self.center;
aLayer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);

self.handleHostLayer = [CALayer layer];
self.handleHostLayer.bounds = CGRectMake(0, 0, 170, 170);
self.handleHostLayer.position = CGPointMake(CGRectGetMaxX(aLayer.bounds) - 170/2.0, CGRectGetMaxY(aLayer.bounds) - 170/2.0);

[aLayer addSublayer:self.handleHostLayer];
[self.layer addSublayer:aLayer];

self.handle = [CALayer layer];
self.handle.bounds = CGRectMake(0, 0, 50, 50);
self.handle.cornerRadius = 25;
self.handle.backgroundColor = [UIColor whiteColor].CGColor;
self.handle.masksToBounds = NO;
self.handle.shadowOffset = CGSizeMake(3.0, 0.0);
self.handle.shadowRadius = 0;
self.handle.shadowOpacity = .15;
self.handle.shadowColor = [UIColor blackColor].CGColor;

[self.handleHostLayer addSublayer:self.self.handle];

Here's the animation code:

CGFloat handleTarget = ToRad(DEG);

CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotationAnimation.fromValue = @([[self.handleHostLayer valueForKeyPath:@"transform.rotation"] floatValue]);
rotationAnimation.toValue = @(handleTarget);
rotationAnimation.duration = .5;
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.cumulative = YES;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];

[self.handleHostLayer addAnimation:rotationAnimation forKey:@"transform.rotation"];
reiserD
  • 57
  • 1
  • 10
  • The animation code seem to be the problem. Can you post only the animation and possibly the drawing code. I don't want to dig through the project to find it. – David Rönnqvist Jul 17 '13 at 10:03
  • Thanks a lot for helping me! I posted the drawing code above (drawing is done in drawRect:) – reiserD Jul 17 '13 at 12:41

1 Answers1

1

OK, I looked at your project. Your problem is that the to and from angles don't both fall in the 0≤ɸ<2π range.

You can make sure that they do by adding and removing 2π until they both are within that range.

CGFloat fromAngle = [[self.handleHostLayer valueForKeyPath:@"transform.rotation"] floatValue];
CGFloat toAngle   = handleTarget;

while (fromAngle >= 2.0*M_PI) { fromAngle -= 2*M_PI; }
while (toAngle   >= 2.0*M_PI) { toAngle   -= 2*M_PI; }
while (fromAngle <  0.0)      { fromAngle += 2*M_PI; }
while (toAngle   <  0.0)      { toAngle   += 2*M_PI; }

CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotationAnimation.fromValue = @(fromAngle);
rotationAnimation.toValue   = @(toAngle);
// As before...

Another things you could change is the misuse of removeOnCompletion. I did a long explanation in this answer about why it's bad to do so.

In short: you are not animating from the value that you think you are animating since you inadvertently introduce a difference between the value of the layers property and what you see on screen.

Skip that line all together. You can also skip the skip the cumulative line since you are not repeating the animation. Now, if your animations doesn't stick: set the model value to its final value before adding the animation to it.

Community
  • 1
  • 1
David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
  • Thank you. unfortunately my problem is not completely fixed. the handle still slides around the whole circle. I think it has something to with my rotation stuff. the handle is a rounded layer inserted as sublayer into a rectangle layer whicht is rotated. If I rotate past zero the angle in rad is e.g. 5.8... the transformation.rotation of the layer is at -0.45... . So that might be the reason why its slinging all around. But how to I fix that.. I'll have a try :) – reiserD Jul 17 '13 at 13:07
  • UNfortunately not. NSLog(@"handleTarget: %f, c: %f", handleTarget, [[self.handleHostLayer valueForKeyPath:@"transform.rotation"] floatValue]); -> handleTarget: 6.178466, c: -0.104720 – reiserD Jul 17 '13 at 13:13
  • Thanks a lot for your efforts. I nearly surrender. shitty circle stuff. Now it's flickery and still moving around the whole circle. Can't get my head around it. Especially if it's about 86°F... – reiserD Jul 17 '13 at 13:19
  • Ok.. I looked at your project. The problem is that you with the angles you are animating between don't both fall in the 0<ɸ<2π range. – David Rönnqvist Jul 17 '13 at 13:26
  • Thanks for having a look at my project. Sorry I didn't get your last sentences right, I guess. Yeah well, the left side is < 0. So do I have to differenciate between > 0 & < 0 and calculate the appropriate value. – reiserD Jul 17 '13 at 13:28
  • Pure awesomeness! Thanks a lot :) Are you freelancing :)? – reiserD Jul 17 '13 at 13:40
  • @reiserD Sorry, I'm not :) – David Rönnqvist Jul 17 '13 at 13:54
  • dammit :) Thanks again for helping me! I nearly surrendered :) – reiserD Jul 17 '13 at 13:56