9

In my application delegate's didFinishLaunchingWithOptions function, I try to customize the appearance of my navigation bar.

[UINavigationBar appearance].translucent = NO;
[[UINavigationBar appearance] 
    setBackgroundImage:[UIImage 
        imageWithColor:[UIColor whiteColor] 
        size:CGSizeMake(1.0f, 1.0f)
    ] 
    forBarMetrics:UIBarMetricsDefault
];
[UINavigationBar appearance].shadowImage = [UIImage 
    imageWithColor:[UIColor redColor] 
    size:CGSizeMake(0.5f, 0.5f)
];

I expect a 1px tall opaque red shadow. Instead I am given a 2px tall translucent red shadow. How can make it appear exactly as I want it to? I've done the analogous appearance settings to UITabBar. It, on the other hand, behaves nicely.

enter image description here

The category function that creates dynamic images is defined as so:

+ (UIImage*)imageWithColor:(UIColor *)color size:(CGSize)size
{
    CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
Pwner
  • 3,714
  • 6
  • 41
  • 67
  • have you looked into just adding a shadow to the CALayer? see http://stackoverflow.com/questions/805872/how-do-i-draw-a-shadow-under-a-uiview – cmyr Dec 02 '14 at 20:23
  • @cmyr `[UINavigationBar appearance].layer.borderWidth = 0.5f; [UINavigationBar appearance].layer.borderColor = [UIColor redColor].CGColor;` shows nothing under the navigation bar. – Pwner Dec 02 '14 at 21:55
  • have you set the layer's clipsToBounds to NO? – cmyr Dec 02 '14 at 23:21

2 Answers2

17

You need to create a graphics context that is retina-aware :

let pixelScale = UIGraphicsBeginImageContextWithOptions(fillRect.size, false, UIScreen.main.scale)

After that, your shadow image will become 1 physical pixel tall.

Here's full code:

extension UIImage {

    static func imageWithColor(color: UIColor) -> UIImage {
        let pixelScale = UIScreen.main.scale
        let pixelSize = 1 / pixelScale
        let fillSize = CGSize(width: pixelSize, height: pixelSize)
        let fillRect = CGRect(origin: CGPoint.zero, size: fillSize)
        UIGraphicsBeginImageContextWithOptions(fillRect.size, false, pixelScale)
        let graphicsContext = UIGraphicsGetCurrentContext()
        CGContextSetFillColorWithColor(graphicsContext, color.CGColor)
        CGContextFillRect(graphicsContext, fillRect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }

}

On GitHub:

https://github.com/salutis/swift-image-with-color

Jakub Truhlář
  • 20,070
  • 9
  • 74
  • 84
Rudolf Adamkovič
  • 31,030
  • 13
  • 103
  • 118
  • I'd propose making the fill size {1, pixelSize} instead of {pixelSize, pixelSize}, otherwise it creates a horizontal gradient effect and the line becomes transparent at the rightpoint point in my case. – Can Poyrazoğlu May 29 '17 at 12:04
-2

Uh...is this what you want?

screenshot

In my view controller, I simply added a UIView with 1 pixel height and an offset of 44pixels:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.view.backgroundColor = [UIColor whiteColor];

    [self setupNavBar];
}

-(void)setupNavBar
{
    self.navigationItem.title = @"Contacts";

    CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];

    UIView *bar = [[UIView alloc] initWithFrame:CGRectMake(0, 44, applicationFrame.size.width, 1.0)];
    bar.backgroundColor = [UIColor redColor];

    [self.navigationController.navigationBar addSubview:bar];
}

You can change the color to whatever you want.

Zhang
  • 11,549
  • 7
  • 57
  • 87
  • 3
    IMHO I think it's not the best solution to add a subview to a navigationBar. It seems error prone (e.g. when multiple view controllers try to add views or - esp - when the view hierarchy for navigation bars might change with an updated version of iOS). I think it's much safer to use publicly available functionalities, a custom background image and UIAppearance settings instead. – auco Jun 25 '15 at 15:33