UIViewController viewDidLoad incorrect width / height

Everyone knows that you cannot trust the size of a frame in the init / viewDidLoad method of the UIViewController; this is:

- (void)viewDidLoad: { NSLog(@"%d", self.view.frame.size.width); } 

will print the wrong sizes in many cases (in particular, it is badly broken in landscape mode)

This will return always corrected results, so it's nice to post subview layouts:

 - (void)viewWillAppear: { NSLog(@"%d", self.view.frame.size.width); } 

The problem is that viewWillAppears is called every time the view appears, so it is not suitable for posting or adding subviews. This way you end up declaring each view in the interface, and you end up with huge header files that I don’t like at all, since most elements no longer need to be manipulated after the initial setup.

So, the first question: Is there a better way to handle positioning routines?

Question two is very related, let's say I have a subclass of UIView, including various other subclauses. I declare it inside my interface, and I select / initialize it in the init / viewDidLoad method.

 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { ... menu = [[SNKSlidingMenu alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); ... } 

As we already know, now we need to move it to viewWillAppear to get a more accurate reading.

 - (void)viewWillAppear:(BOOL)animated{ .... menu.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); .... } 

The problem is that, of course, all sub-views also need to be moved. This is done using the layoutSubviews function, which is called automatically, but we have the same problem: All subqueries must be declared inside the interface of the SNKSlidingMenu class. Is there any way around this?

Thanks.

+10
source share
4 answers

If you configure iOS 5.0 or better, you can use viewWillLayoutSubviews and viewDidLayoutSubviews to make changes.

As for your second question, if you need access to the instance variable in a method other than init , you need to save it, I don't see a problem with it.

However, you can use automatic layouts and configure rules between subzones so that they are automatically laid out for you without the need to save a link.

+10
source

viewDidLoad is only called when you create your view, but many things can affect the size of the frame , and it will not be called again when the frame changes.

Instead

  • create routines in viewDidLoad
  • set their sizes in viewWillLayoutSubviews .

See additional information for rotation processing: https://stackoverflow.com/a/166198/

+6
source

viewWillLayoutSubviews and viewDidLayoutSubviews can solve this problem.
But two methed will be executed more times. this is my code to get the correct self.view.frame .

 - (void)viewDidLoad { [super viewDidLoad]; ... dispatch_async(dispatch_get_main_queue(), ^{ // init you view and set it`s frame. this can get correct frame. ... } ... } 
+3
source

It saved my life more than once:

 override func viewDidLoad() { super.viewDidLoad() self.view.setNeedsLayout() self.view.layoutIfNeeded() } 

This basically forces the viewcontroller to correctly position its view, and from there you can get the right frames for all of your subviews. This is especially helpful when animating transitions, and your view controllers use autolayout and an interface builder.

From what I noticed, it looks like the initial frames are set according to what default size class is set in your interface constructor. I usually edit using the iPhone XS size class, so in viewDidLoad it seems that the viewing width is always 375 regardless of whether you use the iPhone XR or not. It fixes itself before viewWillAppear, though.

The above code will fix this problem and allow you to get the correct frames for your view / subview before the ViewController is displayed on the screen.

0
source

All Articles