How to handle subviews of UITabBarController when interactivePopGesture (like Flipboard)

1.4k views Asked by At

I'd like to have an underline that indicates which item was selected. It slides to any other items whenever the item was tapped. Therefore, I added a subview to the custom UITabBarController and set the animation. Then I use hidesBottomBarWhenPushed to hide the tab bar when pushed. However, the underline seems not combined with the custom UITabBarController.

How to handle the subview so it is always on top even when using the back gesture? This Flipboard app capture is what I want to do.

capture

Edit:

CustomTabBarController.m
- (void)viewDidLoad
{
    [super viewDidLoad];

    // create underline view
    CGRect tabBarFrame = self.tabBar.frame;
    CGFloat itemWidth = (CGFloat)CGRectGetWidth(tabBarFrame) / MIN(5, self.tabBar.items.count);
    CGFloat originX = (CGFloat)itemWidth * self.selectedIndex;
    CGRect underlineFrame = CGRectMake(originX, CGRectGetMaxY(tabBarFrame) - 3.0f, itemWidth, 3.0f);

    self.underlineView = [[UIView alloc] initWithFrame:underlineFrame];
    self.underlineView.backgroundColor = [UIColor redColor];
    [self.view addSubview:self.underlineView];
}

#pragma mark - UITabBarDelegate

- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
    NSUInteger itemIndex = [tabBar.items indexOfObject:item];
    CGRect underlineFrame = self.underlineView.frame;
    CGFloat originX = (CGFloat)CGRectGetWidth(self.underlineView.frame) * itemIndex;

    // underline shifting animation
    [UIView animateWithDuration:0.25
                     animations:^{
                         self.underlineView.frame = CGRectMake(originX, underlineFrame.origin.y, CGRectGetWidth(underlineFrame), CGRectGetHeight(underlineFrame));
                     }];
}

CustomTableViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    UIViewController *detailViewController = segue.destinationViewController;
    detailViewController.hidesBottomBarWhenPushed = YES;
}

hidesBottomBarWhenPushed hides the tab bar but its subview (the underline view). If I hide it by myself and show it in viewWillAppear, the underline view does not look like on top of the tab bar.

capture

1

There are 1 answers

0
Azules On BEST ANSWER

I finally found a workaround. To override the method hidesBottomBarWhenPushed, then you can add an alternative view for tab bar's subviews.

sourceViewController.m

- (BOOL)hidesBottomBarWhenPushed
{
    [super hidesBottomBarWhenPushed];

    CustomTabBarController *tabBarController = (CustomTabBarController *)self.tabBarController;

    if (tabBarController.underlineView.isHidden) {

        CGRect tabBarBounds = tabBarController.tabBar.bounds;
        CGFloat underlineHeight = CGRectGetHeight(tabBarController.underlineView.frame);
        CGFloat itemWidth = (CGFloat)CGRectGetWidth(tabBarBounds) / MIN(5, tabBarController.tabBar.items.count);
        CGFloat originX = (CGFloat)itemWidth * tabBarController.selectedIndex;

        UIView *alternativeView = [[UIView alloc] initWithFrame:CGRectMake(originX, 
                                                                CGRectGetMaxY(tabBarBounds) - underlineHeight,
                                                                itemWidth,
                                                                underlineHeight)];
        alternativeView.tag = tabBarController.underlineViewTag;
        alternativeView.backgroundColor = tabBarController.underlineView.backgroundColor;
        [tabBarController.tabBar addSubview:alternativeView];
    }

    return NO;
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    CustomTabBarController *tabBarController = (CustomTabBarController *)self.tabBarController;

    if (tabBarController.underlineView.isHidden) {
        tabBarController.underlineView.hidden = NO;

        NSInteger underlineViewTag = tabBarController.underlineViewTag;
        UIView *alternativeView = [tabBarController.tabBar viewWithTag:underlineViewTag];
        [alternativeView removeFromSuperview];
    }
}

Don't forget the case that interactivePopGesture failure to popover view controller, the alternative view still be added to tab bar. So remove it at destination view controller if needed.

destinationViewController.m

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    CustomTabBarController *tabBarController = (CustomTabBarController *)self.tabBarController;
    NSInteger underlineViewTag = tabBarController.underlineViewTag;
    UIView *alternativeView = [tabBarController.tabBar viewWithTag:underlineViewTag];

    if (alternativeView) [alternativeView removeFromSuperview];
}