tabBar动画实现 -- UITabBarItem翻转

前言

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

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

效果图

先简单介绍一下CYLTabBarController的使用

  • 由于需要手动修改一些方法,所以不建议使用cocoapods,而是手动集成
    代码结构
  • 接下来初始化TabBarController,这里参考CYLTabBarController采取Config的方式。
    CYLTabBarControllerConfig.h
    image

    CYLTabBarControllerConfig.m
    image
    image
    image
    image

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

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

  • AppDelegate中。
    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"]];

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

异常效果

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容