AutoLayout拾遺

前言:

在面前已經(jīng)寫過一篇有關(guān)AutoLayout的東西了(項目干貨挖掘4——如何優(yōu)雅地使用AutoLayout自動布局)蛤签。這幾天在看《iOS Auto Layout開發(fā)秘籍》這本書辞友,又撈到了些干貨。所以作此篇震肮,用于補充。


約束:

約束是什么戳晌?
AutoLayout是給相應(yīng)的視圖添加“約束”來進行布局的鲫尊。所謂“約束”Constraint,就是有關(guān)視圖布局的限定躬厌。

有關(guān)約束的類
NSLayoutConstraint马昨,唯一一個共有的,我們可以直接操作的類扛施,所謂自動布局時給視圖添加約束就是使用該類鸿捧;
NSContentSizeLayoutConstraint,內(nèi)容大小約束疙渣,私有匙奴。像UILabelUIImageView有內(nèi)容大小,我們可以指定視圖尺寸和內(nèi)容大小的規(guī)則妄荔,比如內(nèi)容吸附規(guī)則盡量避免添加補白泼菌,而內(nèi)容壓縮規(guī)則防止內(nèi)容被剪切谍肤。這個類就是用來處理這個的。
NSAutoresizingMaskLayoutConstrain哗伯,自動尺寸調(diào)整約束荒揣,私有。該類將自動尺寸調(diào)整掩碼轉(zhuǎn)換成AutoLayout系統(tǒng)中對應(yīng)的約束焊刹。
_UILayoutSupportConstraint系任,布局支持約束,私有虐块。iOS 7新增的約束俩滥,它用來建立視圖控制器實例頂部和底部的實際邊界。布局支持約束防止視圖的內(nèi)容與狀態(tài)欄之類的障礙物重疊贺奠。
NSIBPrototypingLayoutConstraint霜旧,原型約束,私有儡率。iOS 7新增的約束挂据,它是Interface Builder(IB)為你添加的約束。

在這些約束中儿普,我們直接可以使用的只有NSLayoutConstraint這個共有類棱貌,但是其他這幾個私有的約束類型,我們也要了解一下箕肃。因為當(dāng)布局有問題時婚脱,在控制器的日志里,會打印出布局時有問題的約束信息勺像,即約束的類名障贸,通過類名,我們可以快速明白哪里出了問題吟宦。

約束應(yīng)該添加在哪個視圖上篮洁?
約束添加在該約束所引用幾個視圖的最近公共祖先中。

約束的優(yōu)先級:

@property UILayoutPriority priority;
typedef float UILayoutPriority;
static const UILayoutPriority UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) = 1000; // A required constraint.  Do not exceed this.
static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750; // This is the priority level with which a button resists compressing its content.
static const UILayoutPriority UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) = 250; // This is the priority level at which a button hugs its contents horizontally.
static const UILayoutPriority UILayoutPriorityFittingSizeLevel NS_AVAILABLE_IOS(6_0) = 50; // When you send -[UIView systemLayoutSizeFittingSize:], the size fitting most closely to the target size (the argument) is computed.  UILayoutPriorityFittingSizeLevel is the priority level with which the view wants to conform to the target size in that computation.  It's quite low.  It is generally not appropriate to make a constraint at exactly this priority.  You want to be higher or lower.

優(yōu)先級在NSLayoutConstraint中殃姓,以priority屬性來修改袁波,它是UILayoutPriority類型的,而該類型其實是對float的再定義蜗侈,也就是說優(yōu)先級其實是浮點類型的篷牌,范圍是從1到1000。
蘋果為我們提供了幾個優(yōu)先級枚舉踏幻,UILayoutPriorityRequired的優(yōu)先級為1000枷颊,表示這是一個必需執(zhí)行的優(yōu)先級;UILayoutPriorityDefaultHigh的優(yōu)先級為750,表示這是一個抵抗壓縮阻力的優(yōu)先級夭苗,假如我們設(shè)置某Label尺寸的約束優(yōu)先級為751信卡,且尺寸小于Label的內(nèi)容大小,則會執(zhí)行尺寸的優(yōu)先級题造,因為它的優(yōu)先級更高傍菇,更迫切。這樣的話會造成Label被剪切了界赔;UILayoutPriorityDefaultLow的優(yōu)先級為250桥嗤,表示這是一個抵抗拉伸阻力的優(yōu)先級。

蘋果建議我們采用更靈活的數(shù)字來給約束設(shè)置優(yōu)先級仔蝌,而少用枚舉的幾個值。


內(nèi)容大小荒吏,壓縮阻力敛惊,拉伸阻力:

** 內(nèi)容大小:**
對于像UILabelUIImageView之類的有內(nèi)容的視圖绰更,一般都有內(nèi)容大小intrinsicContentSize瞧挤,即不用你指定該視圖的尺寸大小,它的尺寸默認(rèn)是內(nèi)容大小儡湾,內(nèi)容有多大特恬,它就顯示多大。對于普通的徐钠,無內(nèi)容的視圖癌刽,intrinsicContentSize則默認(rèn)是是(-1,-1)。無內(nèi)容的視圖尝丐,intrinsicContentSizegetter方法返回(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric)显拜,若你想讓其有內(nèi)容大小,則可以子類化該視圖爹袁,重寫intrinsicContentSizegetter方法远荠。

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

** 壓縮阻力和拉伸阻力:**
壓縮阻力是為了防止視圖內(nèi)容被剪切,拉伸阻力是為了防止視圖內(nèi)容被擴大失息。決定壓縮阻力或拉伸阻力是否能夠成功的是你設(shè)置的內(nèi)容大小的約束優(yōu)先級是多少譬淳。以壓縮阻力來說,前面提到過盹兢,枚舉值UILayoutPriorityDefaultHigh表示的就是壓縮阻力的優(yōu)先級邻梆,即750。若你給視圖設(shè)置的保持內(nèi)容大小的約束優(yōu)先級大于750绎秒,則說明保持視圖內(nèi)容大小更迫切确虱,則即使設(shè)置的尺寸約束比內(nèi)容大小要小,也不會對視圖進行剪切,因為壓縮阻力起作用了校辩。用代碼來寫就是這樣:

    CustomView *customView =  [CustomView new];
    
    // 壓縮阻力
    [customView setContentCompressionResistancePriority:751 forAxis:UILayoutConstraintAxisHorizontal];
    [customView setContentCompressionResistancePriority:751 forAxis:UILayoutConstraintAxisVertical];
    
    // 拉伸阻力
    [customView setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisHorizontal];
    [customView setContentHuggingPriority:251 forAxis:UILayoutConstraintAxisVertical];

約束的沖突:P131頁

在添加可能最常遇見的問題是不充分的約束和有沖突的約束窘问。不充分的約束就是指約束不足以將視圖確定,有沖突的約束就是給視圖添加的約束邏輯有沖突宜咒。這兩種情況都會導(dǎo)致最終界面顯示出現(xiàn)意外惠赫,很可能在界面上看不到該視圖。此時故黑,就需要你發(fā)現(xiàn)有問題的約束儿咱,做出修改調(diào)整了。

一般來說出現(xiàn)約束沖突有兩種可能性场晶。首先第一種就是我們沒有關(guān)閉視圖的“自動尺寸調(diào)整”屬性混埠,“自動尺寸調(diào)整”autoresizingMaskAutoLayout本身并不沖突。兩者其實是可以同時使用的诗轻,只要兩者所約束的邏輯不沖突钳宪,則不會影響界面布局。而且前面我們也提到了NSAutoresizingMaskLayoutConstrain這個類扳炬,它是將自動尺寸調(diào)整的東西轉(zhuǎn)換成了AutoLayout中對應(yīng)的約束吏颖。

默認(rèn)情況下,視圖是沒有關(guān)閉“自動尺寸調(diào)整”屬性的恨樟,若我們打算在項目中統(tǒng)一使用AutoLayout來進行界面布局的話半醉,就得將其關(guān)閉。代碼如下:

    CustomView *customView =  [CustomView new];
    customView.translatesAutoresizingMaskIntoConstraints = NO;

自動布局的動畫劝术;

若你的界面是以frame進行布局的缩多,則在進行UIView動畫時,對frame進行相應(yīng)的修改就行了养晋。那若你的界面是AutoLayout的瞧壮,在UIView動畫時該怎樣執(zhí)行動畫呢?其實和修改frame一樣匙握,需要我們在執(zhí)行動畫時對約束進行調(diào)整咆槽。

    [UIView animateWithDuration:1.f animations:^{
        
        [_frontView removeAllConstraint]; // 移除原有的約束,開始重新添加約束
        LAY(_frontView.left, _bottomView.left, 1, 0);
        LAY(_frontView.centerY, _bottomView.centerY, 1, 0);
        LAY(_frontView.height, _bottomView.height, 1, 0);
        LAY(_frontView.width, _bottomView.width, 0.9, 0);
        
        [self.contentView layoutIfNeeded]; // 立即重新布局
    }];

關(guān)于layoutIfNeeded方法圈纺,詳情請見:UIView的layoutSubviews秦忿、layoutIfNeeded、setNeedsLayout區(qū)別和聯(lián)系


滾動視圖的自動布局:

如果用frame布局蛾娶,滾動視圖的用法我們已經(jīng)非常熟悉了灯谣。創(chuàng)建scrollView并設(shè)置其frame,然后設(shè)置contentSize蛔琅,這個屬性表示scrollView可滾動的區(qū)域胎许,有了該屬性設(shè)置的區(qū)域,滑動scrollView時,才會在有限的frame區(qū)域中滑出所有的視圖辜窑。這個contentSize屬性是必不可少的钩述,不然不會顯示scrollView上的子視圖。

    _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 100, PDWidth_mainScreen, 300)];
    _scrollView.backgroundColor = [UIColor grayColor];
    _scrollView.contentSize = CGSizeMake(PDWidth_mainScreen*4, 0);
    [self.contentView addSubview:_scrollView];
    
    for(int i=0; i<4; i++)
    {
        UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(i*PDWidth_mainScreen, 0, PDWidth_mainScreen, 300)];
        subView.backgroundColor = PDColor_Random;
        [_scrollView addSubview:subView];
    }

但如果穆碎,我們將frame布局牙勘,直接替換為AutoLayout布局。雖然打印出來的contentSize是正常的所禀,但是程序跑起來后發(fā)現(xiàn)根本就沒subView顯示:

    _scrollView = [UIScrollView create];
    _scrollView.backgroundColor = [UIColor grayColor];
    _scrollView.contentSize = CGSizeMake(PDWidth_mainScreen*4, 0);
    [self.contentView addSubview:_scrollView];
    LAY(_scrollView.left, self.contentView.left, 1, 0);
    LAY(_scrollView.right, self.contentView.right, 1, 0);
    LAY(_scrollView.centerY, self.contentView.centerY, 1, 0);
    LAYC(_scrollView.height, 300);
    
    for(int i=0; i<4; i++)
    {
        UIView *subView = [UIView create];
        subView.backgroundColor = PDColor_Random;
        [_scrollView addSubview:subView];
        LAY(subView.top, _scrollView.top, 1, 0);
        LAY(subView.bottom, _scrollView.bottom, 1, 0);
        LAYC(subView.width, self.contentView.bounds.size.width);
        if(i==0){
            LAY(subView.left, _scrollView.left, 1, 0);
        }else{
            UIView *preView = _scrollView.subviews[i-1];
            LAY(subView.left, preView.right, 1, 0);
        }
    }

    NSLog(@"??????:scrollView.contentSize = %@", NSStringFromCGSize(_scrollView.contentSize));
屏幕快照 2017-04-05 19.51.46.png

這篇文章對此問題做些較詳細的解說(史上最簡單的UIScrollView+Autolayout出坑指南)方面,我直接給出正確的方案吧。

可以看到色徘,我們建了個containerView放在了scrollView上恭金,然后將多個子視圖是添加在這個containerView上的,而并非直接添加在scrollView上褂策。而且最最重要的是有關(guān)containerView的布局約束横腿,containerViewtop,left,bottom,rightscrollViewtop,left,bottom,right的間距均為0。普通情況下我們只需要這四個約束就已足夠辙培,因為可以將一個視圖的布局固定了。但是這里邢锯,我們之所以創(chuàng)建containerView添加在scrollView上扬蕊,其實是為了正確地算出滾動的范圍,所以也要給containerView添加widthheight的約束丹擎,這個是關(guān)鍵尾抑!

    _scrollView = [UIScrollView create];
    _scrollView.backgroundColor = PDColor_Name_Black;
//    _scrollView.contentSize = CGSizeMake(PDWidth_mainScreen*4, 0);
    _scrollView.pagingEnabled = YES;
    [self.contentView addSubview:_scrollView];
    LAY(_scrollView.left, self.contentView.left, 1, 0);
    LAY(_scrollView.right, self.contentView.right, 1, 0);
    LAY(_scrollView.centerY, self.contentView.centerY, 1, 0);
    LAYC(_scrollView.height, 150);
    
    UIView *containerView = [UIView create];
    containerView.backgroundColor = PDColor_Orange;
    [_scrollView addSubview:containerView];
    LAY(containerView.top, _scrollView.top, 1, 0);
    LAY(containerView.left, _scrollView.left, 1, 0);
    LAY(containerView.bottom, _scrollView.bottom, 1, 0);
    LAY(containerView.right, _scrollView.right, 1, 0);
    LAYC(containerView.width, PDWidth_mainScreen*4);
    LAYC(containerView.height, 150);
    
    for(int i=0; i<4; i++)
    {
        UIView *subView = [UIView create];
        subView.backgroundColor = PDColor_Random;
        [containerView addSubview:subView];
        LAY(subView.top, containerView.top, 1, 0);
        LAY(subView.bottom, containerView.bottom, 1, 0);
        LAYC(subView.width, PDWidth_mainScreen);
        if(i==0){
            LAY(subView.left, containerView.left, 1, 0);
        }else{
            UIView *preV = containerView.subviews[i-1];
            LAY(subView.left, preV.right, 1, 0);
        }
    }
    NSLog(@"??????:scrollView.contentSize = %@", NSStringFromCGSize(_scrollView.contentSize));

注意:在上面的代碼中,我們沒有給contentSize賦值蒂培,所以contentSize打印出的值為(0,0)


如何封裝一個AutoLayout庫:

項目干貨挖掘4——如何優(yōu)雅地使用AutoLayout自動布局

AutoLayout的封裝以及Demo再愈,我上傳至GitHub了(YWAutoLayoutGitHub地址)。Demo效果如下:

屏幕快照 2017-04-07 01.55.26.png

如何在xCode設(shè)置护戳,使其在模擬器上顯示自動布局的邊線:

Xcode——>Debug——>View Debugging——>Show View Frames或者Show Alignments Rectangles翎冲。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市媳荒,隨后出現(xiàn)的幾起案子抗悍,更是在濱河造成了極大的恐慌,老刑警劉巖钳枕,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缴渊,死亡現(xiàn)場離奇詭異,居然都是意外死亡鱼炒,警方通過查閱死者的電腦和手機衔沼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人指蚁,你說我怎么就攤上這事菩佑。” “怎么了欣舵?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵擎鸠,是天一觀的道長。 經(jīng)常有香客問我缘圈,道長劣光,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任糟把,我火速辦了婚禮绢涡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘遣疯。我一直安慰自己雄可,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布缠犀。 她就那樣靜靜地躺著数苫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辨液。 梳的紋絲不亂的頭發(fā)上虐急,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音滔迈,去河邊找鬼止吁。 笑死,一個胖子當(dāng)著我的面吹牛燎悍,可吹牛的內(nèi)容都是我干的敬惦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼谈山,長吁一口氣:“原來是場噩夢啊……” “哼俄删!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奏路,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤抗蠢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后思劳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迅矛,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年潜叛,在試婚紗的時候發(fā)現(xiàn)自己被綠了秽褒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壶硅。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖销斟,靈堂內(nèi)的尸體忽然破棺而出庐椒,到底是詐尸還是另有隱情,我是刑警寧澤蚂踊,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布约谈,位于F島的核電站,受9級特大地震影響犁钟,放射性物質(zhì)發(fā)生泄漏棱诱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一涝动、第九天 我趴在偏房一處隱蔽的房頂上張望迈勋。 院中可真熱鬧,春花似錦醋粟、人聲如沸靡菇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厦凤。三九已至,卻和暖如春育苟,著一層夾襖步出監(jiān)牢的瞬間较鼓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工宙搬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留笨腥,地道東北人拓哺。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓勇垛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親士鸥。 傳聞我的和親對象是個殘疾皇子闲孤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容