30

I want to make the top of the navigation view a bit smaller. How would you achieve this? This is what I've tried so far, but as you can see, even though I make the navigationbar smaller, the area which it used to occupy is still there (black).

[window addSubview:[navigationController view]];
navigationController.view.frame = CGRectMake(0, 100, 320, 280);
navigationController.navigationBar.frame = CGRectMake(0, 0, 320, 20);
navigationController.view.backgroundColor = [UIColor blackColor];
[window makeKeyAndVisible];

alt text

quano
  • 18,812
  • 25
  • 97
  • 108
  • Why you want to change the standard, instead I think you should design your view to fit the space left. It violates the [Human Interface guidelines][1]. [1]: http://developer.apple.com/iphone/library/documentation/UserExperience/Conceptual/MobileHIG/SpecialViews/SpecialViews.html#//apple_ref/doc/uid/TP40006556-CH10-SW5 – Hoang Pham Jan 25 '10 at 15:37
  • It's more of a thing that bugs me. Maybe I wanted to make the navbar bigger. If I do that now, it's gonna cover the content (the tableview in this case). – quano Jan 25 '10 at 16:04
  • You should select an answer - I'd suggest mackross as all the other simple answers cause the subviews to move down. – David H Dec 10 '15 at 17:21

9 Answers9

79

Create a UINavigationBar Category with a custom sizeThatFits.

@implementation UINavigationBar (customNav)
  - (CGSize)sizeThatFits:(CGSize)size {
    CGSize newSize = CGSizeMake(self.frame.size.width,70);
    return newSize;
  }
@end
eagley
  • 3
  • 2
rnaud
  • 2,610
  • 32
  • 38
  • 2
    Great find! Maybe it's better to use what super returns and then change the height property? Edit: just realized that won't work in a category, only in subclass. – aegzorz Nov 11 '11 at 13:43
  • 2
    For those who want to take approach suggested by @aegzorz - using a subclass is not problematic if one uses the method suggested by Sebastian Celis in this article: http://sebastiancelis.com/2012/03/05/subclassing-hard-to-reach-classes/ – Ivan Vučica Sep 25 '12 at 14:05
  • 6
    Use a subsclass instead of a category, here your changing the base implementation and any code using it will loose the base implementation. Too bad ! – nverinaud Nov 28 '13 at 07:37
24

Using this navigation bar subclass I've successfully created a larger navigation bar on iOS 5.x to iOS 6.x on the iPad. This gives me a larger navigation bar but doesn't break all the animations.

static CGFloat const CustomNavigationBarHeight = 62;
static CGFloat const NavigationBarHeight = 44;
static CGFloat const CustomNavigationBarHeightDelta = CustomNavigationBarHeight - NavigationBarHeight;

@implementation HINavigationBar

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
//      UIColor *titleColor = [[HITheme currentTheme] fontColorForLabelForLocation:HIThemeLabelNavigationTitle];
//      UIFont *titleFont = [[HITheme currentTheme] fontForLabelForLocation:HIThemeLabelNavigationTitle];

//      [self setTitleTextAttributes:@{ UITextAttributeFont : titleFont, UITextAttributeTextColor : titleColor }];

        CGAffineTransform translate = CGAffineTransformMakeTranslation(0, -CustomNavigationBarHeightDelta / 2.0);
        self.transform = translate;
        [self resetBackgroundImageFrame];

    }
    return self;
}

- (void)resetBackgroundImageFrame
{
    for (UIView *view in self.subviews) {
        if ([NSStringFromClass([view class]) rangeOfString:@"BarBackground"].length != 0) {
            view.frame = CGRectMake(0, CustomNavigationBarHeightDelta / 2.0, self.bounds.size.width, self.bounds.size.height);
        }
    }
}

- (void)setBackgroundImage:(UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics
{
    [super setBackgroundImage:backgroundImage forBarMetrics:barMetrics];
    [self resetBackgroundImageFrame];
}

- (CGSize)sizeThatFits:(CGSize)size
{
    size.width = self.frame.size.width;
    size.height = CustomNavigationBarHeight;
    return size;
}

- (void)setFrame:(CGRect)frame
{
    [super setFrame:frame];
    [self resetBackgroundImageFrame];
}



@end
mackross
  • 2,234
  • 17
  • 19
  • 1
    Nice solution with transform. Thanks! I have 2 alternatives for getting background image view in resetBackgroundImageFrame code: 1, [self.subview objectAtIndex:0] - we can quite safely assume that backgound view will be the at the bottom of the stack - it's a backgound, right? 2, use KVO [self valueForKey:@"backgroundView"], this is approach I decided to take. – Michal Dec 13 '12 at 08:28
  • 1
    how do I apply this subclass? – jturolla Jan 03 '13 at 04:25
  • UINavigationController's initWithNavigationBarClass... method – mackross Jan 04 '13 at 00:14
  • If you're loading your UINavigationBar/UINavigationController from a xib/storyboard, you'll need to override `- (id)initWithCoder:(NSCoder *)aDecoder` instead. – Kyle Fleming Jan 22 '13 at 23:48
  • This is a lovely solution. I simply plonked this into my custom class and ran my app; works really nicely (iOS 6, not tested on iOS 5 yet). – Mackey18 Apr 23 '13 at 18:42
  • Nice solution. The only problem I am encountering is that the translation is applied to the background as well. Any ideas for that behaviour..? – konrad.bajtyngier May 27 '13 at 22:39
  • I've not come across this. What iOS version is this happening on? – mackross Jun 09 '13 at 01:39
  • If you're loading from a xib or storyboard, select the navigation bar in the navigation controller and set the subclass there. – codepoet Aug 29 '14 at 18:49
  • @mackross, then... how do I set rootViewController with navigation controller? I guess, I can't set both navigation bar class and root view controller together. – Hemang Jul 04 '15 at 07:02
7

For swift

create a subclass of Uinavigation bar.

import UIKit

class higherNavBar: UINavigationBar {

override func sizeThatFits(size: CGSize) -> CGSize {
    var newSize:CGSize = CGSizeMake(self.frame.size.width, 87)
    return newSize
}

There will be two blank strips on both sides, I changed the width to the exact number to make it work.

However the title and back button are aligned to the bottom.

rastasheep
  • 10,416
  • 3
  • 27
  • 37
Chen Lu
  • 71
  • 1
  • 4
6

It's not necessary to subclass the UINavigationBar. In Objective-C you can use a category and in Swift you can use an extension.

extension UINavigationBar {
    public override func sizeThatFits(size: CGSize) -> CGSize {
        return CGSize(width: frame.width, height: 70)
    }
}
Douglas Ferreira
  • 2,279
  • 2
  • 27
  • 42
4

I have found the following code to perform better on iPad (and iPhone):

- (CGSize)sizeThatFits:(CGSize)size
{
     return CGSizeMake(self.superview.bounds.size.width, 62.0f);
}
Foti Dim
  • 1,303
  • 13
  • 19
1

If you want to use a custom height for your nav bar, I think you should probably, at the very least, use a custom nav bar (not one in your nav controller). Hide the navController's bar and add your own. Then you can set its height to be whatever you want.

Ben Gottlieb
  • 85,404
  • 22
  • 176
  • 172
  • Setting the height wasn't the problem, more that the other things, in this case the content (tableview) doesn't seem to adjust its position when the navbar changes its size. How's adding my custom navbar going to affect the content? – quano Jan 25 '10 at 16:06
  • the view hierarchy of a NavController-with-navBar is a bit complex. There's a parent view, a navBar, and then content stored in your view controller's view. If you resize the nav bar, it doesn't necessarily resize your controller's view. If YOU resize stuff, you can better control what gets re-flowed to fit. – Ben Gottlieb Jan 25 '10 at 16:48
  • 1
    I've also found that resizing the nav bar does not work in conjunction with UITableViews, UITabBarControllers, etc. It seems too much is hard-coded by apple. (I tried setting the height of the tabBar in the UINavigationControllerDelegate). – benvolioT Jan 27 '11 at 04:28
  • @Ben Gottlieb @benvolioT So, in conclusion, there is now way to change the height of the navigationBar ? – aneuryzm Jun 19 '11 at 08:49
  • 1
    Any updates on this question ? I have the exact same problem. Or, the custom design for the navigation bar must be 44px ? – Ryan Ye Jun 26 '11 at 04:15
1

I was able to use the following subclass code in Swift. It uses the existing height as a starting point and adds to it.

Unlike the other solutions on this page, it seems to still resize correctly when switching between landscape and portrait orientation.

class TallBar: UINavigationBar {
    override func sizeThatFits(size: CGSize) -> CGSize {
        var size = super.sizeThatFits(size)
        size.height += 20
        return size
    }
}
  • Only problem is that the title and bar buttons move down - all the new space is at the top. – David H Dec 10 '15 at 15:52
  • 1
    This is true, but in my case, this was actually exactly what I was trying to do. I put a small segmented control above the navigation bar with the buttons themselves appearing below. But good catch - maybe not the best solution if someone was trying to simply add space below the existing navigation bar area. – Matthew Wakefield Dec 10 '15 at 17:28
  • I just now got the @mackross solution below working in iOS9 (not using images, but using the same overrides). I'm having to create a taller nav bar with the buttons and title in the normal position. I spent most of the morning doing it (PITA). Anyway, what you have is a necessary component of the solution! – David H Dec 10 '15 at 17:56
  • 2
    Fair warning - this solution will no longer work in iOS 11. – Matthew Wakefield Sep 01 '17 at 18:07
1

Here's a pretty nice subclass in Swift that you can configure in Storyboard. It's based on the work done by mackross, which is great, but it was pre-iOS7 and will result in your nav bar not extending under the status bar.

class UINaviationBarCustomHeight: UINavigationBar {

// Note: this must be set before the navigation controller is drawn (before sizeThatFits is called),
// so set in IB or viewDidLoad of the navigation controller
@IBInspectable var barHeight: CGFloat = -1
@IBInspectable var barHeightPad: CGFloat = -1

override func sizeThatFits(size: CGSize) -> CGSize {
    var customSize = super.sizeThatFits(size)
    let stockHeight = customSize.height
    if (UIDevice().userInterfaceIdiom == .Pad && barHeightPad > 0) {
        customSize.height = barHeightPad
    }
    else if (barHeight > 0) {
        customSize.height = barHeight
    }
    // re-center everything
    transform = CGAffineTransformMakeTranslation(0, (stockHeight - customSize.height) / 2)
    resetBackgroundImageFrame()
    return customSize
}

override func setBackgroundImage(backgroundImage: UIImage?, forBarPosition barPosition: UIBarPosition, barMetrics: UIBarMetrics) {
    super.setBackgroundImage(backgroundImage, forBarPosition: barPosition, barMetrics: barMetrics)
    resetBackgroundImageFrame()
}

private func resetBackgroundImageFrame() {
    if let bg = valueForKey("backgroundView") as? UIView {
        var frame = bg.frame
        frame.origin.y = -transform.ty
        if (barPosition == .TopAttached) {
            frame.origin.y -= UIApplication.sharedApplication().statusBarFrame.height
        }
        bg.frame = frame
    }
}
}
Code Baller
  • 71
  • 2
  • 4
0

I am a newbie in ios yet. I solved the problem in following way :

  1. I have created a new class that inherits from UINavigationBar

  2. I override the following method :

     (void)setBounds:(CGRect)bounds {
    
       [super setBounds:bounds];
       self.frame = CGRectMake(0, 0, 320, 54);
    
    
      }
    

3.To get a custom background of the navigation bar, I overrided the following method :

-(void)drawRect:(CGRect)rect {

    [super drawRect:rect];

    UIImage *img = [UIImage imageNamed:@"header.png"];

    [img drawInRect:CGRectMake(0,0, self.frame.size.width, self.frame.size.height)];


}
  1. In xib file, I have changed the default UINavigationBar class of the navigation bar to my class.
sudip
  • 2,781
  • 1
  • 29
  • 41
  • This is very close, however it will overlap the underlying view instead of shifting it to line up with the navbar. – rwyland Mar 08 '13 at 00:00