Restrictions on updating after resizing an observation frame

I have a very simple UIViewController , which I use to try to better understand the limitations, auto-layout and frames. The view controller has two subheadings: both are UIView that are designed to either sit side by side or top / bottom depending on the orientation of the device. Within each UIView there is a single label that should be concentrated within its supervisor.

When the device is rotated, updating the UIView is performed correctly. I calculate their size and origin. However, the labels do not remain centered, and they do not respect the restrictions defined in the storyboard.

Here are the screenshots to show the problem. If I comment on the viewDidLayoutSubviews method, the labels will be perfectly centered (but then the UIView not the right size). I understand that I can manually adjust the frame for each of the shortcuts, but I am looking for a way to make them respect their limitations in recently changed supervisors. enter image description hereenter image description here Here is the code:

 #import "ViewController.h" @interface ViewController () @property (nonatomic) CGFloat spacer; @end @implementation ViewController @synthesize topLeftView, bottomRightView, topLeftLabel, bottomRightLabel; - (void)viewDidLoad { [super viewDidLoad]; topLeftLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; bottomRightLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.spacer = 8.0f; } - (void)viewDidLayoutSubviews { if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) { [self setupTopLeftForLandscape]; [self setupBottomRightForLandscape]; } else { [self setupTopLeftForPortrait]; [self setupBottomRightForPortrait]; } } - (void) setupTopLeftForPortrait { CGRect frame = topLeftView.frame; frame.origin.x = self.spacer; frame.origin.y = self.spacer; frame.size.width = self.view.frame.size.width - 2*self.spacer; frame.size.height = (self.view.frame.size.height - 3*self.spacer) * 0.5; [topLeftView setFrame:frame]; } - (void) setupBottomRightForPortrait { CGRect frame = bottomRightView.frame; frame.origin.x = self.spacer; frame.origin.y = topLeftView.frame.size.height + 2*self.spacer; frame.size.width = topLeftView.frame.size.width; frame.size.height = topLeftView.frame.size.height; [bottomRightView setFrame:frame]; } - (void) setupTopLeftForLandscape { CGRect frame = topLeftView.frame; frame.origin.x = self.spacer; frame.origin.y = self.spacer; frame.size.width = (self.view.frame.size.width - 3*self.spacer) * 0.5; frame.size.height = self.view.frame.size.height - 2*self.spacer; [topLeftView setFrame:frame]; } - (void) setupBottomRightForLandscape { CGRect frame = bottomRightView.frame; frame.origin.x = self.topLeftView.frame.size.width + 2*self.spacer; frame.origin.y = self.spacer; frame.size.width = topLeftView.frame.size.width; frame.size.height = topLeftView.frame.size.height; [bottomRightView setFrame:frame]; } @end 
+8
ios objective-c autolayout uiview
source share
1 answer

This is usually a bad idea for blending frames with automatic layout. (An exception is a hierarchy of views that uses constraints containing a view that doesnt, which then does not use any constraints from this point down [and additional reservations]). One big problem is that the constraint system usually does not get any information from setFrame.

Another rule of thumb is that setFrame and traditional layout tree are computed before the constraint system. This may seem intuitive with the first part, but remember that 1) in a traditional tree of layouts, views lay out their subitems and then call layoutSubviews on them, so each frame of the supervisor is set before it goes out, but 2) in the restriction system, he is trying to calculate the supervisor frame from subzones, from bottom to top. But after receiving information from bottom to top, each subheading reports information, the layout of the work is performed from top to bottom.


Fixing

Where does it leave you? It is correct that you need to install this programmatically. There is no way in IB to indicate that you should switch from the top level to side by side. Here's how you can do it:

  • Select one of the turns and make sure that all restrictions are configured as you want in the interface builder, for example, each color view imposes 8 points (your spacer) from the supervisor. The “Clear Limitations” and the “Refresh Frames” buttons below will help you and you will want to click this often to make sure it is synchronized.
  • It is very important that the top view on the left is only connected to the supervisor on the left (leading) and the upper sides, and the lower right is only connected by the right (back) and lower sides. If you clear the dimensions that set the height and width, this will result in a warning. This is normal, and in this case it can be solved by setting "equal widths" and "equal heights" and part of step 3, if necessary. (Note that the constant must be zero so that the values ​​are truly equal.) In other cases, we must set the limit and mark it with a “placeholder” to disable the compiler, if we were sure that we would fill in the information, but the compiler does not know this.
  • Define (or create) two constraints that link the right / bottom view of something to the left and top. You can use the object browser to the left of IB. Create two exits in viewController.h using the helper editor. It will look like this:

    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomViewToTopConstraint; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *rightViewToLeftConstraint;

  • Implement updateConstraints in viewController. Heres where the logic will go:

.

 -(void)updateViewConstraints { //first remove the constraints [self.view removeConstraints:@[self.rightViewToLeftConstraint, self.bottomViewToTopConstraint]]; if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) { //align the tops equal self.bottomViewToTopConstraint = [NSLayoutConstraint constraintWithItem:self.bottomRightView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.topLeftView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; //align to the trailing edge by spacer self.rightViewToLeftConstraint = [NSLayoutConstraint constraintWithItem:self.bottomRightView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.topLeftView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:self.spacer]; } else { //portrait //right view atached vertically to the bottom of topLeftView by spacer self.bottomViewToTopConstraint = [NSLayoutConstraint constraintWithItem:self.bottomRightView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.topLeftView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:self.spacer]; //bottom view left edge aligned to left edge of top view self.rightViewToLeftConstraint = [NSLayoutConstraint constraintWithItem:self.bottomRightView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.topLeftView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]; } [self.view addConstraints:@[self.rightViewToLeftConstraint, self.bottomViewToTopConstraint]]; [super updateViewConstraints]; 

}

Since you cannot change the restrictions after theyre added (except for the constant), we must do this delete-add step. Please note that those in IB can also be placeholders, as they deleted them each time (we could check first). We could change the constant to some offset value, for example, related to the supervisor with spacer + topViewHight + spacer. But this means that when the auto layout goes to calculate this view, you made assumptions based on some other information that could change. The exchange of views and the change in what they associate with factors that should change each other.

Please note that since Auto Layout will use the restrictions here when transferring information up, we first modify them, then we call super. This is a call to a private implementation of the superclass to perform calculations for this view, and not to view this view in the view hierarchy, although in fact the next step will be further down the tree.

+10
source share

All Articles