SourceCode
本篇是DSAnimation系列的第一篇,之前两个月一直在做Hybrid开发和server开发方面的工作,通过这个主题巩固一下ios方面的知识
这个自定义的SwitchButton有两个特点:
- 开关状态切换时Thumb有一个bounce的效果
- 开关状态切换时添加Ripple效果
bounce effect
//switch movement animation
- (void)changeThumbStateONwithAnimation {
[UIView animateWithDuration:0.15f
delay:0.05f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect thumbFrame = self.switchThumb.frame;
thumbFrame.origin.x = self.thumbOnPosition + 3.0f;
self.switchThumb.frame = thumbFrame;
self.switchThumb.backgroundColor = self.OnColor;
self.track.backgroundColor = self.TrackOnColor;
self.userInteractionEnabled = NO;
}completion:^(BOOL finished){
if (self.isOn == NO){
self.isOn = YES;
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
self.isOn = YES;
[UIView animateWithDuration:0.15f
animations:^{
CGRect thumbFrame = self.switchThumb.frame;
thumbFrame.origin.x = self.thumbOnPosition;
self.switchThumb.frame = thumbFrame;
}completion:^(BOOL finished){
self.userInteractionEnabled = YES;
}];
}];
}
ripple effect
//initialize ripple effect
- (void)initializeRipple {
self.rippleLayer = [CAShapeLayer layer];
float rippleScale = 2;
CGRect rippleFrame = CGRectZero;
rippleFrame.origin.x = -self.switchThumb.frame.size.width / (rippleScale * 2);
rippleFrame.origin.y = -self.switchThumb.frame.size.height / (rippleScale * 2);
rippleFrame.size.width = self.switchThumb.frame.size.width * rippleScale;
rippleFrame.size.height = rippleFrame.size.width;
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rippleFrame cornerRadius:self.switchThumb.layer.cornerRadius*2];
self.rippleLayer.path = path.CGPath;
self.rippleLayer.frame = rippleFrame;
self.rippleLayer.opacity = 0.2;
self.rippleLayer.strokeColor = [UIColor clearColor].CGColor;
self.rippleLayer.fillColor = self.RippleColor.CGColor;
self.rippleLayer.lineWidth = 0;
[self.switchThumb.layer insertSublayer:self.rippleLayer below:self.switchThumb.layer];
}
有关UIBezierPath
, CABasicAnimation
请查阅8.1 属性动画
// implement ripple effect
- (void)animateRippleEffect {
if (self.rippleLayer == nil){
[self initializeRipple];
}
// begain animation
self.rippleLayer.opacity = 0.0;
[CATransaction begin];
// remove animation
[CATransaction setCompletionBlock:^{
[self.rippleLayer removeFromSuperlayer];
self.rippleLayer = nil;
}];
//scale ripple
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.fromValue = [NSNumber numberWithFloat:0.5];
scaleAnimation.toValue = [NSNumber numberWithFloat:1.25];
CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
alphaAnimation.fromValue = @0.4;
alphaAnimation.toValue = @0;
// group animation
CAAnimationGroup *animation = [CAAnimationGroup animation];
animation.animations = @[scaleAnimation , alphaAnimation];
animation.duration = 0.4f;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[self.rippleLayer addAnimation:animation forKey:nil];
[CATransaction commit];
}