Search code examples
iosuinavigationcontrolleruinavigationbar

How do I get a UINavigationController to NOT change its view size when setting the translucent property?


I have an app where up until now I've been using a UINavigationController with a UINavigationBar that has its property translucent = YES. This means the UINavigationController's content view (i.e. the views from the view controllers you push) to be full-screen (minus status bar).

However, if you set the navigationBar.translucent = NO, this container view becomes 44pt shorter, as I suppose Apple has assumed you don't need any content under an opaque navigationBar.

... except if you're doing what we're doing and are employing a navigationBar that scrolls away (see This Post on how to do that) So I'd like to know if this is possible.

I want to have translucent = NO, but have everything behave as if it were still set to YES. I like the functionality of the translucent = YES, but I don't actually want the bar to be made translucent by UIKit.


Solution

  • I found a solution that works, although it is indeed a bit of a hack.

    The idea is to give the translucent nav bar an opaque backing. Unfortunately I'm not happy with the solution in that it's dirty and not encapsulated and introduces some potential issues, but i AM happy because it got the job done.

    In my Application's base view controller class (i.e. MyViewController : UIViewController), in the viewDidLoad method, I instantiate a new ivar UIView *_navigationBarBG and give it the same frame as self.navigationController.navigationBar. I then set it's backgroundColor property to [UIColor whiteColor] although this is how you achieve some more tint I guess. [EDIT:If you wanted to be a purist (color values remaining exactly as they come from the .psd), you could make the _navigationBarBG a UIImageView and use your custom background there, and the background of the actual UINavigationBar you set to draw clear (or stretch a 1px transparent image if you wanted to use a typical 'change your navigation bar using an image' recipe that's somewhere on the internet)]

    if(self.navigationController)
    {
        _navigationBarBG = [[UIView alloc] initWithFrame: self.navigationController.navigationBar.frame];
        _navigationBarBG.backgroundColor = [UIColor whiteColor];
        [self.view addSubview:_navigationBarBG];
    }
    

    THEN, (and this is the crappy part, but I don't see any other way), I add this view as a subview. BUT, whenever you would normally make a call to [self.view addSubview: anyView], you have to make sure you call [self.view insertSubview: anyView belowSubview: _navigationBarBG];

    if (_navigationBarBG)
        [self.view insertSubview: anyView belowSubview:_navigationBarBG];
    else
        [self.view addSubview: anyView];
    

    If you forget that, these added views will slide under your navbar background and look weird. So you need to know that this is a source of error.

    WHY AM I DOING THIS? Again you might ask... I want to be able to have a scrolling navigation bar that scrolls out of the way when you scroll down your table view, thereby giving the user more screen space. This is done by using the scrollView delegate (scrollViewDidScroll:) and also viewWillAppear:

        // FIRST DEAL WITH SCROLLING NAVIGATION BAR
    CALayer *layer = self.navigationController.navigationBar.layer;
    
    CGFloat contentOffsetY = scrollView.contentOffset.y;
    CGPoint newPosition;
    
    
    if (contentOffsetY > _scrollViewContentOffsetYThreshold && self.scrollingNavigationBarEnabled) {
        newPosition = CGPointMake(layer.position.x,
                                     22 - MIN((contentOffsetY - _scrollViewContentOffsetYThreshold), 48.0));  // my nav bar BG image is 48.0 tall
        layer.position = newPosition;
        [_navigationBarBG setCenter: newPosition];  // if it's nil, nothing happens
    }
    else
    {
        newPosition = kNavBarDefaultPosition;  // i.e. CGPointMake(160, 22) -- portrait only
        layer.position = newPosition;
        [_navigationBarBG setCenter: newPosition]; // if it's nil, nothing happens
    }