自iphone4s之後,蘋果前後推出了iphone五、iphone5s、iphone六、iphone6plus、iphone6s、iphone6splus這些新的機型,它們的屏幕大小各有所異,今後給咱們開發者留下了一個蛋疼的問題:屏幕適配。程序員
一、顯示座標定位方式:在4和4s的時代,咱們採用顯示座標定位方式設置一個視圖的座標,好比view.frame = CGRectMake(20, 70, 160, 160);在當時顯然是沒有任何問題的,由於手機屏幕的大小是固定的,屏寬320像素,屏高480像素。編程
二、autoresizingMask:自iphone5之後,手機屏幕的高度變成了568像素,有時候咱們定義的視圖在iphone4和4s上運行起來位置擺放正常,可是在5和5s上就不那麼和諧了,這就對開發者提出了適配的任務:如何讓視圖在不一樣大小的屏幕上恰到好處的展示出來呢?其實蘋果最早推出來的跟適配沾邊的技術autoresizingMask。autoresizingMask能給出子視圖相對於父親視圖的對齊方式與縮放係數,當父視圖發生變化時,經過每一個視圖autoresizingMask便可自動得出新的位置。使用步驟是iphone
第一步:設置父視圖的autoresizesSubviews屬性爲YES,superView.autoresizesSubviews = YES;,不然後面的autoresizingMask都將失效。函數
第二步:設置子視圖的autoresizingMask, subView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin。佈局
這裏面的autoresizingMask屬性是一個枚舉值,枚舉值各項含義以下:UIViewAutoresizingNone:就是不自動調整。設計
UIViewAutoresizingFlexibleLeftMargin: 自動調整與superView左邊的距離,保證與superView右邊的距離不變。code
UIViewAutoresizingFlexibleRightMargin: 自動調整與superView的右邊距離,保證與superView左邊的距離不變。orm
UIViewAutoresizingFlexibleTopMargin: 自動調整與superView頂部的距離,保證與superView底部的距離不變。對象
UIViewAutoresizingFlexibleBottomMargin: 自動調整與superView底部的距離,也就是說,與superView頂部的距離不變。圖片
UIViewAutoresizingFlexibleWidth: 自動調整本身的寬度,保證與superView左邊和右邊的距離不變。
UIViewAutoresizingFlexibleHeight: 自動調整本身的高度,保證與superView頂部和底部的距離不變。
UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin: 自動調整與superView左邊的距離,保證與左邊的距離和右邊的距離和原來距左邊和右邊的距離的比例不變。好比原來距離爲20,30,調整後的距離應爲68,102,即68/20=102/30。遺憾的是這些技術的缺陷也十分明顯,是一個不成熟的技術,緣由有兩點:
(1)、autoresizingMask縮放比例是UIKit內部計算的,開發者沒法指定縮放比例的精確值。
(2)、變化規則只能基於父子視圖,沒法解決兄弟視圖之間的位置關係。因此在iOS6推出自動佈局(Auto Layout)技術後,autoresizingMask成爲一項雞肋技術。能夠用食之無味,棄之惋惜來形容。
三、AutoLayout-可視化編程自動佈局:關於AutoLayout技術有可視化編程和純代碼兩種方式,本處先講解可視化的方式。自動佈局是對autoresizingMask的進一步改進,它容許開發者在界面上的任意兩個視圖之間創建精確的線性變化規則。所謂線性變化就是數學中的一次函數,即: y = m*x + c 其中x和y是界面中任意兩個視圖的某個佈局屬性,m爲比例係數,c爲常量。在storyboard或者xib中設置約束的方案有三種:
(1)在設計器中長按Control鍵拖動控件。
(2)在左側文檔結構窗口中長按Control鍵拖動控件。
(3)點擊設計器底部圖標,pin設置距離約束、固定寬高約束、等高等寬和寬高比約束,align設置對齊約束。
注意:方案(1)和(2)中同時按住Shift鍵與Option鍵能夠同時設置多個約束。前面咱們提到了一個重要概念就是約束,約束有不少種類,現列出以下:Leading :距離另外一視圖左邊距離的約束
Trailing :距離另外一視圖左邊距離的約束 Top :距離另外一視圖頂部距離的約束 Bottom :距離另外一視圖底部距離的約束 Width:單個視圖固定寬度的約束 Height:單個視圖固定高度的約束 Equal Widths:兩個視圖等寬的約束 Equal Heights:兩個視圖等高的約束 Aspect Ratio:單個視圖固定寬高比的約束 Horizontally in Container:固定在父視圖水平方向中線的約束 Vertically in Container:固定在父視圖垂直方向中線的約束 Vertically in Container:固定在非父視圖水平方向中線的約束 Horizontall Centers:固定在非父視圖垂直方向中線的約束 Vertical Spacing:兩個視圖垂直方向距離的約束 Horizontally Spacing:兩個視圖水平方向距離的約束 align Top to:頂部對齊約束 align Bottom to:底部對齊約束 align Leading to:左邊對齊約束 align Trailing to:右邊對齊約束
還有一些選項幾乎是咱們必用的,含義列出以下:Update Frames:更新座標以適應約束
Update Constraints:更新約束以適應座標
Add Constraints:添加約束
Constain to margins:若是你點了constrain to margins,左右會有8個點的空擋,從8個點後開始計算約束,而沒有點時,已屏幕的0點開始計算。建議取消勾選。
咱們能夠在畫布的右邊查看每一個約束的詳情,咱們在前面講的y相對於x發生變化的公式 y = m*x + c 在這裏獲得了呼應,First Item對應公式中的y,表示因變量
Relation對應公式中的=,表示相等關係,這裏顯示的是Equal即相等
Second Item對應公式中的x,表示自變量
Multiplier對應公示中的m,表示縮放比例係數
Constant對應公示中的c,表示偏移常量
點擊First Item或者Second Item下拉菜單,選擇Reverse First And Second Item便可交換雙方的位置。
得反函數:x = 1/m * y - c/m
值得一提的是約束既不能少,少了不能肯定視圖位置,也不能多,多了會衝突。
另外,某些用來展示內容的用戶控件,例如文本控件UILabel、按鈕UIButton、圖片視圖UIImageView等,它們具備自身內容尺寸(Intrinsic Content Size),此類用戶控件會根據自身內容尺寸添加布局約束。也就是說,若是開發者沒有顯式給出其寬度或者高度約束,則其自動添加的自身內容約束將會起做用。
四、AutoLayout-代碼自動佈局:並不是全部的狀況都能用IB來解決,好比定義諸如某視圖的高度等於另外一個視圖的寬度這樣的約束,經過代碼構建自動佈局約束是最基礎的,也是最靈活的方式。缺點是代碼很冗長。每個佈局約束是一個NSLayoutConstraint實例,NSLayoutConstraint類的主要屬性定義以下
@property (readonly, assign) id firstItem; @property (readonly) NSLayoutAttribute firstAttribute; @property (readonly) NSLayoutRelation relation; @property (readonly, assign) id secondItem; @property (readonly) NSLayoutAttribute secondAttribute; @property (readonly) CGFloat multiplier; @property CGFloat constant; ... +(instancetype)constraintWithItem:(id)firstItem attribute:(NSLayoutAttribute)firstAttribute relatedBy:(NSLayoutRelation)relation toItem:(id)secondItem attribute:(NSLayoutAttribute)secondAttribute multiplier:(CGFloat)multiplier constant:(CGFloat)constant;
每個佈局約束就是一個明確的線性變化規則,在數學上是以一次函數的形式表示,即:
y = m * x + c
每一個約束就對應以下關係:
firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant
咱們能夠調用NSLayoutConstraint類的constraintWithItem:…方法,傳入全部須要的參數構造一個新的約束。
(1)、firstItem與secondItem分別是界面中受約束的視圖與被參照的視圖,他們不必定非得是兄弟關係或者父子關係,只要是他們有着共同的祖先視圖便可。
(2)、firstAttribute與secondAttribute分別是firstItem與secondItem的某個佈局屬性(NSLayoutAttribute),其中NSLayoutAttributeNotAnAttribute當咱們須要爲某個視圖精確指定一個寬度或者高度值時,這時候secondItem爲nil,secondAttribute爲 NSLayoutAttributeNotAnAttribute。
(3)、relation定義了佈局關係
typedef NS_ENUM(NSInteger, NSLayoutRelation) { NSLayoutRelationLessThanOrEqual = -1, NSLayoutRelationEqual = 0, NSLayoutRelationGreaterThanOrEqual = 1, };
佈局關係能夠是相等、大於等於或者小於等於。
(4)、multiplier即比例係數。
(5)、constant即常量
使用案例以下:
第一步:默認的autoresizingMask代碼和自動佈局約束會衝突,設置爲no,程序只有自動佈局約束代碼,避免衝突。
logoView.translatesAutoresizingMaskIntoConstraints = NO;
第二步:設置約束,爲了惟一肯定視圖的位置,我添加了四個約束
logoViewtopConstraint = [NSLayoutConstraint constraintWithItem:logoView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTopMargin multiplier:1.0f constant:80]; NSLayoutConstraint *logoViewcenterConstaint = [NSLayoutConstraint constraintWithItem:logoView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0]; NSLayoutConstraint *logoViewwidthConstaint = [NSLayoutConstraint constraintWithItem:logoView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:0.3f constant:0]; NSLayoutConstraint *logoViewhightConstaint = [NSLayoutConstraint constraintWithItem:logoView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:logoView attribute:NSLayoutAttributeHeight multiplier:1.0f constant:0]; 第三步:添加約束 logoViewtopConstraint.active = YES; logoViewcenterConstaint.active = YES; logoViewwidthConstaint.active = YES; logoViewhightConstaint.active = YES;
五、VFL:Visual Format Language,可視化格式語言,能直觀又簡單的建立約束,缺點是不能表達全部的約束。用法舉例:
第一步:autoresizingMask設置爲NO。
logoView.translatesAutoresizingMaskIntoConstraints = NO;
第二步:使用VFL添加約束
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-80-[logoView]" options:NSLayoutFormatAlignAllLeft metrics:nil views:@{@"logoView":logoView}]];
語法是這樣的,H表示橫向
V表示豎向
| 表示父視圖
[視圖名字]表示視圖
-表示 間距
-間距- 表示設置其餘間距
[視圖名字(設置視圖的寬高)]
上面constraintsWithVisualFormat方法的參數含義以下
VisualFormat:VFL語句
options:基於哪個選項(基於哪一個方向去計算佈局)
metrics:綁定數值(NSNumber) 與字符串
views:綁定視圖 與字符串
NSDictionaryOfVariableBindings:是一個宏,方便咱們構建字典,NSDictionaryOfVariableBindings(logoView)就等於@{@」logoView」: logoView}。
另外須要注意一些精簡寫法(1)、省略:也就是說若是間距爲0則不用明確寫出,因此@」H:|-0-[logoView]-0-|」能夠精簡爲@」H:|[logoView]|」
(2)、合併:例如@"H:[password]-20-[tfPassword]"和@"[tfPassword]-40-|"能夠合併爲@"H:[password]-20-[tfPassword]-40-|"
六、Masonry:是第三方庫,讓自動佈局的寫法更加簡便,是如今程序員使用較爲廣泛的一種方案。用法案例以下:
第一步:聲明爲__weak,由於咱們後面要使用block,以防內存問題。
__weak typeof(self) weakself = self;
第二步:添加約束。
[logoView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(weakself.view); make.top.mas_equalTo(80); make.width.mas_equalTo(weakself.view.bounds.size.width*0.3); make.height.mas_equalTo(weakself.view.bounds.size.width*0.3); }];
Masonry用法比較簡單,須要注意的地方主要有兩點
注意點(1): 使用 mas_makeConstraints方法的元素必須事先添加到父元素的中,例如[self.view addSubview:view];
注意點(2): masequalTo 和 equalTo 區別:masequalTo 比equalTo多了類型轉換操做,通常來講,大多數時候兩個方法都是 通用的,可是對於數值元素使用mas_equalTo。對於對象或是多個屬性的處理,使用equalTo。特別是多個屬性時,必須使用equalTo,例如 make.left.and.right.equalTo(self.view);
另外,在Masonry中,and,with都沒有具體操做,僅僅是爲了提升程序的可讀性
七、SizeClass:在自適應佈局中,蘋果提出了Size Class(尺寸類型)的概念,用於在概念上表示水平或垂直方向的大小。共有3種類型尺寸,大的稱之爲Regular(標準尺寸類型,簡記爲+),小的稱之爲Compact(緊湊尺寸類型,簡記爲-),任意的稱之爲Any(Regular和Compact任意)。全部的iPad無論什麼方位都是[+, +]
全部的iPhone在豎屏時都是[-, +]
在橫屏時只有iPhone 6 Plus和6s Plus是[+, -],其他的iPhone都是[-, -]。當設計人員給出了不一樣設備上的界面設計稿以後,做爲開發人員的咱們應該首先總結出最通用的自動佈局方案,將其做爲任意尺寸類型的自動佈局(基類);把差別化的佈局放在某個特定尺寸類型的自動佈局(子類)。使用的時候必定要確保Use Auto Layout複選框和Use Size Classes複選框都已選中
八、利用宏進行適配:咱們觀察到從iphone5之後全部的蘋果機型,儘管屏幕大小不盡相同,可是屏幕的寬高比確是一致的,這就爲咱們進行適配又找到了一絲線索,實際上,咱們能夠利用寬高比相等這點,以某種機型爲基礎,好比5s,在5s上的視圖,搬到iphone6以上時,就將這個視圖的大小進行等比擴大,同理,在4和4s上時,將這個視圖的大小進行等比縮小,這樣視圖在不一樣大小屏幕上,只是大小不同而已,看起來卻都是和諧的,由於視圖相對於屏幕大小的比例是同樣的 。用法案例:pch是一個全局文件,在pch文件導入的頭文件和宏,對全局類都是有效的,咱們在pch文件裏定義兩個帶參數的宏,#define Width(x) [UIScreen mainScreen].bounds.size.width/320*x