tabBar动画实现 -- UITabBarItem翻转

前言

最近根据产品的需求,要求实现tabBar的动画,自己之前做过类似的,纯自定义tabBar来加载想要的动画。这次的需求里还添加了一个plusButton,类似闲鱼的tabBar
闲鱼

所以基于CYLTabBarController实现了更加方便定制的tabBar,然后加上自己的动画效果。最后的效果如下(当然,这是参考Demo):

tabBar动画实现 -- UITabBarItem翻转_第1张图片
效果图

先简单介绍一下CYLTabBarController的使用

  • 由于需要手动修改一些方法,所以不建议使用cocoapods,而是手动集成
    tabBar动画实现 -- UITabBarItem翻转_第2张图片
    代码结构
  • 接下来初始化TabBarController,这里参考CYLTabBarController采取Config的方式。
    CYLTabBarControllerConfig.h
    tabBar动画实现 -- UITabBarItem翻转_第3张图片
    image

    CYLTabBarControllerConfig.m
    tabBar动画实现 -- UITabBarItem翻转_第4张图片
    image
    tabBar动画实现 -- UITabBarItem翻转_第5张图片
    image
    tabBar动画实现 -- UITabBarItem翻转_第6张图片
    image
    tabBar动画实现 -- UITabBarItem翻转_第7张图片
    image

    这里只显示了部分,详细的请参考Demo。
  • 自定义PlusButton。
    image

    需要继承CYLPlusButton,以及添加协议CYLPlusButtonSubclassing。定制想要的样式。

  • AppDelegate中。
    tabBar动画实现 -- UITabBarItem翻转_第8张图片
    image

(方便大家直接可以看,所以这里放的是截图,代码在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"]];

那么动画会有明显的异常。

tabBar动画实现 -- UITabBarItem翻转_第9张图片
异常效果

很明显的异常。这个问题我想了挺长时间,开始一直以为是我动画方法的问题,所以尝试了很多动画,但都一样。最后和别人讨论了一下问题的原因,弄了个解决方法。
背景与按钮图片处于同一层次,当按钮图片旋转时,转轴就在背景图上,所以会有一部分在背景图之下 所以会有一半被遮住的情况出现。

两个在同一图层,所以动画出现了异常。那么如何解决呢?

  • 可以自定义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不用写,说就行 一起交流。

你可能感兴趣的:(tabBar动画实现 -- UITabBarItem翻转)