昨天從<<iOS Auto Layout開發(fā)秘籍>>的書中看到了我們在實際開啊中不常用看到的VFL和系統(tǒng)的自動布局類.雖然我們的前輩們已經(jīng)開源了masonry等自動布局框架,但是他們還是在系統(tǒng)的框架基礎上進行的再次封裝.因為系統(tǒng)的自動布局約束看起來并不是那么簡約.代碼量比較多.但是我們在使用第三方約束的時候就應該明白,我們系統(tǒng)約束到底是什么樣的.今天就給大家?guī)硐到y(tǒng)的自動布局約束類.在此并不包括IB中的自動布局.
系統(tǒng)原生自動布局
1.系統(tǒng)原生的自動布局還有劃分,一種就是masonry封裝的
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullableid)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c
如果你看過masonry的源碼肯定會看到這個方法.因為他最終都是通過這個方法對需要約束的視圖進行約束的.
還有一種就是我們今天分享的重點VFL.以前都只是聽說過系統(tǒng)有這種布局方式但是從來沒有用過.那么系統(tǒng)提供兩種布局方式到底有什么異同啦.
第一種約束方式.
先來敲一段和masonry使用同樣方式的約束吧.
首先我們定義一個視圖讓其距離left 64, right 64,top 64,bottom 64.
由于開發(fā)都用masonry所以系統(tǒng)的方法很多都是記憶很模糊.那么我們首先來看看系統(tǒng)的這些方法.
前兩個是添加約束的方法,后面兩個書刪除約束的方法.
這兩個方法很簡單,一個參數(shù)是NSLayoutConstraint,一個是數(shù)組,但數(shù)組元素還是NSLayoutConstraint.這個就不多解釋了.如果有人不知道NSLayoutConstraint,那么就說一下吧.
從字面意思就能看出來他到底是干什么的了.NS不多解釋蘋果老大哥的Foundation的標識例如NSArray.layout就是布局的意思.那后面這個是什么啦?然而這個就是我們的約束.
上面的方法是在UIView的分類中添加的,那結果就是只要你繼承或間接繼承都會有這個添加和刪除的方法.
從上面這張截圖中我們看到了每個方法中都有update這個關鍵字.那么我們的初步估計就是更新約束.或者是更新約束后更新視圖.蘋果的方法最大的特點是意思寫在明面上,這也就要求我們在定義我們的方法時一定要能從方法名看出作用.
1.updateConstraintsIfNeeded調(diào)用這個方法會立即更新約束.
2.系統(tǒng)實際用來更新約束的方法
3.這個方法的調(diào)用的返回值決定是否去掉用系統(tǒng)實際更新約束的方法.作為正常布局的一部分.
4.告訴layoutView需要更新約束循榆,在下次計算或者更新約束會更新約束
從上面的方法我們可以總結出以下幾點.當我們在未來的某個時刻需要更新約束時,會首先調(diào)用setNeedsUpdateConstraints.這個方法.然后在某個時刻上調(diào)用updateConstraintsIfNeeded這個方法.這個方法調(diào)用會調(diào)用updateConstraints這個方法來完成正常的更新約束.
如果只是淡淡的調(diào)用updateConstraintsIfNeeded這個方法,可能并不會調(diào)用updateConstraints這個方法來更新約束.因為吐過視圖上的約束可能還沒有發(fā)生變化且沒有標記需要更新.
系統(tǒng)在調(diào)用layoutSubviews時,就會調(diào)用updateContraintsIfNeeded,通過更新約束,用super view到subview的層次順序,來計算frame,返向確定布局.
stackoverFlow上有關于改變約束的方法研究.
1.如果想立即改變約束,調(diào)用setNeedsLayout
2.吐過想改變view的一些屬性如offsets可能會導致布局的改變,那么會調(diào)用setNeedsUpdateConstraints,更多的時候和面還需要加setNeedsLayout.
3.如果想立即改變布局,如會形成新的frame,那么需要在調(diào)用layoutIfNeeded.
系統(tǒng)中還有很多方法,就不一一介紹了.等到用到時在進行解釋.
上面的布局只是一個簡單的相對于self.view 的布局.那么兩個視圖應該怎樣布局啦.
布局布標,view1和view2并排放置,view2的left距離view的right 15px.并且view1和view2等高等寬,并且centerY等于self.view的centerY.
好了如果左右間距都可以了,上下間距也是可以的.這個留在后面給大家當練習吧.
在這本書上還知道我們?nèi)绾涡薷挠衅缌x的約束和尋找因為不正確約束導致視圖丟失的方法.
1.規(guī)則不一致的約束.有這么一個經(jīng)典的例子就是viewA是viewB寬度的三倍,viewB是viewA寬度的2倍.請用代碼實現(xiàn).
當然代碼我相信大家都能寫的出來.但是肯定會有人問這個約束規(guī)則能實現(xiàn)嗎?
答案是肯定的:絕對能實現(xiàn).也毫無歧義,因為它的表述是完全正確的.在viewA的寬度=viewB的寬度*3和viewB的寬度=viewA的寬度*2這兩個規(guī)則中并沒有指定到底這兩個寬度是多少,如果都是0那么這條規(guī)則就完全成立.所以我們最終就會看到視圖的約束寬度為0.
2.缺失約束也可能導致丟失視圖
舉個簡單的例子,就以我上面的代碼為例:初始化frame為0,0,30,30.那么我給的約束是高度等于80,距離父視圖left 0,top也是0,那么這個視圖會在self.view上出現(xiàn)嗎.并不會!并且約束也不會報錯.那么視圖為什么不出現(xiàn)啦.還記著我們沒意識圖需要自己添加約束時,講一個屬性置為了NO.translatesAutoresizingMaskIntoConstraints.什么意思啦.當我們設置這個屬性時,默認的初始值(0,0,30,30)已經(jīng)被廢棄了.因為寬度沒有確定,系統(tǒng)不能確定視圖的寬度到底是多少,因而導致寬度為0,導致不可見.
還有就是介紹一下,添加約束的方法吧.
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullableid)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c
先來介紹幾個參數(shù)吧
item:需要添加約束的視圖
attr1:需要添加約束的屬性,例如left,bottom,top.right,centerx,centerY.等.
relation:分為三種1.NSLayoutRelationLessThanOrEqual小于等于.NSLayoutRelationEqual等于.NSLayoutRelationGreaterThanOrEqual大于等于.
view2:item布局的參考視圖
attr2:view2的left,right等屬性.比如item的left相對于view2的right距離15.
multiplier:比例
constant:固定值,比如設定item的寬高時,如果不與其他視圖參考.view2可以為nil.attr2可以為NSLayoutAttributeNotAnAttribute.
總結:view.attr1 = view.attr2*multiplier+constant.
第一種約束方式就講到這.
VFL
可能很多人對VFL只知其名,但在現(xiàn)實中沒用過.
就在今天讓我們一起研究它,使用它.
可能對于上面的代碼,我們的感覺是代碼量好大啊,布局一個視圖就這么多代碼.那么當時圖較多時,怎么辦.蘋果的工程師也想到了這個問題就出現(xiàn)了我們第二種布局方式:
Visual format language.
它和第一種的約束布局的區(qū)別主要在于代碼量減少了.而且方法更加簡單.先來看一段??吧.
舉例self.view上下左右各64.
然后我們來分析代碼:
+ (NSArray<__kindofNSLayoutConstraint*> *)constraintsWithVisualFormat:(NSString*)format options:(NSLayoutFormatOptions)opts metrics:(nullableNSDictionary *)metrics views:(NSDictionary *)views;
我們看到這個方法的返回值是一個數(shù)組.數(shù)組中是這個NSLayoutConstraint對象.
參數(shù)format:我們從上面的代碼就可以看出約束都是寫在format中的.an ASCII art-like visual format string
opts:文檔中的大概意思是,屬性的描述和布局的仿效都是對象并且在visual format 字符串中.
metrics:這個字典的鍵必須是visual format字符串 ,他的值必須是NSnumber對象.在visual format字符串中的常量都在這個字段中.
views:visula format字符串中出現(xiàn)的views,保存在這個子彈中,鍵也是visual format字符串.值是view object.
好了看完這個我們應該仔細看看visual format 字符串.
H:|-64-[view]-64-| 這個字符串的意思是:水平方向距離左邊64,右邊64.
V:|-64-[view]-64-| ?這個字符串的意思是:垂直方向舉例上邊64,下邊64.
那么高度和寬度怎么設置啦.
很簡單:
V:|-64-[view(==80)] 這個就是距離top 64 高度等于80.設置寬度類似 只需要將V改為H就表示為,水平方向距離左邊64,寬度為80.
VFL最大的缺點是什么啦.他不能表示相對比例和相對居中的約束.
那么兩個視圖的布局該怎么設置啦.
改有很多關于VFL的用法,這里就不多介紹了,可以查看蘋果官方文檔和(http://study1234.com/autolayoutshen-ru-qian-chu-liu-chun-dai-ma-de-pian-zhi/amp/)
在介紹幾個方法就結束今天的分享了:
//調(diào)用這個方法來判斷視圖的約束是否充分/是否有歧義.
hasAmbiguousLayout
//可以獲得不同方向上的約束
- (NSArray<__kindofNSLayoutConstraint*> *)constraintsAffectingLayoutForAxis:(UILayoutConstraintAxis)axisNS_AVAILABLE_IOS(10_0);
//這個方法會隨機改變視圖的layout到另外一個有效的layout析恢。這樣我們就可以很清楚的看到哪一個layout導致了整體的布局約束出現(xiàn)了錯誤,或者我們應該增加更多的布局約束秧饮。
exerciseAmbiguityInLayout
//讓視圖知道下次布局時需要重新計算.
//[view invalidateIntrinsicContentSize];
//壓縮阻力Size Inspector指定
//代碼設置一個視圖的壓縮阻力.水平軸和垂直軸都需要分別設置.設置的值可以再1到1000之間,默認值為750;
//[view setContentCompressionResistancePriority:500 forAxis:UILayoutConstraintAxisHorizontal];
////代碼設置一個試圖的內(nèi)容吸附優(yōu)先級.默認值為250;
//[view setContentHuggingPriority:501 forAxis:UILayoutConstraintAxisHorizontal];
我在這里先解釋一下壓縮阻力和內(nèi)容吸附.
壓縮阻力.就是指保護其內(nèi)容的方式,壓縮阻力高的視圖可以抵抗壓縮,不允許內(nèi)容被裁減.低的壓縮阻力內(nèi)容會被裁剪.雖然我們空間本身存在著壓縮阻力.但是如果他的優(yōu)先級較低.如果我們改變約束,將其寬度較小.它的內(nèi)容就會被裁剪.要想不被裁剪我們可以手動修改他的重要性.
//[view setContentCompressionResistancePriority:500 forAxis:UILayoutConstraintAxisHorizontal];
內(nèi)容吸附:視圖的大小與內(nèi)容大小匹配的原則.
較高的優(yōu)先級不允許視圖將其伸展.防止出現(xiàn)大片留白.較低的優(yōu)先級允許被拉伸,出現(xiàn)大量留白.
[view setContentHuggingPriority:501 forAxis:UILayoutConstraintAxisHorizontal];
我們可以手動改變其優(yōu)先級.確定是否允許被拉伸.
我們在設置約束時也可以設置約束的優(yōu)先級.
1000為最高.50為最低.
好了我們不怎么熟悉的自動布局就先說到這里.后面還會有關于自動布局的分享.例如自動布局的動畫.IB中的自動布局等.
如果喜歡小編,可以點擊關注,我會不定期的更新一些文章,也可以關注我的專題
本人聯(lián)系方式:qq:513961360
vx:掃描下方二維碼:
希望能有志同道合的好友加我.聊技術.聊生活都可以.
email:513961360@qq.com
也可以加我們的qq群希望能與朋友們一起聊天和學習.群里還有很多iOS開發(fā)者,幫助我們解決問題,并且同時學習.
qq群號:580284575