一、简介
POP 动画极为流畅,其秘密就在于这个 Engine 中的POPAnimator 里,POP 通过 CADisplayLink 高达 60 FPS 的特性,打造了一个游戏级的动画引擎。
源码:https://github.com/facebook/pop
二、基本用法
POPSpringAnimation 有弹性效果的动画类(个人比较喜欢这个)
POPBasicAnimation 基本动画类
POPDecayAnimation 衰减动画类
POPCustomAnimation 可以自定义动画的类
1.POPBasicAnimation
NSInteger height = CGRectGetHeight(self.view.bounds);
NSInteger width = CGRectGetWidth(self.view.bounds);
CGFloat centerX = arc4random() % width;
CGFloat centerY = arc4random() % height;
POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPViewCenter];
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(centerX, centerY)];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.duration = 0.4;
[self.testView pop_addAnimation:anim forKey:@"centerAnimation"];
2.POPSpringAnimation
1.springBounciness 弹簧弹力 取值范围为[0, 20],默认值为4
2.springSpeed 弹簧速度,速度越快,动画时间越短 [0, 20],默认为12,和springBounciness一起决定着弹簧动画的效果
3.dynamicsTension 弹簧的张力
4.dynamicsFriction 弹簧摩擦
5.dynamicsMass 质量 。张力,摩擦,质量这三者可以从更细的粒度上替代springBounciness和springSpeed
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPViewCenter];
NSInteger height = CGRectGetHeight(self.view.bounds);
NSInteger width = CGRectGetWidth(self.view.bounds);
CGFloat centerX = arc4random() % width;
CGFloat centerY = arc4random() % height;
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(centerX, centerY)];
anim.springBounciness = 16;
anim.springSpeed = 6;
[self.testView pop_addAnimation:anim forKey:@"center"];
3.POPDecayAnimation
velocity 也是必须和你操作的属性有相同的结构,如果你操作的是 bounds,想实现一个水滴滴到桌面的扩散效果,那么应该是 [NSValue valueWithCGRect:CGRectMake(0, 0,20.0, 20.0)]
如果 velocity 是负值,那么就会反向递减。
deceleration (负加速度) 是一个你会很少用到的值,默认是就是我们地球的 0.998
POPDecayAnimation *anim = [POPDecayAnimation animWithPropertyNamed:kPOPLayerPositionX];
anim.velocity = @(100.0);
anim.fromValue = @(25.0);
//anim.deceleration = 0.998;
anim.completionBlock = ^(POPAnimation *anim, BOOL finished) {
if (finished) {NSLog(@"Stop!");}};
三、发布界面的弹出效果
#import "XMGPublishViewController.h"
#import "TWVerticalButton.h"
#import "TWPostWordViewController.h"
#import "TWPostBlogViewController.h"
#import "TWPostPictureViewController.h"
#import "TWNavigationController.h"
#import <POP.h>
#import <AFNetworking.h>
#import <SVProgressHUD.h>
static CGFloat const XMGAnimationDelay = 0.1;
static CGFloat const XMGSpringFactor = 10;
@interface XMGPublishViewController ()
@end
@implementation XMGPublishViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self cheakNetworkStatus];
// 让控制器的view不能被点击
self.view.userInteractionEnabled = NO;
// 数据
NSArray *images = @[@"publish-text", @"publish-picture", @"publish-text"];
NSArray *titles = @[@"发说说", @"发图片", @"发日志"];
// 中间的6个按钮
int maxCols = 3;
CGFloat buttonW = 72;
CGFloat buttonH = buttonW + 30;
CGFloat buttonStartY = (TWScreenH - buttonH) * 0.5;
CGFloat buttonStartX = 20;
CGFloat xMargin = (TWScreenW - 2 * buttonStartX - maxCols * buttonW) / (maxCols - 1);
for (int i = 0; i<images.count; i++) {
TWVerticalButton *button = [[TWVerticalButton alloc] init];
button.tag = i;
[button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
// 设置内容
button.titleLabel.font = [UIFont systemFontOfSize:14];
[button setTitle:titles[i] forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:images[i]] forState:UIControlStateNormal];
// 计算X\Y
int row = i / maxCols;
int col = i % maxCols;
CGFloat buttonX = buttonStartX + col * (xMargin + buttonW);
CGFloat buttonEndY = buttonStartY + row * buttonH;
CGFloat buttonBeginY = buttonEndY - TWScreenH;
// 按钮动画
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
anim.fromValue = [NSValue valueWithCGRect:CGRectMake(buttonX, buttonBeginY, buttonW, buttonH)];
anim.toValue = [NSValue valueWithCGRect:CGRectMake(buttonX, buttonEndY, buttonW, buttonH)];
anim.springBounciness = XMGSpringFactor;
anim.springSpeed = XMGSpringFactor;
anim.beginTime = CACurrentMediaTime() + XMGAnimationDelay * i;
[button pop_addAnimation:anim forKey:nil];
}
// 添加标语
CGFloat centerX = TWScreenW * 0.5;
CGFloat centerEndY = TWScreenH * 0.2;
CGFloat centerBeginY = centerEndY - TWScreenH;
UIImageView *sloganView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"app_slogan"]];
sloganView.center = CGPointMake(centerX, centerBeginY);
[self.view addSubview:sloganView];
// 标语动画
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPViewCenter];
anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(centerX, centerBeginY)];
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(centerX, centerEndY)];
anim.beginTime = CACurrentMediaTime() + images.count * XMGAnimationDelay;
anim.springBounciness = XMGSpringFactor;
anim.springSpeed = XMGSpringFactor;
[anim setCompletionBlock:^(POPAnimation *anim, BOOL finished) {
// 标语动画执行完毕, 恢复点击事件
self.view.userInteractionEnabled = YES;
}];
[sloganView pop_addAnimation:anim forKey:nil];
}
- (void)cheakNetworkStatus{
// 1.获得网络监控的管理者
AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager];
// 2.设置网络状态改变后的处理
[mgr setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
// 当网络状态改变了, 就会调用这个block
switch (status) {
case AFNetworkReachabilityStatusUnknown: // 未知网络
NSLog(@"未知网络");
break;
case AFNetworkReachabilityStatusNotReachable: // 没有网络(断网)
[SVProgressHUD showImage:nil status:@"请连接网络"];
break;
case AFNetworkReachabilityStatusReachableViaWWAN: // 手机自带网络
NSLog(@"手机自带网络");
break;
case AFNetworkReachabilityStatusReachableViaWiFi: // WIFI
NSLog(@"WIFI");
break;
}
}];
// 3.开始监控
[mgr startMonitoring];
}
- (void)buttonClick:(UIButton *)button
{
[self cancelWithCompletionBlock:^{
if (button.tag == 0) {
TWPostWordViewController *postWord = [[TWPostWordViewController alloc] init];
TWNavigationController *nav = [[TWNavigationController alloc] initWithRootViewController:postWord];
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
[root presentViewController:nav animated:YES completion:nil];
}else if(button.tag == 1){
TWPostPictureViewController *postWord = [[TWPostPictureViewController alloc] init];
TWNavigationController *nav = [[TWNavigationController alloc] initWithRootViewController:postWord];
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
[root presentViewController:nav animated:YES completion:nil];
}else if(button.tag == 2){
TWPostBlogViewController *postWord = [[TWPostBlogViewController alloc] init];
TWNavigationController *nav = [[TWNavigationController alloc] initWithRootViewController:postWord];
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
[root presentViewController:nav animated:YES completion:nil];
}else {
}
}];
}
- (IBAction)cancel {
[self cancelWithCompletionBlock:nil];
}
/**
* 先执行退出动画, 动画完毕后执行completionBlock
*/
- (void)cancelWithCompletionBlock:(void (^)())completionBlock
{
// 让控制器的view不能被点击
self.view.userInteractionEnabled = NO;
int beginIndex = 2;
for (int i = beginIndex; i<self.view.subviews.count; i++) {
UIView *subview = self.view.subviews[i];
// 基本动画
POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPViewCenter];
CGFloat centerY = subview.centerY + TWScreenH;
// 动画的执行节奏(一开始很慢, 后面很快)
// anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(subview.centerX, centerY)];
anim.beginTime = CACurrentMediaTime() + (i - beginIndex) * XMGAnimationDelay;
[subview pop_addAnimation:anim forKey:nil];
// 监听最后一个动画
if (i == self.view.subviews.count - 1) {
[anim setCompletionBlock:^(POPAnimation *anim, BOOL finished) {
[self dismissViewControllerAnimated:NO completion:nil];
// 执行传进来的completionBlock参数
!completionBlock ? : completionBlock();
}];
}
}
}
/**
pop和Core Animation的区别
1.Core Animation的动画只能添加到layer上
2.pop的动画能添加到任何对象
3.pop的底层并非基于Core Animation, 是基于CADisplayLink
4.Core Animation的动画仅仅是表象, 并不会真正修改对象的frame\size等值
5.pop的动画实时修改对象的属性, 真正地修改了对象的属性
*/
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self cancelWithCompletionBlock:nil];
}
@end