In a recent revision of the project, the product design redesigned tabbar animation to improve the app’s forced grid. The design drawing is borrowed from tabBar:

Look it up. There is no open source code for it, so go ahead and do it yourself.

First disassemble functional points:

  • Custom tabBar, height 56
  • TAB display: Home page TAB when selected is a big logo without text, not selected is a picture text; All other tabs that are not checked or checked are picture text
  • TAB switch: Click between tabs to switch between selected zooming in animation
  • When the home page slides to a certain distance, the large logo and small rocket of the home page TAB execute the switching animation:Gesture up - LOGO down switch to small rocket; Gesture down - Small rocket up switch to logo;

Let’s start piecemeal

1. Custom tabBar, height 56

Delete system tabBar, create custom tabBar,

@implementation WMTabBar - (void)layoutSubviews {[super layoutSubviews]; for (UIView *view in self.subviews) { if ([view isKindOfClass:NSClassFromString(@"UITabBarButton")]) { [view removeFromSuperview]; }} // create a custom 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; // Default is 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]; } // select index, override logic self.selectedIndex = 0 in setter methods; } <! Select index to override setter method, --> - (void)setSelectedIndex:(NSInteger)selectedIndex {_selectedIndex = selectedIndex; [self.itemArray enumerateObjectsUsingBlock:^(WMTabBarItem *tabBarItem, NSUInteger idx, BOOL * _Nonnull stop) {// if idx=selectedIndex, Record selected BOOL selected = (independence idx = = selectedIndex); / / the contents of the configuration tabBarItem information [tabBarItem configTitle: self. TitleArray [independence idx] normalImage:self.imageArray[idx] selectedImage:self.selectedImageArray[idx] index:idx selected:selected LastSelectIndex: self. LastSelectIndex]; / / when traversing to the last, If (idx == (self.itemarray.count -1)) {self.lastSelectIndex = selectedIndex;}}]; }Copy the code

In WMTabBar, two WMTabBar proxy methods are added, imitating the clicking method of system tabBar, so as to satisfy the operation of tabBar clicking on the project or whether it is selected according to the login state when clicking tabBar:

@protocol WMTabBarDelegate <NSObject> /** select tabbar */ - (void)wmtabBar:(wmtabBar *) wmtabBar selectedWMTabBarItemAtIndex:(NSInteger)index; / whether optional tabbar * / * * - (BOOL) wmtabBar: (wmtabBar *) wmtabBar shouldSelectedWMTabBarItemAtIndex (NSInteger) index; @endCopy the code

Set the tabBar in WMTabBarController

// Change tabbar height - (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; }} / / added modules - (void) creatTabBarWithChildVCArray: (NSArray *) childVCArray titleArray: (titleArray NSArray *) 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; }Copy the code

2. TAB display: When TAB is selected, there is a big logo without text, when not selected, there is a picture text; All other tabs that are not checked or checked are picture text

Create the display UI inside the tabBarItem
@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) {/ / when for home page TAB, add the following [self addSubview: self. HomeTabSelectedBgView]; The first solution * / / / / * * / / [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); / * * the second scheme * / [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; }Copy the code
Handle in TAB click events:
// When the TAB item is clicked, Run - (void)configTitle:(NSString *)title normalImage:(NSString *)normalImage selectedImage:(NSString *)selectedImage index:(NSInteger)index selected:(BOOL)selected lastSelectIndex:(NSInteger )lastSelectIndex { self.titleLabel.text = title; // when index == 0, The page 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; The first solution * / / / / * * / / [self. HomeTabAnimateImageView setImage: [UIImage imageNamed: @ "tabbar_home_selecetedLogo"]]. // ** If the first TAB is the same as the last TAB, execute the push animation. If (lastSelectIndex == index) {if (self.flag) { [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 {/ / other 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"]; If (lastSelectIndex! = index) { [self animationWithNormalTab]; } }else { [self.imageView setImage:[UIImage imageNamed:normalImage]]; self.titleLabel.textColor = [self colorFromHexRGB:@"575D66"]; }}}Copy the code

This is done in the TAB click event:

  • When index==0, that is, the home page TAB, according to whether the state is seleted to determine whether the display is a large logo control or a normal text picture control.
  • Other TAB, hide the big logo control, only show the picture text control, and then according to whether it is seleted state to determine the selected and unselected picture and text color configuration.

3. TAB switching: Click between tabs to switch between selected zooming in animation

CABasicAnimation: CABasicAnimation: CABasicAnimation: CABasicAnimation:

- (void)animationForHomeTab {CABasicAnimation *animation = CABasicAnimation animationWithKeyPath:@"transform.scale"]; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; Animation. Duration = 0.2 f; Animation. FromValue = [NSNumber numberWithFloat: 0.5 f]; animation.toValue = [NSNumber numberWithFloat:1.f]; [self.homeTabSelectedBgView.layer addAnimation:animation forKey:nil]; } // Other tabs - (void)animationForNormalTab {CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; Animation. Duration = 0.25 f; Animation. FromValue = [NSNumber numberWithFloat: 0.5 f]; animation.toValue = [NSNumber numberWithFloat:1.f]; [self.imageView.layer addAnimation:animation forKey:nil]; [self.titleLabel.layer addAnimation:animation forKey:nil]; }Copy the code

The effect is as follows:

4. When the home page slides to a certain distance, the big logo and small rocket of the home page TAB execute the switching animation

By observing the design drawing, it can be concluded that the sliding animation scheme can be determined according to the sliding offset and sliding gesture of the home page:

In a sliding behavior: when offset > threshold, and gesture slide up - LOGO switch down to the small rocket; When offset < threshold and gesture down - small rocket up switch to logo;

Home sliding proxy implementation:
// tabBar animation - judge swipe gesture, According to gestures, offset animation types - (void) tabBarAnimateWithScrollView: (UIScrollView *) scrollView {CGFloat currentPostionOffsetY = scrollView.contentOffset.y; If (currentPostionOffsetY > self.lastPositionOffestY) {NSLog(@" gesture up "); / / tabBar animation if (self tabAnimateOnceScrollFlag) {/ / in a sliding and currentPostionOffsetY > 456, If ((currentPostionOffsetY > 456.f)) {NSLog(@" execute - switch rocket "); [[AppLoginHandle sharedInstance].tabBarController pushHomeTabBarAnimationType:anmationDirectionUp]; self.tabAnimateOnceScrollFlag = NO; }}}else {NSLog(@" gesture down "); / / if tabBar animation (self. TabAnimateOnceScrollFlag) {/ / in a slide, slide gestures, and currentPostionOffsetY < 456, If ((currentPostionOffsetY < 456.f)) {NSLog(@" trigger - toggles logo"); [[AppLoginHandle sharedInstance].tabBarController pushHomeTabBarAnimationType:anmationDirectionDown]; self.tabAnimateOnceScrollFlag = NO; }}}} // Execute this method when you start scrolling the view. One effective slide (start a slide, slide a short distance, as long as the finger does not release, only count as a slide), perform only once. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { if ([scrollView isEqual:self.collectionView]) { self.tabAnimateOnceScrollFlag = YES; self.lastPositionOffestY = scrollView.contentOffset.y; }}Copy the code
WMTabBar exposes the external call toggle animation as follows:
/ / logo and enumeration of rocket switch animation anmationDirection typedef NS_ENUM (NSUInteger anmationDirection) {anmationDirectionUp, / / push animation, the rockets head out, Logo anmationDirectionDown, / / push the animation, the rocket head, logo out}; @class WMTabBar; @protocol WMTabBarDelegate <NSObject> /** select tabbar */ - (void)wmtabBar:(wmtabBar *) wmtabBar didSelectWMTabBarItemAtIndex:(NSInteger)index; / whether optional tabbar * / * * - (BOOL) wmtabBar: (wmtabBar *) wmtabBar shouldSelectWMTabBarItemAtIndex (NSInteger) index; @end @interface WMTabBar : UITabBar @property (nonatomic, weak) id <WMTabBarDelegate> tabBarDelegate; @property (nonatomic, assign) anmationDirection anmationDirection; /** instantiate */ + (instancetype)tabBarWithTitleArray:(NSArray *)titleArray imageArray:(NSArray *)imageArray selectedImageArray:(NSArray *)selectedImageArray; - (void)selectedTabbarAtIndex:(NSNumber *)index; / / exposed to external switch animated logo and the rockets' method - (void) pushHomeTabBarAnimationType (anmationDirection) anmationDirection; @endCopy the code
WMTabBarItem to achieve the home TAB between the small rocket and logo switch animation
Here are two methods: the first is the initial attempt to achieve an imageView control through the CATransition push animation to switch between two images, as follows:
  • The first scheme is push animation scheme:
-(void)pushHomeTabAnimationUp {self.flag = YES; The first solution * / / / / * * / / [self. HomeTabAnimateImageView setImage: [UIImage imageNamed: @ "tabbar_home_selecetedPush"]]. // CATransition *animation = [CATransition animation]; // animation.type = kCATransitionPush; // animation. Subtype = kCATransitionFromTop; / / set the direction of the animation / / animation timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; / / animation. Duration = 0.25 f; // [self.homeTabAnimateImageView.layer addAnimation:animation forKey:@"pushAnimation"]; } // pushHomeTabAnimationDown -(void)pushHomeTabAnimationDown {self.flag = NO; The first solution * / / / / * * / / [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"]; }Copy the code

The effect is as follows:

Although the function can be realized, there will be residual effect in the switching process, which is not very good display, and then think about whether there is a better implementation method.

And then the collectionView comes to mind.

  • The second option is collection slide item:
Set the collectionView to the TAB size of the home page, and make the collection’s cell scrollToItem, i.e., slide to the NTH item, to achieve the animation effect as follows:
-(void)pushHomeTabAnimationUp {self.flag = YES; */ [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:1 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; } // pushHomeTabAnimationDown -(void)pushHomeTabAnimationDown {self.flag = NO; */ [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; }Copy the code

Beautiful!

The new tabBar implementation is complete!

Demo address stamp here