前言
最近根据产品的需求,要求实现tabBar的动画,自己之前做过类似的,纯自定义tabBar来加载想要的动画。这次的需求里还添加了一个plusButton,类似闲鱼的tabBar
所以基于CYLTabBarController实现了更加方便定制的tabBar,然后加上自己的动画效果。最后的效果如下(当然,这是参考Demo):
先简单介绍一下CYLTabBarController的使用
- 由于需要手动修改一些方法,所以不建议使用cocoapods,而是手动集成
- 接下来初始化TabBarController,这里参考CYLTabBarController采取Config的方式。
CYLTabBarControllerConfig.h
CYLTabBarControllerConfig.m
这里只显示了部分,详细的请参考Demo。
- 自定义PlusButton。 需要继承CYLPlusButton,以及添加协议CYLPlusButtonSubclassing。定制想要的样式。
- AppDelegate中。
(方便大家直接可以看,所以这里放的是截图,代码在Demo里都有)
接下来讲述一下动画的定制。
翻转动画
我们想要实现的效果是水平翻转动画。简单而言,对于实现翻转动画,方式有很多。
使用Pop
Facebook的Pop动画是很强大的,遇到想要实现动画的地方第一反应就是它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| POPBasicAnimation *rotateAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerRotationY]; rotateAnimation.fromValue = @(0); rotateAnimation.toValue = @(M_PI * 2); rotateAnimation.duration = 0.6; // 0.5 0.3 [view.layer pop_addAnimation:rotateAnimation forKey:@"layerRotateAnimation"]; [rotateAnimation setCompletionBlock:^(POPAnimation *animation, BOOL finished) { if (finished) { [UIView animateWithDuration:0.3 animations:^{ // Pop 选择动画有时会发生动画停止的时候位置有稍微偏移的情况,所以最好在动画结束后恢复一下 view.transform = CGAffineTransformIdentity; } completion:nil]; } }];
|
使用CABasicAnimation
系统动画,比较直接,这里同样也可以使用关键帧动画,可以做得更加自然一点
1 2 3 4 5 6 7 8 9 10 11
| CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
// 设定动画选项 rotateAnimation.duration = 1.2; // 持续时间 rotateAnimation.repeatCount = 1; // 重复次数
// 设定旋转角度 rotateAnimation.fromValue = @(0); // 起始角度 rotateAnimation.toValue = @(M_PI * 2); // 终止角度
[view.layer addAnimation:rotateAnimation forKey:@"rotateAnimation"];
|
1 2 3 4 5 6 7 8 9
| [UIView animateWithDuration:0.32 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ view.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0); } completion:nil]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [UIView animateWithDuration:0.70 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{ view.layer.transform = CATransform3DMakeRotation(2 * M_PI, 0, 1, 0); } completion:nil]; });
|
我采用的是第三种,其他二者都是可以的。之所以写得有点繁琐是为了看起来更加自然一点。
tabBar中添加动画
tabBar的自定义过程中我采取了CYLTabBarController的,另外还可以完全自定义,完全自定义的话,所有的tabBarItem都是自定义的Button,动画直接在event中添加即可,这里就不详述,有兴趣的话可以私下交流一下。
这里来讲述CYLTabBarController的tabBar动画。
在CYLTabBar中的
1
| - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
|
添加位置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { BOOL canNotResponseEvent = self.hidden || (self.alpha <= 0.01f) || (self.userInteractionEnabled == NO); if (canNotResponseEvent) { return nil; } if (!CYLExternPlusButton && ![self pointInside:point withEvent:event]) { return nil; } if (CYLExternPlusButton) { CGRect plusButtonFrame = self.plusButton.frame; BOOL isInPlusButtonFrame = CGRectContainsPoint(plusButtonFrame, point); if (!isInPlusButtonFrame && (point.y < 0) ) { return nil; } if (isInPlusButtonFrame) { self.times ++; if (self.times % 2 == 0) { // 这里添加动画 [self addRotateAnimationOnPlusButton]; } return CYLExternPlusButton; } } NSArray *tabBarButtons = self.tabBarButtonArray; if (self.tabBarButtonArray.count == 0) { tabBarButtons = [self tabBarButtonFromTabBarSubviews:self.subviews]; } for (NSUInteger index = 0; index < tabBarButtons.count; index++) { UIView *selectedTabBarButton = tabBarButtons[index]; CGRect selectedTabBarButtonFrame = selectedTabBarButton.frame; if (CGRectContainsPoint(selectedTabBarButtonFrame, point)) { self.times ++; if (self.times % 2 == 1) { // 这里添加动画 [self addRotateAnimation:selectedTabBarButton]; } return selectedTabBarButton; } } return nil; }
|
动画 上述两个位置加的动画实现一样,区分开来方便分开定制
1 2 3 4 5 6 7 8 9 10 11 12
| - (void)addRotateAnimationOnPlusButton { // 这里动画 [UIView animateWithDuration:0.32 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ _plusButton.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0); } completion:nil]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [UIView animateWithDuration:0.70 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{ _plusButton.layer.transform = CATransform3DMakeRotation(2 * M_PI, 0, 1, 0); } completion:nil]; }); }
|
由于hit的响应比较频繁,所以采取了折半。
重要提示
在设置tabBar的过程中,如果没有设置背景图片,即为透明的话,动画是正常的。但是如果为了需要设置了背景图片,比如:
1
| [[UITabBar appearance] setBackgroundImage:[UIImage imageNamed:@"tab_bar"]];
|
那么动画会有明显的异常。
很明显的异常。这个问题我想了挺长时间,开始一直以为是我动画方法的问题,所以尝试了很多动画,但都一样。最后和别人讨论了一下问题的原因,弄了个解决方法。
背景与按钮图片处于同一层次,当按钮图片旋转时,转轴就在背景图上,所以会有一部分在背景图之下 所以会有一半被遮住的情况出现。
两个在同一图层,所以动画出现了异常。那么如何解决呢?
- 可以自定义UIimageView放在tabBar上代替tabBarItem,然后将imageView.layer.zPosition = 最大图片宽度
- 找到tabbar中放置图片的imageView,调整zPosition
我这里采取了第一种方法。
1 2 3
| for (UIView *tabBarButton in self.tabBar.subviews) { tabBarButton.layer.zPosition = 65.f / 2;//最大图片宽度的一半 }
|
还有一点,这个设置需要放在TabBarViewController的viewWillAppear方法中
1 2 3 4 5 6
| - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; for (UIView *tabBarButton in self.tabBar.subviews) { tabBarButton.layer.zPosition = 65.f / 2;//最大图片宽度的一半 } }
|
为了定制方便,建议大家继承CYLTabBarController,使用自己的,添加功能,以及这个设置。
详情参考Demo,希望大家指出我的不足以及改进之处,共同学习进步
附github地址:
https://github.com/hllGitHub/CYLTabBarController
也可以加入群:(139852091)Demo不用写,说就行 一起交流。