【OC梳理】自動布局

自動布局基礎(chǔ)篇

關(guān)于自動布局的基本使用,參考網(wǎng)上的文章即可,如:
iOS開發(fā)-自動布局篇:史上最牛的自動布局教學(xué)!

自動布局進階篇

抗拉伸與抗壓縮

相信許多比較少使用自動布局的同學(xué)對下面的參數(shù)都感覺比較頭疼:

其實不難,請往下看:

Content Hugging Priority : 抗拉伸優(yōu)先級

最常見的情況是兩個label并排放置,并設(shè)置水平間距:

報錯了?莫慌,當(dāng)兩個label的文字長度加上水平間距不足以填滿父視圖時,需要設(shè)置抗拉伸優(yōu)先級。
解決辦法:假設(shè)我們不希望Label1被拉伸,則將其Hugging Priority值調(diào)大(默認(rèn)為251)

調(diào)整后,修改label1的文字,其長度隨之變化:

然后我們在調(diào)整的過程中會發(fā)現(xiàn):

又報錯了? 接著往下看:

Content Compression Resistance Priority : 抗壓縮優(yōu)先級

當(dāng)兩個label的文字長度加上水平間距超出了父視圖寬度時,需要設(shè)置抗壓縮優(yōu)先級。
解決辦法:假設(shè)我們希望Label1的內(nèi)容盡可能地展示完全,則將其Resistance Priority值調(diào)大(默認(rèn)為750)

調(diào)整后:

可以看到,Label2直接被壓沒了。
我們把Label2的Resistance Priority值調(diào)大:

調(diào)整后:

經(jīng)過上面的過程后,對于如何使用自動布局做出下圖的效果,是不是就心里有數(shù)了呢:

PS: 抗壓縮、抗拉伸優(yōu)先級同樣適用于與父視圖的約束優(yōu)先級(默認(rèn)值1000)
熟悉了xib中的優(yōu)先級設(shè)置后,在Masonry中對應(yīng)優(yōu)先級的思路相同,使用方法- (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis;進行對應(yīng)設(shè)置(系統(tǒng)自帶方法,不是Masonry加的)。

內(nèi)容尺寸(Intrinsic Content Size)

某些用來展現(xiàn)內(nèi)容的用戶控件,例如文本控件UILabel、按鈕UIButton、圖片視圖UIImageView等,它們具有自身內(nèi)容尺寸(對于UIImageView,其自身內(nèi)容尺寸就是圖片(1倍圖)的尺寸)。

如何設(shè)置自定義控件的內(nèi)容尺寸?

我們可以通過重寫- (CGSize)intrinsicContentSize方法來指定內(nèi)容尺寸。
先來看看UIView默認(rèn)的返回值:

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

這個方法的作用是當(dāng)使用已有約束不能夠計算出內(nèi)容寬度/高度時,自動為視圖添加寬度/高度約束,其值為返回值中對應(yīng)的寬/高。
因此我們可以在這個方法中返回計算好的內(nèi)容大小。

我們在xib中也可以看到下面的參數(shù)(需要注意的是,在這里設(shè)置的size僅僅只用于xib中展示效果,實際運行時并沒有什么卵用):

知道了intrinsicContentSize的用法后,我們考慮一下下面的需求:

寫一個自定義的View,滿足下面的功能:

  • 如果未使用約束,直接顯示frame的大小即可;
  • 如果添加了能夠定位左上角點坐標(biāo)的約束,則使用默認(rèn)的內(nèi)容大小。
  • 如果添加了能夠定位左上角點坐標(biāo)以及能夠計算出寬/高其中一個數(shù)值時,使用寬/高的另一個默認(rèn)值。
  • 如果添加了能夠計算出全部frame值的約束,則不使用默認(rèn)內(nèi)容大小。

整體效果類似UILabel,但是這里簡化成固定的默認(rèn)尺寸。

這里給出一個簡單的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是自動布局調(diào)整后的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)) {
        // 計算默認(rèn)內(nèi)容寬高的代碼
        float width = 100;
        float height = 20;
        _defaultSize = CGSizeMake(width, height);
    }
    return _defaultSize;
}
@end

ScrollView的自動內(nèi)容大小

我們看下面的布局:

當(dāng)我們需要Label2可以顯示多行數(shù)據(jù)時,僅僅設(shè)置右邊距是不夠的:

我們可以讓Label2的寬度等于Scrollview的寬度減去左右邊距之和:

看看效果:

效果出來了,但是僅僅這樣我們發(fā)現(xiàn)不能上下滾動,因為沒有設(shè)置底部間距,scrollview無法計算出內(nèi)容高度,需要給最下面的view(Label2)添加底部間距:

看看效果:

下面考慮更進一步的需求:Label1也要可以顯示多行時的情況。
我們把Label1右邊與Label2對齊,然后改一下內(nèi)容文字:

OK大功告成。

PS:如果使用Tablview時用到Cell的自動高度(不管是用系統(tǒng)自帶還是UITableView+FDTemplateLayoutCell),也都需要設(shè)置好約束,讓它能夠計算出豎直方向的內(nèi)容高度(水平方向tableview不需要,collectionView需要),具體過程和上面類似。

自定義View與自動布局

我們看一個樣式:

要實現(xiàn)這一樣式的方式有很多種,這里我們考慮用UILabel的子類直接實現(xiàn)的情況(盡可能少的View):

UIView中有這一個類方法:+ (Class)layerClass;,該方法返回的就是view.layer的類型,因此我們可以使用自定義的Layer替換掉默認(rèn)的Layer,然后裁剪出對應(yīng)的形狀(原理比較簡單,直接上代碼了):

@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動態(tài)調(diào)整的界面,許多小伙伴都不知道該如何使用自動布局進行動畫而放棄,其實不難:

如果使用xib或者代碼創(chuàng)建,最簡單的方式就是將約束設(shè)置成對應(yīng)屬性,直接修改約束的constant即可。

如果使用Masonry創(chuàng)建布局,那么只需要使用下面的代碼就可以使用動畫效果了:

// 如果其約束還沒有生成的時候需要動畫的話,就請先強制刷新后才寫動畫,否則所有沒生成的約束會直接跑動畫
[view.superview layoutIfNeeded];
[UIView animateWithDuration:3 animations:^{
  [view mas_updateConstraints:^(MASConstraintMaker *make) {
    make.height.mas_equalTo(123);
  }];
}];
// 強制繪制
[view.superview layoutIfNeeded];

記得動畫前后需要刷新。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容