關(guān)于iOS自動(dòng)適配的那點(diǎn)事

自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ù)就交給大家思考下吧!

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

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

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