0

I'm trying to create a UIViewController which has a UINavigationBar and a UIImageView. The size of the image I'm putting in the UIImageView is 416 × 901 pixels. I'm trying to create a view such that I have the navigation bar at the top, then the UIImageView pinned to the edges of the screen, and under the toolbar. However, when I run my app, the UIImage seems to extend past the navigation bar.

Here's a screenshot from Xcode:

Layout Analyzer 1 Layout Analyzer 2

As you can see, the UINavigationBar is obscured by the UIImage. When I remove the image, The layout looks as I expect (I comment out the UIImage assignment image.image = [UIImage imageNamed:@"attachment.jpg"]; which you can see in the code I posted below) :

Layout Analyzer 3 Layout Analyzer 4

I'm pretty stumped. I'm guessing it has something to do with a missing parameter in the UIImageView, or I need to create a completely different constraint, but I'm not sure. I've pasted the code for the layout below. I appreciate whatever help I can get.

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

UINavigationBar *navigation = [[UINavigationBar alloc] initWithFrame:CGRectZero];
    navigation.translatesAutoresizingMaskIntoConstraints = NO;
    UINavigationItem* navigationItem = [[UINavigationItem alloc] initWithTitle:@"Edit"];
    UIBarButtonItem* cancelBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)];
    navigationItem.leftBarButtonItem = cancelBtn;

    UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(save:)];
    navigationItem.rightBarButtonItem = doneBtn;

    [navigation setItems:@[navigationItem]];
    navigation.delegate = self;

    UIImageView *image = [[UIImageView alloc] init];
    image.contentMode = UIViewContentModeScaleAspectFit;
    image.backgroundColor = [UIColor redColor];
    image.image = [UIImage imageNamed:@"attachment.jpg"];
    image.translatesAutoresizingMaskIntoConstraints = NO;

    UIView *content = [[UIView alloc] initWithFrame:CGRectZero];
    content.translatesAutoresizingMaskIntoConstraints = NO;
    content.backgroundColor = [UIColor yellowColor];
    [content addSubview:image];
    [content addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[image]-0-|"
                                                                      options:0
                                                                      metrics:nil
                                                                        views:NSDictionaryOfVariableBindings(image)]];
    [content addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[image]-0-|"
                                                                      options:0
                                                                      metrics:nil
                                                                        views:NSDictionaryOfVariableBindings(image)]];


    [self.view addSubview:navigation];
    [self.view addSubview:content];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[navigation]-0-[content]-0-|"
                                                                      options:0
                                                                      metrics:nil
                                                                        views:NSDictionaryOfVariableBindings(navigation, content)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[navigation]-0-|"
                                                                      options:0
                                                                      metrics:nil
                                                                        views:NSDictionaryOfVariableBindings(navigation, content)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[content]-0-|"
                                                                      options:0
                                                                      metrics:nil
                                                                        views:NSDictionaryOfVariableBindings(navigation, content)]];
}


- (UIBarPosition)positionForBar:(id <UIBarPositioning>)bar {
        return UIBarPositionTopAttached;
}

- (void)cancel:(id)button {}
- (void)save:(id)button {}

@end

I just noticed that there are some runtime warnings in Xcode with respect to the layout. Here's what Xcode shows me:

Xcode warnings

I guess the navigation bar's height is ambiguous because the height of the UIImageView is not well defined. However in this case, I'm trying to pin the height of the image expecting the height of the navigation to be well defined. Basically, I'm banking on the navigation to define how much space I need for the UIImageView. Maybe I need to fix the height of one of the two elements?

2 Answers2

0

Adding your own navigation bar is a little quirky, as it needs to resize when the device is rotated.

Try it like this (I don't like VFL - seems to have its own issues sometimes):

@interface ViewController ()
@property (strong, nonatomic) NSLayoutConstraint *navBarHeightCon;
@end

@implementation ViewController

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    _navBarHeightCon.constant = (self.view.bounds.size.height > self.view.bounds.size.width)? 44 : 32;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    UINavigationBar *navigation = [[UINavigationBar alloc] initWithFrame:CGRectZero];
    navigation.translatesAutoresizingMaskIntoConstraints = NO;
    UINavigationItem* navigationItem = [[UINavigationItem alloc] initWithTitle:@"Edit"];
    UIBarButtonItem* cancelBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)];
    navigationItem.leftBarButtonItem = cancelBtn;

    UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(save:)];
    navigationItem.rightBarButtonItem = doneBtn;

    [navigation setItems:@[navigationItem]];
    navigation.delegate = self;

    UIImageView *image = [[UIImageView alloc] init];
    image.contentMode = UIViewContentModeScaleAspectFit;
    image.backgroundColor = [UIColor redColor];

    image.image = [UIImage imageNamed:@"attachment.jpg"];
    image.translatesAutoresizingMaskIntoConstraints = NO;

    UIView *content = [[UIView alloc] initWithFrame:CGRectZero];
    content.translatesAutoresizingMaskIntoConstraints = NO;
    content.backgroundColor = [UIColor yellowColor];

    [content addSubview:image];

    [self.view addSubview:navigation];
    [self.view addSubview:content];

    UILayoutGuide *g = self.view.safeAreaLayoutGuide;

    _navBarHeightCon = [navigation.heightAnchor constraintEqualToConstant:44.0];

    [NSLayoutConstraint activateConstraints:@[

        // constrain content view top to navigation bar bottom
        [content.topAnchor constraintEqualToAnchor:navigation.bottomAnchor],

        // constrain content view bottom / leading / trailing to safe area
        [content.bottomAnchor constraintEqualToAnchor:g.bottomAnchor],
        [content.leadingAnchor constraintEqualToAnchor:g.leadingAnchor],
        [content.trailingAnchor constraintEqualToAnchor:g.trailingAnchor],

        // constrain image top / bottom / leading / trailing to content view
        [image.topAnchor constraintEqualToAnchor:content.topAnchor],
        [image.bottomAnchor constraintEqualToAnchor:content.bottomAnchor],
        [image.leadingAnchor constraintEqualToAnchor:content.leadingAnchor],
        [image.trailingAnchor constraintEqualToAnchor:content.trailingAnchor],

        // constrain navigation bar top / leading / trailing to safe area
        [navigation.topAnchor constraintEqualToAnchor:g.topAnchor],
        [navigation.leadingAnchor constraintEqualToAnchor:g.leadingAnchor],
        [navigation.trailingAnchor constraintEqualToAnchor:g.trailingAnchor],

        // height constraint will be set in viewWillLayoutSubviews
        _navBarHeightCon,

    ]];

}

- (UIBarPosition)positionForBar:(id <UIBarPositioning>)bar {
    return UIBarPositionTopAttached;
}

- (void)cancel:(id)button {}
- (void)save:(id)button {}

@end
DonMag
  • 69,424
  • 5
  • 50
  • 86
0

After (too) much investigation, the solution posted here worked for me.

Specifically

Solution is to increase the Hugging Priority of SuperView to High(750 or more) and decrease the Compression Resistance Priority of UIImageView to Low(250 or less). This will let constraints override the image Intrinsic Size.