3

I have a simple layout where I have a UINavigationViewController and a regular view controller displaying inside of it. I then have an image of fixed size, and some text that is supposed to fill the remaining space. See below:

simple view layout with a navigation controller, an image and some text

The top of the image in the foreground is constrained to the top layout guide, which works fine in Xcode (doesn't show any warnings or anything), but at run time seems to be ignored:

ios simulator showing incorrect layout

The top of the image is anchored to the top of the screen instead of the bottom of the navigation bar. I understand that this is not necessarily incorrect (table views scrolling up below the navigation bar), but this is super annoying because there's no scrolling involved here.

But wait, there's more. If I rotate the screen, it fixes itself!

ios simulator showing fixed layout

I tried stuff like adding a call to layoutIfNeeded in different places, to no avail.

What am I doing wrong?

Side notes:

  • I tested it on device, and it behaves identically, so I only screenshotted the simulator
  • Ignore the background image view, it is not relevant
  • I don't know why the text displays cut off in Xcode, but it seems to work fine at runtime so I don't care

Per dasdom's comment below, I have tried adding this constraint in code, with no apparent change. I'm not 100% certain that I got it right, though:

-(void)viewDidLayoutSubviews {
    NSMutableDictionary * views = [NSMutableDictionary new];
    views[@"v"] = self.imageView;
    views[@"topLayoutGuide"] = self.topLayoutGuide;
    NSMutableArray * constraints = [NSMutableArray new];

    [constraints addObjectsFromArray:[NSLayoutConstraint
                                 constraintsWithVisualFormat:@"V:[topLayoutGuide][v]"
                                 options:0
                                 metrics:nil
                                 views:views]
    ];
    [self.view addConstraints:constraints];
    [self.view layoutSubviews];
}

A few debugging outputs:

If I print out the navigation controller during viewWillAppear, I get this:

2014-05-21 14:45:51.961 iBeaconMap[914:60b] self.navigationController.navigationBar: <UINavigationBar: 0x8cbded0; frame = (0 20; 320 44); opaque = NO; autoresize = W; userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x8c72440>; layer = <CALayer: 0x8cbe010>>

If I set a timer to print out the value of topLayoutGuide at regular intervals, it says "20" until the first time I rotate, at which point it says "52" or "64" depending on orientation (as expected).

Maybe I need to figure out what the rotation is doing to the navigation controller, and simulate that....... is grasping at straws

jscs
  • 63,694
  • 13
  • 151
  • 195
Mike Caron
  • 14,351
  • 4
  • 49
  • 77
  • Stuff like that let me believe that the Interface Builder isn't good enough to do Auto Layout. Try to add the constraint to the top layout guide in `viewWillAppear:` in the view controller. – dasdom May 21 '14 at 17:48
  • I just tried that, but it did not appear to change anything. I have updated the question with the code I used. I implemented it in `viewDidLayoutSubviews`, since that seems to be what apple recommends. Still, no dice. – Mike Caron May 21 '14 at 18:16
  • Is the navigation bar there already? Try to print out the top layout guide in the debugger. – dasdom May 21 '14 at 18:34
  • I'm not sure whether it's "there" or not, but `self.topLayoutGuide.length` prints out `20` (the status bar, presumably). If I go crazy and ask for `self.navigationController.topLayoutGuide.length`, it returns `0`. This might explain my the local topLayoutGuide is incorrect, although it doesn't explain why... – Mike Caron May 21 '14 at 18:38
  • Do me a favor and add it for a test in viewWillAppear. And add the following Log: `NSLog(@"self.navigationController.navigationBar: %@", self.navigationController.navigationBar);` – dasdom May 21 '14 at 18:40
  • Done. Also added confirmation that the guide is correcting itself on rotation. – Mike Caron May 21 '14 at 18:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/54143/discussion-between-mike-caron-and-dasdom). – Mike Caron May 21 '14 at 18:53
  • If you put this (or a stripped version) on github I'll be glad to wade in and try to help. – matt May 21 '14 at 19:12
  • I wonder what is up with the downvotes on both the question AND answer, a year after the fact? – Mike Caron Jun 12 '15 at 13:39
  • Also don't understand the down votes, I'm experiencing the same issues. I noticed the issue is not present when I set navigationBar.translucent = NO; – rantunes Jun 22 '15 at 01:03

1 Answers1

-1

After implementing the solution in Navigation controller top layout guide not honored with custom transition, the problem is fixed.

The problem is actually caused by a custom transition that I didn't even think to mention (because why would that be the problem??). It seems that the topLayoutGuide property is unreliable in the face of custom transitions. Good to know, I guess.

Community
  • 1
  • 1
Mike Caron
  • 14,351
  • 4
  • 49
  • 77