Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ios10: viewDidLoad frame width/height not initialized correctly

Since upgrading to XCode8 GM and ios10, all of my views created via Interface Builder are not being initialized correctly until much much later than expected. This means in viewDidLoad, cellForRowAtIndexPath, viewWillAppear, etc, the frame size is set to {1000,1000} for every view. At some point they seem to correct, but its far too late.

The first problem encountered is with common rounding of corners failing across the board:

view.layer.cornerRadius = view.frame.size.width/2

Further problems are showing for anything that relies on frame size to do calculations in the code.

cellForRowAtIndexPath 

For cellForRowAtIndexPath, frame size fails on initial table display, but then works fine once you scroll it. willDisplayCell:forRowAtIndexPath does not have the correct frame size either.

I've hardcoded a few values but obviously this is very bad code practice, as well as quite numerous in my projects.

Is there a way or place to get correct frame sizes?

EDIT

I've discovered that using the height/width constraint instead of frame width height is more reliable. This may add the overhead of needing lot of new IBOutlets to link the height/width constraints on items though.

For now I've created a UIView category that lets me access a View's height/width constraints directly without the IBOutlets. For minimal use the small loop shouldn't be a big deal. Results not guaranteed for IB items without the width/height constraints created yet obviously. Probably returns 0 at best for the constant, or worse. Also, if you don't have a height/width constraint and your view is sized dynamically based on leading/trailing constraints, this won't work.

-viewDidLoad appears to have correct frame size, but will often result in a visual change to the UI if you do modifications here.

UIView+WidthHeightConstraints.h

@interface UIView (WidthHeightConstraints)

-(NSLayoutConstraint*)widthConstraint;
-(NSLayoutConstraint*)heightConstraint;
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute;

@end

UIView+WidthHeightConstraints.m

#import "UIView+WidthHeightConstraints.h"

@implementation UIView (WidthHeightConstraints)

-(NSLayoutConstraint*)widthConstraint{
    return [self constraintForAttribute:NSLayoutAttributeWidth];
}
-(NSLayoutConstraint*)heightConstraint {
    return [self constraintForAttribute:NSLayoutAttributeHeight];
}
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute {
    NSLayoutConstraint *targetConstraint = nil;
    for (NSLayoutConstraint *constraint in self.constraints) {
        if (constraint.firstAttribute == attribute) {
            targetConstraint = constraint;
            break;
        }
    }
    return targetConstraint;
}

@end

EDIT 2

The category above has proven only partially effective. Mainly because ios appears to auto add a couple extra height/width constraint duplicates, that are of type NSContentSizeLayoutConstraint, which are actually not the same size as the normal constraint. The NSContentSizeLayoutConstraint is also a private class so I can't do isKindOfClass to filter those out. I haven't found another way to effectively test for those yet. This is annoying.

like image 329
Miro Avatar asked Sep 13 '16 17:09

Miro


3 Answers

The most common issues you describe are appearing in iOS 10 only and can be solved by adding this line (if necessary):

self.view.layoutIfNeeded()

just above the code, that is responsible for changing constraint, layer.cornerRadius etc.

OR

place your code related to frames / layers into viewDidLayoutSubviews() method:

override func viewDidLayoutSubviews() {

    super.viewDidLayoutSubviews()
    view.layer.cornerRadius = self.myView.frame.size.width/2
    view.clipsToBounds = true

    ... etc
}
like image 140
pedrouan Avatar answered Oct 23 '22 23:10

pedrouan


We created a radar (28342777 (marked as duplicate for 28221021 but Open)) for the similar problem and the reply that we got was as below:

"Thank you for reporting the issue. Could we get more information about the profile image view? In Xcode 8, a fully constraint, non-misplaced view no longer saves out a frame to minimize diffs and support automatically update frames in IB. At runtime, these views get decoded with a placeholder size of 1000x1000, but are resolved after first layout. Could the image be assigned before initial layout, and would assigning the image to the image view after first layout address this case? Please send a sample to help us further analyze. thanks!"

At present we have provided them the sample project. My observations:

  • The problem that we had used to happen for XIBs that are converted from Xcode 7.x to Xcode 8.x
  • If we intentionally break the constraint in XIB then viewDidLoad will get expected height and width and not 1000x1000.
  • For us it was a UIImageView on which we were apply some layering for making it circular and using masksToBounds. If we set masksToBounds = NO then we everything was working fine.

Though Apple claims that it is going to be a standard from Xcode 8 that views will be set to 1000x1000, the behavior doesn't seem to be consistent.

Hope this helps.

like image 26
Bhavik Bhagat Avatar answered Oct 23 '22 21:10

Bhavik Bhagat


I encountered the same issue and try to solve it without luck by referring above suggestions.

Seems it should be a bug for Apple to solve. I finally find a solution by changing to save my XIB document back to Xcode 7.x format and my UI back to normal.

Until Apple releasing a fix, I don't want to spend my time on hacking it.

enter image description here enter image description here

like image 39
allen Avatar answered Oct 23 '22 22:10

allen