今天我们看下按钮转场动画的实现,下面是不带导航栏和带导航栏的效果(不带导航栏会好看些 = =)
一、button动画相关代码:
1.首先是SHAnimationButton,button对象的创建、属性、动画开始结束方法以及代理方法等
SHAnimationButton.h
#import
@class SHAnimationButton;
@protocol SHAnimationButtonDelegate
/**
动画开始
@param SHAnimationButton SHAnimationButton
*/
- (void)SHAnimationButtonDidStartAnimation:(SHAnimationButton *)SHAnimationButton;
/**
动画将要接结束
@param SHAnimationButton SHAnimationButton
*/
- (void)SHAnimationButtonWillFinishAnimation:(SHAnimationButton *)SHAnimationButton;
/**
动画已经结束
@param SHAnimationButton SHAnimationButton
*/
- (void)SHAnimationButtonDidFinishAnimation:(SHAnimationButton *)SHAnimationButton;
@end
@interface SHAnimationButton : UIButton
/**
代理对象
*/
@property (nonatomic, weak) id delegate;
/**
创建对象
@param frame frame
@return 对象
*/
+ (instancetype)buttonWithFrame:(CGRect)frame;
/**
边缘颜色
@param color color
*/
- (void)setborderColor:(UIColor *)color;
/**
边缘宽度
@param width width
*/
- (void)setborderWidth:(CGFloat)width;
/**
手动执行开始动画(一般在button的响应里调用,调用后会走代理进行回调)
*/
- (void)startAnimation;
/**
手动停止动画(停止前和停止后会走代理回调)
*/
- (void)stopAnimation;
@end
SHAnimationButton.m
#import "SHAnimationButton.h"
#import "SHCircleAnimationView.h"
static NSTimeInterval startDuration = 0.3;
static NSTimeInterval endDuration = 0.5;
@interface SHAnimationButton ()
@property (nonatomic, strong) SHCircleAnimationView *circleView;
@property (nonatomic, assign) CGRect origionRect;
@end
@implementation SHAnimationButton
- (SHCircleAnimationView *)circleView{
if (!_circleView) {
_circleView = [SHCircleAnimationView viewWithButton:self];
[self addSubview:_circleView];
}
return _circleView;
}
+ (instancetype)buttonWithFrame:(CGRect)frame{
SHAnimationButton *button = [SHAnimationButton buttonWithType:UIButtonTypeCustom];
button.frame = frame;
button.layer.cornerRadius = frame.size.height / 2;
button.layer.masksToBounds = YES;
// 不裁剪超出父视图的子视图部分
button.clipsToBounds = NO;
button.origionRect = button.frame;
return button;
}
- (void)setborderColor:(UIColor *)color{
self.layer.borderColor = color.CGColor;
}
- (void)setborderWidth:(CGFloat)width{
self.layer.borderWidth = width;
}
- (void)startAnimation{
CGPoint center = self.center;
CGFloat width = self.layer.cornerRadius * 2;
CGFloat height = self.frame.size.height;
CGRect desFrame = CGRectMake(center.x - width / 2, center.y - height / 2, width, height);
self.userInteractionEnabled = NO;
if ([self.delegate respondsToSelector:@selector(SHAnimationButtonDidStartAnimation:)]) {
[self.delegate SHAnimationButtonDidStartAnimation:self];
}
[UIView animateWithDuration:startDuration animations:^{
self.titleLabel.alpha = .0f;
self.frame = desFrame;
} completion:^(BOOL finished) {
[self.circleView startAnimation];
}];
}
- (void)stopAnimation{
self.userInteractionEnabled = YES;
if ([self.delegate respondsToSelector:@selector(SHAnimationButtonWillFinishAnimation:)]) {
[self.delegate SHAnimationButtonWillFinishAnimation:self];
}
[self.circleView removeFromSuperview];
self.circleView = nil;
[UIView animateWithDuration:endDuration animations:^{
self.frame = self.origionRect;
self.titleLabel.alpha = 1.0f;
} completion:^(BOOL finished) {
if ([self.delegate respondsToSelector:@selector(SHAnimationButtonDidFinishAnimation:)]) {
[self.delegate SHAnimationButtonDidFinishAnimation:self];
}
}];
}
@end
2.然后是SHCircleAnimationView,利用UIBezierPath绘制外围的圆圈视图
SHCircleAnimationView.h
#import
@interface SHCircleAnimationView : UIView
+ (instancetype)viewWithButton:(UIButton *)button;
- (void)startAnimation;
@end
SHCircleAnimationView.m
#import "SHCircleAnimationView.h"
@interface SHCircleAnimationView ()
@property (nonatomic, strong) UIColor *borderColor;
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) CGFloat timeFlag;
@end
@implementation SHCircleAnimationView
+ (instancetype)viewWithButton:(UIButton *)button{
SHCircleAnimationView *animationView = [[SHCircleAnimationView alloc] initWithFrame:CGRectMake(-8, -8, button.frame.size.width + 16, button.frame.size.height + 16)];
animationView.backgroundColor = [UIColor clearColor];
animationView.borderColor = button.titleLabel.textColor;
animationView.timeFlag = 0;
return animationView;
}
- (void)removeFromSuperview{
[self.timer invalidate];
[super removeFromSuperview];
}
- (void)startAnimation{
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(continueAnimation) userInfo:nil repeats:YES];
[self.timer fire];
}
- (void)continueAnimation{
self.timeFlag += 0.02;
// 因为drawRect方法只调用1次,所以如果需要刷新图形,需要用setNeedsDisplay强制调用刷新
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect{
UIBezierPath *path = [UIBezierPath bezierPath];
// 圆心
CGPoint center = CGPointMake(rect.size.width / 2, rect.size.height / 2);
// 半径
CGFloat radius = rect.size.width / 2 - 2;
// 起始的弧度
CGFloat start = -M_PI_2 + self.timeFlag * 2 * M_PI;
// 圆弧结束的弧度
CGFloat end = -M_PI_2 + 0.45 * 2 * M_PI + self.timeFlag * 2 * M_PI;
[path addArcWithCenter:center radius:radius startAngle:start endAngle:end clockwise:YES]; // clockwise:YES为顺时针,NO为逆时针
// 设置描边色
[self.borderColor setStroke];
// 设置描边宽度
path.lineWidth = 1.5;
// 描边
[path stroke];
}
@end
二、navigation动画相关代码:
1.首先是SHNavAnimation,navgationController的动画
SHNavAnimation.h
#import
@interface SHNavAnimation : NSObject
@property (nonatomic, strong) UIButton *centerButton;
@end
SHNavAnimation.m
#import "SHNavAnimation.h"
@interface SHNavAnimation ()
@property (nonatomic, strong) id transitionContext;
@end
@implementation SHNavAnimation
/**
动画持续时间
@param transitionContext 转场上下文
@return NSTimeInterval
*/
- (NSTimeInterval)transitionDuration:(id)transitionContext{
return 0.5;
}
/**
动画的内容
@param transitionContext 转场上下文
*/
- (void)animateTransition:(id)transitionContext{
self.transitionContext = transitionContext;
UIView *contentView = [self.transitionContext containerView];
CGPoint point = self.centerButton.center;
// 创建在rect里的内切曲线
UIBezierPath *origionPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(point.x , point.y, 0, 0)];
CGFloat X = [UIScreen mainScreen].bounds.size.width - point.x;
CGFloat Y = [UIScreen mainScreen].bounds.size.height - point.y;
CGFloat radius = sqrtf(X * X + Y * Y);
UIBezierPath *finalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(CGRectMake(point.x , point.y, 0, 0), -radius, -radius)];
// 获取参与转场的视图控制器
UIViewController *toVc = [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = finalPath.CGPath;
toVc.view.layer.mask = layer;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.delegate = self;
// 所改变属性的起始值
animation.fromValue = (__bridge id _Nullable)(origionPath.CGPath);
// 所改变属性的结束时的值
animation.toValue = (__bridge id _Nullable)(finalPath.CGPath);
// 动画的时长
animation.duration = 0.25;
// 添加动画
[layer addAnimation:animation forKey:@"path"];
[contentView addSubview:toVc.view];
}
#pragma mark - CAAnimationDelegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
[self.transitionContext completeTransition:YES];
}
@end
2.然后是SHNavigationController,自定义navigationController,使用自定义的push方法
SHNavigationController.h
#import
@interface SHNavigationController : UINavigationController
- (void)pushViewController:(UIViewController *)viewController withCenterButton:(UIButton*)button;
@end
SHNavigationController.m
#import "SHNavigationController.h"
#import "SHNavAnimation.h"
@interface SHNavigationController ()
@property (nonatomic, strong) UIButton *centerButton;
@end
@implementation SHNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)pushViewController:(UIViewController *)viewController withCenterButton:(UIButton *)button{
self.centerButton = button;
self.delegate = self;
[super pushViewController:viewController animated:YES];
}
#pragma mark - UINavigationControllerDelegate
- (id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
SHNavAnimation *animation = [[SHNavAnimation alloc] init];
animation.centerButton = self.centerButton;
return animation;
}
@end
三、调用
需要先将controller放到SHNavigationController中
#import "ViewController.h"
#import "SHAnimationButton.h"
#import "SecondViewController.h"
#import "SHNavigationController.h"
@interface ViewController ()
@property (nonatomic, strong) SHAnimationButton *button;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 可以在这里将navigationBar隐藏,效果会更好
self.navigationController.navigationBarHidden = YES;
self.view.backgroundColor = [UIColor cyanColor];
SHAnimationButton *okButton = [SHAnimationButton buttonWithFrame:CGRectMake(80, 100, self.view.bounds.size.width - 2 * 80, 50)];
okButton.delegate = self;
okButton.backgroundColor = [UIColor whiteColor];
[okButton setTitle:@"OK" forState:(UIControlStateNormal)];
[okButton setTitleColor:[UIColor redColor] forState:(UIControlStateNormal)];
[self.view addSubview:okButton];
[okButton addTarget:self action:@selector(click:) forControlEvents:(UIControlEventTouchUpInside)];
SHAnimationButton *loginButton = [SHAnimationButton buttonWithFrame:CGRectMake(30, 300, self.view.bounds.size.width - 2 * 30, 50)];
loginButton.delegate = self;
loginButton.backgroundColor = [UIColor whiteColor];
[loginButton setTitle:@"Login" forState:(UIControlStateNormal)];
[loginButton setTitleColor:[UIColor lightGrayColor] forState:(UIControlStateNormal)];
[self.view addSubview:loginButton];
[loginButton addTarget:self action:@selector(click:) forControlEvents:(UIControlEventTouchUpInside)];
self.button = loginButton;
}
- (void)click:(SHAnimationButton *)button{
[button startAnimation];
}
#pragma mark - SHAnimationButtonDelegate
- (void)SHAnimationButtonDidStartAnimation:(SHAnimationButton *)SHAnimationButton{
NSLog(@"开始动画");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[SHAnimationButton stopAnimation];
});
}
- (void)SHAnimationButtonWillFinishAnimation:(SHAnimationButton *)SHAnimationButton{
NSLog(@"动画将要结束时回调 即 结束动画未执行时");
if (SHAnimationButton == self.button) {
SecondViewController *secondVC = [[SecondViewController alloc] init];
[((SHNavigationController*)self.navigationController) pushViewController:secondVC withCenterButton:SHAnimationButton];
}
}
- (void)SHAnimationButtonDidFinishAnimation:(SHAnimationButton *)SHAnimationButton{
NSLog(@"结束动画");
}
@end