前言
最近根据产品的需求,要求实现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动画是很强大的,遇到想要实现动画的地方第一反应就是它。
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
系统动画,比较直接,这里同样也可以使用关键帧动画,可以做得更加自然一点
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"];
使用transform动画
[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中的
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
添加位置如下:
- (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;
}
动画 上述两个位置加的动画实现一样,区分开来方便分开定制
- (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的过程中,如果没有设置背景图片,即为透明的话,动画是正常的。但是如果为了需要设置了背景图片,比如:
[[UITabBar appearance] setBackgroundImage:[UIImage imageNamed:@"tab_bar"]];
那么动画会有明显的异常。
很明显的异常。这个问题我想了挺长时间,开始一直以为是我动画方法的问题,所以尝试了很多动画,但都一样。最后和别人讨论了一下问题的原因,弄了个解决方法。
背景与按钮图片处于同一层次,当按钮图片旋转时,转轴就在背景图上,所以会有一部分在背景图之下 所以会有一半被遮住的情况出现。
两个在同一图层,所以动画出现了异常。那么如何解决呢?
- 可以自定义UIimageView放在tabBar上代替tabBarItem,然后将imageView.layer.zPosition = 最大图片宽度
- 找到tabbar中放置图片的imageView,调整zPosition
我这里采取了第一种方法。
for (UIView *tabBarButton in self.tabBar.subviews) {
tabBarButton.layer.zPosition = 65.f / 2;//最大图片宽度的一半
}
还有一点,这个设置需要放在TabBarViewController的viewWillAppear方法中
- (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不用写,说就行 一起交流。