自iphone4s以后,蘋果先后推出了iphone5、iphone5s、iphone6、iphone6plus、iphone6s、iphone6splus這些新的機(jī)型,它們的屏幕大小各有所異,從此給我們開(kāi)發(fā)者留下了一個(gè)蛋疼的問(wèn)題:屏幕適配。
1、顯示坐標(biāo)定位方式:在4和4s的時(shí)代,我們采用顯示坐標(biāo)定位方式設(shè)置一個(gè)視圖的坐標(biāo),比如view.frame ?= CGRectMake(20, 70, 160, 160);在當(dāng)時(shí)顯然是沒(méi)有任何問(wèn)題的,因?yàn)槭謾C(jī)屏幕的大小是固定的,屏寬320像素,屏高480像素。
2、autoresizingMask:自iphone5以后,手機(jī)屏幕的高度變成了568像素,有時(shí)候我們定義的視圖在iphone4和4s上運(yùn)行起來(lái)位置擺放正常,但是在5和5s上就不那么和諧了,這就對(duì)開(kāi)發(fā)者提出了適配的任務(wù):如何讓視圖在不同大小的屏幕上恰到好處的展現(xiàn)出來(lái)呢?其實(shí)蘋果最先推出來(lái)的跟適配沾邊的技術(shù)autoresizingMask。autoresizingMask能給出子視圖相對(duì)于父親視圖的對(duì)齊方式與縮放系數(shù),當(dāng)父視圖發(fā)生變化時(shí),通過(guò)每個(gè)視圖autoresizingMask即可自動(dòng)得出新的位置。使用步驟是
第一步:設(shè)置父視圖的autoresizesSubviews屬性為YES,superView.autoresizesSubviews = YES;,否則后面的autoresizingMask都將失效。
第二步:設(shè)置子視圖的autoresizingMask, subView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin。
這里面的autoresizingMask屬性是一個(gè)枚舉值,枚舉值各項(xiàng)含義如下:UIViewAutoresizingNone:就是不自動(dòng)調(diào)整。
UIViewAutoresizingFlexibleLeftMargin: 自動(dòng)調(diào)整與superView左邊的距離,保證與superView右邊的距離不變。
UIViewAutoresizingFlexibleRightMargin: 自動(dòng)調(diào)整與superView的右邊距離,保證與superView左邊的距離不變。
UIViewAutoresizingFlexibleTopMargin: 自動(dòng)調(diào)整與superView頂部的距離,保證與superView底部的距離不變。
UIViewAutoresizingFlexibleBottomMargin: 自動(dòng)調(diào)整與superView底部的距離,也就是說(shuō),與superView頂部的距離不變。
UIViewAutoresizingFlexibleWidth: 自動(dòng)調(diào)整自己的寬度,保證與superView左邊和右邊的距離不變。
UIViewAutoresizingFlexibleHeight: 自動(dòng)調(diào)整自己的高度,保證與superView頂部和底部的距離不變。
UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin: 自動(dòng)調(diào)整與superView左邊的距離,保證與左邊的距離和右邊的距離和原來(lái)距左邊和右邊的距離的比例不變。比如原來(lái)距離為20,30,調(diào)整后的距離應(yīng)為68,102,即68/20=102/30。遺憾的是這些技術(shù)的缺陷也十分明顯,是一個(gè)不成熟的技術(shù),原因有兩點(diǎn):
(1)、autoresizingMask縮放比例是UIKit內(nèi)部計(jì)算的,開(kāi)發(fā)者無(wú)法指定縮放比例的精確值。
(2)、變化規(guī)則只能基于父子視圖,無(wú)法解決兄弟視圖之間的位置關(guān)系。所以在iOS6推出自動(dòng)布局(Auto Layout)技術(shù)后,autoresizingMask成為一項(xiàng)雞肋技術(shù)??梢杂檬持疅o(wú)味,棄之可惜來(lái)形容。
3、AutoLayout-可視化編程自動(dòng)布局:關(guān)于AutoLayout技術(shù)有可視化編程和純代碼兩種方式,本處先講解可視化的方式。自動(dòng)布局是對(duì)autoresizingMask的進(jìn)一步改進(jìn),它允許開(kāi)發(fā)者在界面上的任意兩個(gè)視圖之間建立精確的線性變化規(guī)則。所謂線性變化就是數(shù)學(xué)中的一次函數(shù),即: y = m*x + c ?其中x和y是界面中任意兩個(gè)視圖的某個(gè)布局屬性,m為比例系數(shù),c為常量。在storyboard或者xib中設(shè)置約束的方案有三種:
(1)在設(shè)計(jì)器中長(zhǎng)按Control鍵拖動(dòng)控件。
(2)在左側(cè)文檔結(jié)構(gòu)窗口中長(zhǎng)按Control鍵拖動(dòng)控件。
(3)點(diǎn)擊設(shè)計(jì)器底部圖標(biāo),pin設(shè)置距離約束、固定寬高約束、等高等寬和寬高比約束,align設(shè)置對(duì)齊約束。
注意:方案(1)和(2)中同時(shí)按住Shift鍵與Option鍵可以同時(shí)設(shè)置多個(gè)約束。前面我們提到了一個(gè)重要概念就是約束,約束有很多種類,現(xiàn)列出如下:Leading :距離另一視圖左邊距離的約束
Trailing :距離另一視圖左邊距離的約束
Top :距離另一視圖頂部距離的約束
Bottom :距離另一視圖底部距離的約束
Width:單個(gè)視圖固定寬度的約束
Height:單個(gè)視圖固定高度的約束
Equal Widths:兩個(gè)視圖等寬的約束
Equal Heights:兩個(gè)視圖等高的約束
Aspect Ratio:單個(gè)視圖固定寬高比的約束
Horizontally in Container:固定在父視圖水平方向中線的約束
Vertically in Container:固定在父視圖垂直方向中線的約束
Vertically in Container:固定在非父視圖水平方向中線的約束
Horizontall Centers:固定在非父視圖垂直方向中線的約束
Vertical Spacing:兩個(gè)視圖垂直方向距離的約束
Horizontally Spacing:兩個(gè)視圖水平方向距離的約束
align Top to:頂部對(duì)齊約束
align Bottom to:底部對(duì)齊約束
align Leading to:左邊對(duì)齊約束
align Trailing to:右邊對(duì)齊約束
還有一些選項(xiàng)幾乎是我們必用的,含義列出如下:Update Frames:更新坐標(biāo)以適應(yīng)約束
Update Constraints:更新約束以適應(yīng)坐標(biāo)
Add Constraints:添加約束
Constain to margins:如果你點(diǎn)了constrain to margins,左右會(huì)有8個(gè)點(diǎn)的空擋,從8個(gè)點(diǎn)后開(kāi)始計(jì)算約束,而沒(méi)有點(diǎn)時(shí),已屏幕的0點(diǎn)開(kāi)始計(jì)算。建議取消勾選。
我們可以在畫(huà)布的右邊查看每個(gè)約束的詳情,我們?cè)谇懊嬷v的y相對(duì)于x發(fā)生變化的公式 y = m*x + c ?在這里得到了呼應(yīng),F(xiàn)irst Item對(duì)應(yīng)公式中的y,表示因變量
Relation對(duì)應(yīng)公式中的=,表示相等關(guān)系,這里顯示的是Equal即相等
Second Item對(duì)應(yīng)公式中的x,表示自變量
Multiplier對(duì)應(yīng)公示中的m,表示縮放比例系數(shù)
Constant對(duì)應(yīng)公示中的c,表示偏移常量
點(diǎn)擊First Item或者Second Item下拉菜單,選擇Reverse First And Second Item即可交換雙方的位置。
得反函數(shù):x = 1/m * y - c/m
值得一提的是約束既不能少,少了不能確定視圖位置,也不能多,多了會(huì)沖突。
另外,某些用來(lái)展現(xiàn)內(nèi)容的用戶控件,例如文本控件UILabel、按鈕UIButton、圖片視圖UIImageView等,它們具有自身內(nèi)容尺寸(Intrinsic Content Size),此類用戶控件會(huì)根據(jù)自身內(nèi)容尺寸添加布局約束。也就是說(shuō),如果開(kāi)發(fā)者沒(méi)有顯式給出其寬度或者高度約束,則其自動(dòng)添加的自身內(nèi)容約束將會(huì)起作用。
4、AutoLayout-代碼自動(dòng)布局:并非所有的情況都能用IB來(lái)解決,比如定義諸如某視圖的高度等于另一個(gè)視圖的寬度這樣的約束,通過(guò)代碼構(gòu)建自動(dòng)布局約束是最基礎(chǔ)的,也是最靈活的方式。缺點(diǎn)是代碼很冗長(zhǎng)。每一個(gè)布局約束是一個(gè)NSLayoutConstraint實(shí)例,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;
每一個(gè)布局約束就是一個(gè)明確的線性變化規(guī)則,在數(shù)學(xué)上是以一次函數(shù)的形式表示,即:
y = m * x + c
每個(gè)約束就對(duì)應(yīng)如下關(guān)系:
firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant
我們可以調(diào)用NSLayoutConstraint類的constraintWithItem:…方法,傳入所有需要的參數(shù)構(gòu)造一個(gè)新的約束。
(1)、firstItem與secondItem分別是界面中受約束的視圖與被參照的視圖,他們不一定非得是兄弟關(guān)系或者父子關(guān)系,只要是他們有著共同的祖先視圖即可。
(2)、firstAttribute與secondAttribute分別是firstItem與secondItem的某個(gè)布局屬性(NSLayoutAttribute),其中NSLayoutAttributeNotAnAttribute當(dāng)我們需要為某個(gè)視圖精確指定一個(gè)寬度或者高度值時(shí),這時(shí)候secondItem為nil,secondAttribute為 ? ?NSLayoutAttributeNotAnAttribute。
(3)、relation定義了布局關(guān)系
typedef NS_ENUM(NSInteger, NSLayoutRelation)
{
NSLayoutRelationLessThanOrEqual = -1,
NSLayoutRelationEqual = 0,
NSLayoutRelationGreaterThanOrEqual = 1,
};
布局關(guān)系可以是相等、大于等于或者小于等于。
(4)、multiplier即比例系數(shù)。
(5)、constant即常量
使用案例如下:
第一步:默認(rèn)的autoresizingMask代碼和自動(dòng)布局約束會(huì)沖突,設(shè)置為no,程序只有自動(dòng)布局約束代碼,避免沖突。
logoView.translatesAutoresizingMaskIntoConstraints = NO;
第二步:設(shè)置約束,為了唯一確定視圖的位置,我添加了四個(gè)約束
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;
5、VFL:Visual Format Language,可視化格式語(yǔ)言,能直觀又簡(jiǎn)單的創(chuàng)建約束,缺點(diǎn)是不能表達(dá)所有的約束。用法舉例:
第一步:autoresizingMask設(shè)置為NO。
logoView.translatesAutoresizingMaskIntoConstraints = NO;
第二步:使用VFL添加約束
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-80-[logoView]" options:NSLayoutFormatAlignAllLeft metrics:nil views:@{@"logoView":logoView}]];
語(yǔ)法是這樣的,H表示橫向
V表示豎向
| 表示父視圖
[視圖名字]表示視圖
-表示 間距
-間距- 表示設(shè)置其他間距
[視圖名字(設(shè)置視圖的寬高)]
上面constraintsWithVisualFormat方法的參數(shù)含義如下
VisualFormat:VFL語(yǔ)句
options:基于哪一個(gè)選項(xiàng)(基于哪個(gè)方向去計(jì)算布局)
metrics:綁定數(shù)值(NSNumber) 與字符串
views:綁定視圖 與字符串
NSDictionaryOfVariableBindings:是一個(gè)宏,方便我們構(gòu)建字典,NSDictionaryOfVariableBindings(logoView)就等于@{@”logoView”: logoView}。
另外需要注意一些精簡(jiǎn)寫法(1)、省略:也就是說(shuō)如果間距為0則不用明確寫出,所以@”H:|-0-[logoView]-0-|”可以精簡(jiǎn)為@”H:|[logoView]|”
(2)、合并:例如@"H:[password]-20-[tfPassword]"和@"[tfPassword]-40-|"可以合并為@"H:[password]-20-[tfPassword]-40-|"
6、Masonry:是第三方庫(kù),讓自動(dòng)布局的寫法更加簡(jiǎn)便,是現(xiàn)在程序員使用較為普遍的一種方案。用法案例如下:
第一步:聲明為_(kāi)_weak,因?yàn)槲覀兒竺嬉褂胋lock,以防內(nèi)存問(wèn)題。
__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用法比較簡(jiǎn)單,需要注意的地方主要有兩點(diǎn)
注意點(diǎn)(1): 使用 mas_makeConstraints方法的元素必須事先添加到父元素的中,例如[self.view addSubview:view];
注意點(diǎn)(2): masequalTo 和 equalTo 區(qū)別:masequalTo 比equalTo多了類型轉(zhuǎn)換操作,一般來(lái)說(shuō),大多數(shù)時(shí)候兩個(gè)方法都是 通用的,但是對(duì)于數(shù)值元素使用mas_equalTo。對(duì)于對(duì)象或是多個(gè)屬性的處理,使用equalTo。特別是多個(gè)屬性時(shí),必須使用equalTo,例如 make.left.and.right.equalTo(self.view);
另外,在Masonry中,and,with都沒(méi)有具體操作,僅僅是為了提高程序的可讀性
7、SizeClass:在自適應(yīng)布局中,蘋果提出了Size Class(尺寸類型)的概念,用于在概念上表示水平或垂直方向的大小。共有3種類型尺寸,大的稱之為Regular(標(biāo)準(zhǔn)尺寸類型,簡(jiǎn)記為+),小的稱之為Compact(緊湊尺寸類型,簡(jiǎn)記為-),任意的稱之為Any(Regular和Compact任意)。所有的iPad不管什么方位都是[+, +]
所有的iPhone在豎屏?xí)r都是[-, +]
在橫屏?xí)r只有iPhone 6 Plus和6s Plus是[+, -],其余的iPhone都是[-, -]。當(dāng)設(shè)計(jì)人員給出了不同設(shè)備上的界面設(shè)計(jì)稿之后,作為開(kāi)發(fā)人員的我們應(yīng)該首先總結(jié)出最通用的自動(dòng)布局方案,將其作為任意尺寸類型的自動(dòng)布局(基類);把差異化的布局放在某個(gè)特定尺寸類型的自動(dòng)布局(子類)。使用的時(shí)候一定要確保Use Auto Layout復(fù)選框和Use Size Classes復(fù)選框都已選中
8、利用宏進(jìn)行適配:我們觀察到從iphone5以后所有的蘋果機(jī)型,盡管屏幕大小不盡相同,但是屏幕的寬高比確是一致的,這就為我們進(jìn)行適配又找到了一絲線索,實(shí)際上,我們可以利用寬高比相等這點(diǎn),以某種機(jī)型為基礎(chǔ),比如5s,在5s上的視圖,搬到iphone6以上時(shí),就將這個(gè)視圖的大小進(jìn)行等比擴(kuò)大,同理,在4和4s上時(shí),將這個(gè)視圖的大小進(jìn)行等比縮小,這樣視圖在不同大小屏幕上,只是大小不一樣而已,看起來(lái)卻都是和諧的,因?yàn)橐晥D相對(duì)于屏幕大小的比例是一樣的 。用法案例:pch是一個(gè)全局文件,在pch文件導(dǎo)入的頭文件和宏,對(duì)全局類都是有效的,我們?cè)趐ch文件里定義兩個(gè)帶參數(shù)的宏,#define Width(x) [UIScreen mainScreen].bounds.size.width/320*x
#define Height(y) [UIScreen mainScreen].bounds.size.height/568*y,這樣我們?cè)诳刂破髦性O(shè)置一個(gè)視圖的位置可以這么寫,bigView = [[UIView alloc] initWithFrame:CGRectMake(Width(20), Height (70), Width(160), Height(160))];當(dāng)然你可以重寫CGRectMake方法,這樣使用的時(shí)候會(huì)更加的方便,筆者將這個(gè)最后的小任務(wù)就交給大家思考下吧!