關於自動佈局的基本使用,參考網上的文章便可,如: iOS開發-自動佈局篇:史上最牛的自動佈局教學!bash
相信許多比較少使用自動佈局的同窗對下面的參數都感受比較頭疼:佈局
其實不難,請往下看: #####Content Hugging Priority : 抗拉伸優先級 最多見的狀況是兩個label並排放置,並設置水平間距:動畫
報錯了?莫慌,當兩個label的文字長度加上水平間距不足以填滿父視圖時,須要設置抗拉伸優先級。 解決辦法:假設咱們不但願Label1被拉伸,則將其Hugging Priority值調大(默認爲251)ui
調整後,修改label1的文字,其長度隨之變化:atom
而後咱們在調整的過程當中會發現:spa
又報錯了? 接着往下看: #####Content Compression Resistance Priority : 抗壓縮優先級 當兩個label的文字長度加上水平間距超出了父視圖寬度時,須要設置抗壓縮優先級。 解決辦法:假設咱們但願Label1的內容儘量地展現徹底,則將其Resistance Priority值調大(默認爲750)3d
調整後:code
能夠看到,Label2直接被壓沒了。 咱們把Label2的Resistance Priority值調大:cdn
調整後:blog
通過上面的過程後,對於如何使用自動佈局作出下圖的效果,是否是就內心有數了呢:
PS: 抗壓縮、抗拉伸優先級一樣適用於與父視圖的約束優先級(默認值1000) 熟悉了xib中的優先級設置後,在Masonry中對應優先級的思路相同,使用方法
- (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis;
進行對應設置(系統自帶方法,不是Masonry加的)。
某些用來展示內容的用戶控件,例如文本控件UILabel、按鈕UIButton、圖片視圖UIImageView等,它們具備自身內容尺寸(對於UIImageView,其自身內容尺寸就是圖片(1倍圖)的尺寸)。
咱們能夠經過重寫- (CGSize)intrinsicContentSize
方法來指定內容尺寸。 先來看看UIView默認的返回值:
- (CGSize)intrinsicContentSize{
return CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
}
複製代碼
這個方法的做用是當使用已有約束不可以計算出內容寬度/高度時,自動爲視圖添加寬度/高度約束,其值爲返回值中對應的寬/高。 所以咱們能夠在這個方法中返回計算好的內容大小。
咱們在xib中也能夠看到下面的參數(須要注意的是,在這裏設置的size僅僅只用於xib中展現效果,實際運行時並無什麼卵用):
知道了intrinsicContentSize的用法後,咱們考慮一下下面的需求:
寫一個自定義的View,知足下面的功能:
- 若是未使用約束,直接顯示frame的大小便可;
- 若是添加了可以定位左上角點座標的約束,則使用默認的內容大小。
- 若是添加了可以定位左上角點座標以及可以計算出寬/高其中一個數值時,使用寬/高的另外一個默認值。
- 若是添加了可以計算出所有frame值的約束,則不使用默認內容大小。
總體效果相似UILabel,可是這裏簡化成固定的默認尺寸。
這裏給出一個簡單的demo僅供參考:
@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;
}
return self;
}
- (void)layoutSubviews{
[super layoutSubviews];
// 是否使用了自動佈局
if (self.constraints.count > 0) {
// 判斷是否添加了能夠計算出寬度/高度的約束
if (self.frame.size.width != _widthConstrant) {
_widthConstraintAdded = YES;
}
if (self.frame.size.height != _heightConstrant) {
_heightConstraintAdded = YES;
}
// 計算缺乏的寬/高
// 計算時可使用self.frame.size,這個size是自動佈局調整後的size了
if (!_widthConstraintAdded) {
// 計算寬度的代碼...
_widthConstrant = 10;
}
if (!_heightConstraintAdded) {
// 計算高度的代碼...
_heightConstrant = 10;
}
// 添加約束
if (!CGSizeEqualToSize(self.frame.size, [self intrinsicContentSize])) {
[self invalidateIntrinsicContentSize];
}
}
}
- (CGSize)intrinsicContentSize{
return CGSizeMake(_widthConstrant, _heightConstrant);
}
- (CGSize)defaultSize{
if (CGSizeEqualToSize(_defaultSize, CGSizeZero)) {
// 計算默認內容寬高的代碼
float width = 100;
float height = 20;
_defaultSize = CGSizeMake(width, height);
}
return _defaultSize;
}
@end
複製代碼
咱們看下面的佈局:
當咱們須要Label2能夠顯示多行數據時,僅僅設置右邊距是不夠的:
咱們可讓Label2的寬度等於Scrollview的寬度減去左右邊距之和:
看看效果:
效果出來了,可是僅僅這樣咱們發現不能上下滾動,由於沒有設置底部間距,scrollview沒法計算出內容高度,須要給最下面的view(Label2)添加底部間距:
看看效果:
下面考慮更進一步的需求:Label1也要能夠顯示多行時的狀況。 咱們把Label1右邊與Label2對齊,而後改一下內容文字:
OK大功告成。
PS:若是使用Tablview時用到Cell的自動高度(不論是用系統自帶仍是UITableView+FDTemplateLayoutCell),也都須要設置好約束,讓它可以計算出豎直方向的內容高度(水平方向tableview不須要,collectionView須要),具體過程和上面相似。
咱們看一個樣式:
要實現這同樣式的方式有不少種,這裏咱們考慮用UILabel的子類直接實現的狀況(儘量少的View):
UIView中有這一個類方法:+ (Class)layerClass;
,該方法返回的就是view.layer的類型,所以咱們可使用自定義的Layer替換掉默認的Layer,而後裁剪出對應的形狀(原理比較簡單,直接上代碼了):
@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
複製代碼
對於須要進行frame動態調整的界面,許多小夥伴都不知道該如何使用自動佈局進行動畫而放棄,其實不難:
若是使用xib或者代碼建立,最簡單的方式就是將約束設置成對應屬性,直接修改約束的constant
便可。
若是使用Masonry建立佈局,那麼只須要使用下面的代碼就可使用動畫效果了:
// 若是其約束尚未生成的時候須要動畫的話,就請先強制刷新後才寫動畫,不然全部沒生成的約束會直接跑動畫
[view.superview layoutIfNeeded];
[UIView animateWithDuration:3 animations:^{
[view mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(123);
}];
}];
// 強制繪製
[view.superview layoutIfNeeded];
複製代碼
記得動畫先後須要刷新。