内容简介:最近项目改版里,产品设计重新设计了tabbar动画,旨在提升app的逼格。。。 设计图是借鉴淘宝的tabBar:找资料查找下,还没有相关开源的代码,好吧,那就自己开干吧。删除系统tabBar,创建自定义tabBar,
最近项目改版里,产品设计重新设计了tabbar动画,旨在提升app的逼格。。。 设计图是借鉴淘宝的tabBar:
找资料查找下,还没有相关开源的代码,好吧,那就自己开干吧。
先拆解功能点:
手势上滑 - logo向下切换到小火箭; 手势下滑 - 小火箭向上切换到logo;
开始各个击破吧
1. 自定义tabBar,高度56
删除系统tabBar,创建自定义tabBar,
@implementation WMTabBar // 删除系统tabbar的UITabBarButton - (void)layoutSubviews { [super layoutSubviews]; for (UIView *view in self.subviews) { if ([view isKindOfClass:NSClassFromString(@"UITabBarButton")]) { [view removeFromSuperview]; } } } // 根据配置信息创建自定义tabBar + (instancetype)tabBarWithTitleArray:(NSArray *)titleArray imageArray:(NSArray *)imageArray selectedImageArray:(NSArray *)selectedImageArray { WMTabBar *tabBar = [[WMTabBar alloc] init]; tabBar.titleArray = titleArray; tabBar.imageArray = imageArray; tabBar.selectedImageArray = selectedImageArray; [tabBar setupUI]; return tabBar; } - (void)setupUI { self.lastSelectIndex = 100;//默认为100 self.backgroundColor = [UIColor whiteColor]; for (int i = 0; i < self.titleArray.count; i++) { CGFloat itemWidth = (kScreen_width/self.titleArray.count); CGRect frame = CGRectMake(i*itemWidth, 0, itemWidth, 56); WMTabBarItem *tabBarItem = [[WMTabBarItem alloc] initWithFrame:frame index:i]; tabBarItem.tag = i ; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tabBarItemClickAction:)]; [tabBarItem addGestureRecognizer:tap]; [self addSubview:tabBarItem]; [self.itemArray addObject:tabBarItem]; } // 记录选中index, 在其setter方法里重写逻辑 self.selectedIndex = 0; } <!-- 选中index 重写setter方法,在里面做tabBar的item的内容动态配置 --> - (void)setSelectedIndex:(NSInteger )selectedIndex { _selectedIndex = selectedIndex; [self.itemArray enumerateObjectsUsingBlock:^(WMTabBarItem *tabBarItem, NSUInteger idx, BOOL * _Nonnull stop) { // 当遍历的idx=selectedIndex时,记录选中状态 BOOL selected = (idx == selectedIndex); // 配置tabBarItem的内容信息 [tabBarItem configTitle:self.titleArray[idx] normalImage:self.imageArray[idx] selectedImage:self.selectedImageArray[idx] index:idx selected:selected lastSelectIndex:self.lastSelectIndex]; // 当遍历到最后一个时,赋值lastSelectIndex if (idx == (self.itemArray.count-1)) { self.lastSelectIndex = selectedIndex; } }]; } 复制代码
在WMTabBar里仿照系统tabBar的点击方法,添加两个wmTabBar代理方法,以满足项目里tabBar点击选中或点击tabBar时根据登录状态进行是否选中等操作:
@protocol WMTabBarDelegate <NSObject> /** 选中tabbar */ - (void)wmtabBar:(WMTabBar *)wmTabBar selectedWMTabBarItemAtIndex:(NSInteger)index; /** 是否可选tabbar */ - (BOOL)wmtabBar:(WMTabBar *)wmTabBar shouldSelectedWMTabBarItemAtIndex:(NSInteger)index; @end 复制代码
在WMTabBarController里设置tabBar
// 重写tabbar的frame,改变tabbar高度 - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; CGRect frame = self.tabBar.frame; if (frame.size.height != kTABBARHEIGHT) { frame.size.height = kTABBARHEIGHT; frame.origin.y = self.view.frame.size.height - frame.size.height; self.tabBar.frame = frame; } } //添加子模块 - (void)creatTabBarWithChildVCArray:(NSArray *)childVCArray titleArray:(NSArray *)titleArray imageArray:(NSArray *)imageArray selectedImageArray:(NSArray *)selectedImageArray { for (UIViewController *viewController in childVCArray) { MPNavigationController *navigationController = [[MPNavigationController alloc] initWithRootViewController:viewController]; [self.controllersArray addObject:navigationController]; } self.wmTabBar = [WMTabBar tabBarWithTitleArray:titleArray imageArray:imageArray selectedImageArray:selectedImageArray]; self.wmTabBar.tabBarDelegate = self; [self setValue:self.wmTabBar forKeyPath:@"tabBar"]; self.viewControllers = self.controllersArray; } 复制代码
2. tab显示:首页tab在选中时是大logo 无文字,未选中时是图片文字 ;其他tab未选中和选中都是图片文字
在tabBarItem里创建显示UI
@implementation WMTabBarItem - (instancetype)initWithFrame:(CGRect)frame index:(NSInteger )index { self = [super initWithFrame:frame]; if (self) { [self addSubview:self.titleLabel]; [self addSubview:self.imageView]; [self addSubview:self.homeTabSelectedBgView]; self.imageView.frame = CGRectMake(self.bounds.size.width/2-14, 7, 28, 28); self.titleLabel.frame = CGRectMake(0, CGRectGetMaxY(self.imageView.frame)+2, self.bounds.size.width, 14); self.homeTabSelectedBgView.frame = CGRectMake(self.bounds.size.width/2-21, 7, 42, 42); if (index == 0) { // 当为首页tab时,加上以下内容 [self addSubview:self.homeTabSelectedBgView]; // /** 第一种方案 */ // [self.homeTabSelectedBgView addSubview:self.homeTabAnimateImageView]; // self.homeTabAnimateImageView.frame = CGRectMake(0, 0, 32, 32); // self.homeTabAnimateImageView.center = CGPointMake(self.homeTabSelectedBgView.frame.size.width/2, self.homeTabSelectedBgView.frame.size.height/2); /** 第二种方案 */ [self.homeTabSelectedBgView addSubview:self.collectionView]; self.homeTabSelectedBgView.frame = CGRectMake(self.bounds.size.width/2-21, 7, 42, 42); self.collectionView.frame = CGRectMake(0, 0, 42, 42); self.collectionView.center = CGPointMake(self.homeTabSelectedBgView.frame.size.width/2, self.homeTabSelectedBgView.frame.size.height/2); [self.collectionView registerClass:[WMTabBarItemCell class] forCellWithReuseIdentifier:NSStringFromClass([WMTabBarItemCell class])]; } } return self; } 复制代码
在tab点击事件里处理:
// 当点击tab的item时,执行 - (void)configTitle:(NSString *)title normalImage:(NSString *)normalImage selectedImage:(NSString *)selectedImage index:(NSInteger)index selected:(BOOL)selected lastSelectIndex:(NSInteger )lastSelectIndex { self.titleLabel.text = title; // 当index == 0, 即首页tab if (index == 0) { if (selected) { [self.homeTabSelectedBgView setImage:[UIImage imageNamed:@"tabbar_home_selecetedBg"]]; self.homeTabSelectedBgView.hidden = NO; YES;self.imageView.hidden = self.titleLabel.hidden = YES; // /** 第一种方案 */ // [self.homeTabAnimateImageView setImage:[UIImage imageNamed:@"tabbar_home_selecetedLogo"]]; // /** 第二种方案 默认显示第一个cell 所以不用在这里设置图片 */ // 如果本次点击和上次是同一个tab 都是第0个,则执行push动画,否则执行放大缩小动画 if (lastSelectIndex == index) { if (self.flag) { // 如果已经是火箭状态,则点击切换logo,且发通知 让首页滑到顶部 [self pushHomeTabAnimationDown]; [[NSNotificationCenter defaultCenter] postNotificationName:@"kPushDownAnimationScrollTopNotification" object:nil]; } }else { [self animationWithHomeTab]; } }else { [self.imageView setImage:[UIImage imageNamed:normalImage]]; self.homeTabSelectedBgView.hidden = YES; self.imageView.hidden = self.titleLabel.hidden = NO; } }else { // 其他tab self.homeTabSelectedBgView.hidden = YES; self.imageView.hidden = self.titleLabel.hidden = NO; if (selected) { [self.imageView setImage:[UIImage imageNamed:selectedImage]]; self.titleLabel.textColor = [self colorFromHexRGB:@"18A2FF"]; // 如果本次点击和上次是同一个tab 则无反应,否则执行放大缩小动画 if (lastSelectIndex != index) { [self animationWithNormalTab]; } }else { [self.imageView setImage:[UIImage imageNamed:normalImage]]; self.titleLabel.textColor = [self colorFromHexRGB:@"575D66"]; } } } 复制代码
就是在tab点击事件里做处理:
- 当index==0 即首页tab时,根据是否是seleted状态 来判断显示是大logo控件还是正常文字图片控件。
- 其他tab时,隐藏大logo控件,只显示图片文字控件,再根据是否是seleted状态 来判断选中和未选中时的图片及文字颜色配置。
3.tab切换:tab之间相互点击切换选中时的缩小放大的动画
tab在相互点击切换选中时的缩小放大动画,通过CABasicAnimation来实现:
/** * tab之间切换动画 */ // 首页tab缩小放大动画 - (void)animationForHomeTab { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; animation.duration = 0.2f; animation.fromValue = [NSNumber numberWithFloat:0.5f]; animation.toValue = [NSNumber numberWithFloat:1.f]; [self.homeTabSelectedBgView.layer addAnimation:animation forKey:nil]; } // 其他tab缩小放大动画 - (void)animationForNormalTab { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; animation.duration = 0.25f; animation.fromValue = [NSNumber numberWithFloat:0.5f]; animation.toValue = [NSNumber numberWithFloat:1.f]; [self.imageView.layer addAnimation:animation forKey:nil]; [self.titleLabel.layer addAnimation:animation forKey:nil]; } 复制代码
效果如下:
4.当首页滑动到一定距离时,首页tab的大logo和小火箭执行切换动画
观察设计图,可以得出根据首页的滑动偏移量及滑动手势,来确定滑动动画方案:
在一次滑动行为中:当偏移量>阈值,且手势上滑 - logo向下切换到小火箭;当偏移量<阈值,且手势下滑 - 小火箭向上切换到logo;
首页里滑动代理实现:
// tabBar动画 - 判断滑动手势,再根据手势,偏移量 判断动画类型 - (void)tabBarAnimateWithScrollView:(UIScrollView *)scrollView { CGFloat currentPostionOffsetY = scrollView.contentOffset.y; if (currentPostionOffsetY > self.lastPositionOffestY) { NSLog(@"手势上滑"); // tabBar动画 if (self.tabAnimateOnceScrollFlag) { // 在一次滑动中,且currentPostionOffsetY>456,执行logo切换火箭:rocket:动画 if ((currentPostionOffsetY > 456.f)) { NSLog(@"执行-切换火箭"); [[AppLoginHandle sharedInstance].tabBarController pushHomeTabBarAnimationType:anmationDirectionUp]; self.tabAnimateOnceScrollFlag = NO; } } }else { NSLog(@"手势下滑"); // tabBar动画 if (self.tabAnimateOnceScrollFlag) { // 在一次滑动中,下滑手势, 且currentPostionOffsetY<456,执行火箭:rocket:切换logo动画 if ((currentPostionOffsetY < 456.f)) { NSLog(@"触发-切换logo"); [[AppLoginHandle sharedInstance].tabBarController pushHomeTabBarAnimationType:anmationDirectionDown]; self.tabAnimateOnceScrollFlag = NO; } } } } // 当开始滚动视图时,执行该方法。一次有效滑动(开始滑动,滑动一小段距离,只要手指不松开,只算一次滑动),只执行一次。 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { if ([scrollView isEqual:self.collectionView]) { self.tabAnimateOnceScrollFlag = YES; self.lastPositionOffestY = scrollView.contentOffset.y; } } 复制代码
WMTabBar里暴露出外部调用切换动画的方法如下:
// logo和火箭切换动画的枚举anmationDirection typedef NS_ENUM(NSUInteger, anmationDirection) { anmationDirectionUp,//push动画,火箭头出来,logo下去 anmationDirectionDown,//push动画,火箭头下去,logo出来 }; @class WMTabBar; @protocol WMTabBarDelegate <NSObject> /** 选中tabbar */ - (void)wmtabBar:(WMTabBar *)wmTabBar didSelectWMTabBarItemAtIndex:(NSInteger)index; /** 是否可选tabbar */ - (BOOL)wmtabBar:(WMTabBar *)wmTabBar shouldSelectWMTabBarItemAtIndex:(NSInteger)index; @end @interface WMTabBar : UITabBar @property (nonatomic, weak) id <WMTabBarDelegate> tabBarDelegate; @property (nonatomic, assign) anmationDirection anmationDirection; /** 实例化 */ + (instancetype)tabBarWithTitleArray:(NSArray *)titleArray imageArray:(NSArray *)imageArray selectedImageArray:(NSArray *)selectedImageArray; // 外部指定跳转到某个tab时调用 - (void)selectedTabbarAtIndex:(NSNumber *)index; // 暴露外部的切换动画logo和火箭的方法 - (void)pushHomeTabBarAnimationType:(anmationDirection )anmationDirection; @end 复制代码
WMTabBarItem里实现首页tab的小火箭和logo之间切换动画
这里写了两种方法,第一种是最开始尝试的,通过CATransition的push动画,来达到一个imageView控件的两个图片切换,如下:
- 第一种方案 push动画方案:
// push动画,火箭头出来 -(void)pushHomeTabAnimationUp { self.flag = YES; // /** 第一种方案 */ // [self.homeTabAnimateImageView setImage:[UIImage imageNamed:@"tabbar_home_selecetedPush"]]; // CATransition *animation = [CATransition animation]; // animation.type = kCATransitionPush;//设置动画的类型 // animation.subtype = kCATransitionFromTop; //设置动画的方向 // animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; // animation.duration = 0.25f; // [self.homeTabAnimateImageView.layer addAnimation:animation forKey:@"pushAnimation"]; } // push动画,火箭头落下 -(void)pushHomeTabAnimationDown { self.flag = NO; // /** 第一种方案 */ // [self.homeTabAnimateImageView setImage:[UIImage imageNamed:@"tabbar_home_selecetedLogo"]]; // CATransition *animation = [CATransition animation]; // animation.type = kCATransitionPush;//设置动画的类型 // animation.subtype = kCATransitionFromBottom; //设置动画的方向 // animation.duration = 0.25f; // [self.homeTabAnimateImageView.layer addAnimation:animation forKey:@"pushAnimation"]; } 复制代码
效果如下:
虽然能实现功能,但在切换过程中,会有残留效果,这显示是不太美好的,再想想有没有更好的实现方法。
然后就想到了collectionView了。
- 第二种方案 collection滑动item方案:
把collectionView设置成首页tab大小,通过让collection的cell的scrollToItem方法, 即滑动到第n个item来达到切换动画效果,如下:
// push动画,火箭头出来 -(void)pushHomeTabAnimationUp { self.flag = YES; /** 第二种方案 */ [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:1 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; } // push动画,火箭头落下 -(void)pushHomeTabAnimationDown { self.flag = NO; /** 第二种方案 */ [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; } 复制代码
漂亮!
到此,新版tabBar实现就完成了!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 如何利用强化学习设计出更好的火箭发动机
- 火箭五年四遇勇士,终究还是败了
- 火箭军总医院卢敬泰:互联网+医疗大数据辅助诊疗研究
- 滑动验证码的原理并利用 Vue 实现滑动验证码
- Flink 滑动窗口优化
- Flink 滑动窗口优化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
精通数据科学:从线性回归到深度学习
唐亘 / 人民邮电出版社 / 2018-5-8 / 99.00元
数据科学是一门内涵很广的学科,它涉及到统计分析、机器学习以及计算机科学三方面的知识和技能。本书深入浅出、全面系统地介绍了这门学科的内容。 本书分为13章,最初的3章主要介绍数据科学想要解决的问题、常用的IT工具Python以及这门学科所涉及的数学基础。第4-7章主要讨论数据模型,主要包含三方面的内容:一是统计中最经典的线性回归和逻辑回归模型;二是计算机估算模型参数的随机梯度下降法,这是模型工......一起来看看 《精通数据科学:从线性回归到深度学习》 这本书的介绍吧!