Automatic layout basics

About the basic use of automatic layout, refer to the online article can, such as: iOS development – automatic layout: the history of the most cattle automatic layout teaching!

Automatic layout advanced text

Resistance to tension and compression

I believe that many students who seldom use automatic layout feel the following parameters are a headache:

#####Content Hugging Priority The most common situation for both labels to be placed side by side, with horizontal spacing:

An error? Do not panic, when the length of the text of the two labels plus the horizontal spacing is not enough to fill the superview, you need to set the tensile priority. Solution: Assuming that we don’t want Label1 to be stretched, make its Size bigger (default: 251)

After adjustment, modify the text of Label1 and its length will change accordingly:

Then we find in the process of adjustment:

Error again? #####Content Compression Resistance Priority When the text length plus horizontal spacing of two labels exceeds the width of the parent view, you need to set the Compression Resistance Priority. Workaround: Assuming we want Label1’s content to be as complete as possible, increase its Resistance Priority value (default: 750)

After the adjustment:

As you can see, Label2 is just crushed away. We increased the Resistance Priority value of Label2:

After the adjustment:

After the above process, you should have a good idea of how to use automatic layout to make the following effect:

PS: The uncompressed priority and unstretched priority also apply to the constraint priority of the parent view (default value: 1000). After you know the setting of the priority in xib, you will find the corresponding priority in the next navigation. Method of use – (void) setContentCompressionResistancePriority: (UILayoutPriority) priority forAxis (UILayoutConstraintAxis) axis; You can set the navigation Settings for the next task.

Intrinsic Content Size

Some user controls that are used to present content, such as text controls UILabel, buttons UIButton, image views UIImageView, etc., have their own content size (in the case of UIImageView, its own content size is the size of an image).

How do I set the content size of a custom control?

We can specify the content size by overriding the – (CGSize)intrinsicContentSize method. Let’s look at UIView’s default return value:

- (CGSize)intrinsicContentSize{
    return CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
}
Copy the code

This method automatically adds a width/height constraint to the view when the width/height cannot be calculated using existing constraints. The value is the corresponding width/height in the return value. So we can return the calculated content size in this method.

We can also see the following parameters in the XIB (note that the size set here is only used to show the effect in the XIB, it is not used in the actual runtime) :

Now that we know the use of intrinsicContentSize, consider the following requirements:

Write a custom View that satisfies the following functions:

  • If no constraint is used, simply display the size of the frame.
  • If you add a constraint that positions the coordinates of the top-left point, the default content size is used.
  • Use another default value for width/height if you add the ability to locate the coordinates of the top-left point and the ability to calculate either width/height.
  • If you add a constraint that calculates the full frame value, the default content size is not used.

The overall effect is similar to UILabel, but simplified to a fixed default size.

Here is a simple demo for your reference only:

@interface DemoView()

@property (nonatomic,assign) CGSize defaultSize;

@end

@implementation DemoView {
    BOOL _widthConstraintAdded;
    BOOL _heightConstraintAdded;
    float _widthConstrant;
    float _heightConstrant;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if (self) {
        _widthConstraintAdded = _heightConstraintAdded = NO;
        _widthConstrant = self.defaultSize.width;
        _heightConstrant = self.defaultSize.height;
    }
    returnself; } - (void)layoutSubviews{ [super layoutSubviews]; // Whether automatic layout is usedif(self.constraints. Count > 0) {// Determine whether a constraint has been added that can calculate the width/heightif(self.frame.size.width ! = _widthConstrant) { _widthConstraintAdded = YES; }if(self.frame.size.height ! = _heightConstrant) { _heightConstraintAdded = YES; } // Calculate the missing width/height // Use self.frame. Size, which is the size of the automatic layout adjustmentif(! _widthConstraintAdded) {// Calculate width code... _widthConstrant = 10; }if(! _heightConstraintAdded) {// Calculate the height of the code... _heightConstrant = 10; } // Add constraintsif(! CGSizeEqualToSize(self.frame.size, [self intrinsicContentSize])) { [self invalidateIntrinsicContentSize]; } } } - (CGSize)intrinsicContentSize{return CGSizeMake(_widthConstrant, _heightConstrant);
}

- (CGSize)defaultSize{
    if(CGSizeEqualToSize(_defaultSize, CGSizeZero)) {// The code that calculates the width and height of the default contentfloat width = 100;
        float height = 20;
        _defaultSize = CGSizeMake(width, height);
    }
    return _defaultSize;
}
@end
Copy the code

The automatic content size of a ScrollView

Let’s look at the following layout:

When we need Label2 to display multiple rows of data, it is not enough to simply set the right margin:

We can make the width of Label2 equal to the width of the Scrollview minus the sum of the left and right margins:

Look at the results:

Scrollview cannot calculate the height of the content without setting the bottom spacing. We need to add the bottom spacing to the bottom view (Label2) :

Look at the results:

Consider a further requirement: Label1 should also be able to display multiple lines. Let’s align the right side of Label1 with Label2 and change the content text:

OK, done.

If the automatic height of the Tablview Cell is used (whether it is from the system or UITableView+FDTemplateLayoutCell), the constraint must be set. Let it calculate the height of the content in the vertical direction (not for the horizontal TableView, but for the collectionView).

Custom View with automatic layout

Let’s look at a style:

There are a number of ways to implement this style, but here we consider using a UILabel subclass directly (with as few views as possible) :

UIView has this Class method: + (Class)layerClass; This method returns the view.layer type, so we can replace the default layer with a custom layer and crop out the corresponding shape:

@interface DemoLabelLayer : CAShapeLayer

@end

@implementation DemoLabelLayer

- (void)layoutSublayers{
    [super layoutSublayers];
    [self setBackgroundPath];
}

- (void)setBackgroundPath{
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.fillColor = [UIColor whiteColor].CGColor;
    shapeLayer.fillRule = kCAFillRuleEvenOdd;
    shapeLayer.path = [self backgroundPathFrame:self.bounds].CGPath;
    self.mask = shapeLayer;
}

- (UIBezierPath *)backgroundPathFrame:(CGRect)frame{
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:UIRectCornerTopRight|UIRectCornerBottomRight cornerRadii:CGSizeMake(self.cornerRadius, self.cornerRadius)];
    return path;
}

@end

@interface DemoView()

@end

@implementation DemoView

- (void)awakeFromNib{
    [super awakeFromNib];
}

- (void)layoutSubviews{
    [super layoutSubviews];
    DemoLabelLayer *layer = (DemoLabelLayer *)self.layer;
    layer.backgroundColor = self.backgroundColor.CGColor;
    layer.cornerRadius = self.frame.size.height/2.f;
}

+ (Class)layerClass{
    return [DemoLabelLayer class];
}

@end
Copy the code

Automatic layout animation

For the interface that needs dynamic frame adjustment, many friends do not know how to use automatic layout for animation and give up, in fact, it is not difficult:

If you use xiB or code creation, the easiest way to do this is to set the constraint to the corresponding attribute and simply change the constant of the constraint.

If you would like to create a layout for navigation, you would only need to use the following code to animate it:

Superview layoutIfNeeded [view.superview layoutIfNeeded]; [UIView animateWithDuration:3 animations:^{ [view mas_updateConstraints:^(MASConstraintMaker *make) { make.height.mas_equalTo(123); }]; }]; // Force draw [view.superview layoutIfNeeded];Copy the code

Remember to refresh before and after the animation.