1. 程式人生 > >IOS純程式碼實現AutoLayout適配多螢幕

IOS純程式碼實現AutoLayout適配多螢幕

關於AutoLayout,最早從iOS6開始引入使用。

主要功能是使用約束,對檢視進行相對佈局,以適應不同屏尺的變換。

網上大量的資料都在介紹xib和storyboard,如何使用AutoLayout,說純程式碼使用AutoLayout進行UI佈局的越來越少。對於我這個習慣了程式碼UI佈局的人,寫個備忘:

AutoLayout是什麼?
使用一句Apple的官方定義的話

    AutoLayout是一種基於約束的,描述性的佈局系統。 Auto Layout Is a Constraint-Based, Descriptive Layout System.

關鍵詞:

基於約束 - 和以往定義frame的位置和尺寸不同,AutoLayout的位置確定是以所謂相對位置的約束來定義的,比如x座標為superView的中心,y座標為螢幕底部上方10畫素等
描述性 - 約束的定義和各個view的關係使用接近自然語言或者視覺化語言(稍後會提到)的方法來進行描述
佈局系統 - 即字面意思,用來負責介面的各個元素的位置。

總而言之,AutoLayout為開發者提供了一種不同於傳統對於UI元素位置指定的佈局方法。以前,不論是在IB裡拖放,還是在程式碼中寫,每個UIView都會有自己的frame屬性,來定義其在當前檢視中的位置和尺寸。使用AutoLayout的話,就變為了使用約束條件來定義view的位置和尺寸。這樣的最大好處是一舉解決了不同解析度和螢幕尺寸下view的適配問題,另外也簡化了旋轉時view的位置的定義,原來在底部之上10畫素居中的view,不論在旋轉螢幕或是更換裝置(iPad或者iPhone5或者以後可能出現的mini iPad)的時候,始終還在底部之上10畫素居中的位置,不會發生變化。 總結

    使用約束條件來描述佈局,view的frame會依據這些約束來進行計算 Describe the layout with constraints, and frames are calculated automatically.

AutoLayout和Autoresizing Mask的區別
Autoresizing Mask是我們的老朋友了…如果你以前一直是程式碼寫UI的話,你肯定寫過UIViewAutoresizingFlexibleWidth之類的列舉;如果你以前用IB比較多的話,一定注意到過每個view的size inspector中都有一個紅色線條的Autoresizing的指示器和相應的動畫縮放的示意圖,這就是Autoresizing Mask。在iOS6之前,關於螢幕旋轉的適配和iPhone,iPad螢幕的自動適配,基本都是由Autoresizing Mask來完成的。但是隨著大家對iOS app的要求越來越高,以及已經以及今後可能出現的多種螢幕和解析度的裝置來說,Autoresizing Mask顯得有些落伍和遲鈍了。AutoLayout可以完成所有原來Autoresizing Mask能完成的工作,同時還能夠勝任一些原來無法完成的任務,其中包括:

AutoLayout可以指定任意兩個view的相對位置,而不需要像Autoresizing Mask那樣需要兩個view在直系的view hierarchy中。
AutoLayout不必須指定相等關係的約束,它可以指定非相等約束(大於或者小於等);而Autoresizing Mask所能做的佈局只能是相等條件的。
AutoLayout可以指定約束的優先順序,計算frame時將優先按照滿足優先順序高的條件進行計算。

總結

Autoresizing Mask是AutoLayout的子集,任何可以用Autoresizing Mask完成的工作都可以用AutoLayout完成。AutoLayout還具備一些Autoresizing Mask不具備的優良特性,以幫助我們更方便地構建介面。

AutoLayout基本使用方法
Interface Builder
這部分網上大量的教程,都是說的這個

手動使用API新增約束
建立
iOS6中新加入了一個類:NSLayoutConstraint,一個形如這樣的約束

item1.attribute = multiplier ? item2.attribute + constant

對應的程式碼為
1
[NSLayoutConstraint constraintWithItem:button
2
attribute:NSLayoutAttributeBottom
3
relatedBy:NSLayoutRelationEqua
4
toItem:superview
5
attribute:NSLayoutAttributeBottom
6
multiplier:1.0
7
constant:-padding]
這對應的約束是“button的底部(y) = superview的底部 -10”。
新增
在建立約束之後,需要將其新增到作用的view上。UIView(當然NSView也一樣)加入了一個新的例項方法:

-(void)addConstraint:(NSLayoutConstraint *)constraint;

用來將約束新增到view。在新增時唯一要注意的是新增的目標view要遵循以下規則:

對於兩個同層級view之間的約束關係,新增到他們的父view上



對於兩個不同層級view之間的約束關係,新增到他們最近的共同父view上



對於有層次關係的兩個view之間的約束關係,新增到層次較高的父view上

重新整理
可以通過-setNeedsUpdateConstraints和-layoutIfNeeded兩個方法來重新整理約束的改變,使UIView重新佈局。這和CoreGraphic的-setNeedsDisplay一套東西是一樣的~
Visual Format Language 可視格式語言
UIKit團隊這次相當有愛,估計他們自己也覺得新加約束的API名字太長了,因此他們發明了一種新的方式來描述約束條件,十分有趣。這種語言是對視覺描述的一種抽象,大概過程看起來是這樣的: accept按鈕在cancel按鈕右側預設間距處

最後使用VFL(Visual Format Language)描述變成這樣:
1
[NSLayoutConstraint constraintsWithVisualFormat:@\”[cancelButton]-[acceptButton]\”
2
options:0
3
metrics:nil
4
views:viewsDictionary];
其中viewsDictionary是綁定了view的名字和物件的字典,對於這個例子可以用以下方法得到對應的字典:
1
UIButton *cancelButton = …
2
UIButton *acceptButton = …
3
viewsDictionary = NSDictionaryOfVariableBindings(cancelButton,acceptButton);
生成的字典為

{ acceptButton = “”; cancelButton = “”; }

當然,不嫌累的話自己手寫也未嘗不可。現在字典啊陣列啊寫法相對簡化了很多了,因此也不復雜。關於Objective-C的新語法,可以參考我之前的一篇WWDC 2012筆記:WWDC 2012 Session筆記——405 Modern Objective-C。 在view名字後面新增括號以及連線處的數字可以賦予表示式更多意義,以下進行一些舉例:

[cancelButton(72)]-12-[acceptButton(50)]
    取消按鈕寬72point,accept按鈕寬50point,它們之間間距12point
[wideView(>[email protected])]
    wideView寬度大於等於60point,該約束條件優先順序為700(優先順序最大值為1000,優先順序越高的約束越先被滿足)
V:[redBox][yellowBox(==redBox)]
    豎直佈局,先是一個redBox,其下方緊接一個寬度等於redBox寬度的yellowBox
H:|-[Find]-[FindNext]-[FindField(>=20)]-|
    水平佈局,Find距離父view左邊緣預設間隔寬度,之後是FindNext距離Find間隔預設寬度;再之後是寬度不小於20的FindField,它和FindNext以及父view右邊緣的間距都是預設寬度。(豎線'|‘ 表示superview的邊緣)

容易出現的錯誤
因為涉及約束問題,因此約束模型下的所有可能出現的問題這裡都會出現,具體來說包括兩種:

Ambiguous Layout 佈局不能確定
Unsatisfiable Constraints 無法滿足約束

佈局不能確定指的是給出的約束條件無法唯一確定一種佈局,也即約束條件不足,無法得到唯一的佈局結果。這種情況一般新增一些必要的約束或者調整優先順序可以解決。無法滿足約束的問題來源是有約束條件互相沖突,因此無法同時滿足,需要刪掉一些約束。兩種錯誤在出現時均會導致佈局的不穩定和錯誤,Ambiguous可以被容忍並且選擇一種可行佈局呈現在UI上,Unsatisfiable的話會無法得到UI佈局並報錯。 對於不能確定的佈局,可以通過除錯時暫停程式,在debugger中輸入

po [[UIWindow keyWindow] _autolayoutTrace]

來檢查是否存在Ambiguous Layout以及存在的位置,來幫助新增條件。另外還有一些檢查方法,來檢視view的約束和約束狀態:

[view constraintsAffectingLayoutForOrientation/Axis: NSLayoutConstraintOrientationHorizontal/Vertical]
[view hasAmbiguousLayout]
    [view exerciseAmbiguityInLayout]

佈局動畫
動畫是UI體驗的重要部分,更改佈局以後的動畫也非常關鍵。說到動畫,Core Animation又立功了..自從CA出現以後,所有的動畫效果都非常cheap,在auto layout中情況也和collection view裡一樣,很簡單(可以參考WWDC 2012 Session筆記——219 Advanced Collection Views and Building Custom Layouts),只需要把layoutIfNeeded放到animation block中即可~
1
[UIView animateWithDuration:0.5 animations:^{
2
[view layoutIfNeeded];
3
}];

部分程式碼
純淨程式碼UI正常佈局後,新增autolayout就可以了,調整相當方便
這是一段水平居中,垂直並列的4個按鈕 佈局程式碼
setTranslatesAutoresizingMaskIntoConstraints 是為no,開啟AutoLayou.

//-----autoLayout

[_btn_1 setTranslatesAutoresizingMaskIntoConstraints:NO];

[_btn_2 setTranslatesAutoresizingMaskIntoConstraints:NO];

[_btn_3 setTranslatesAutoresizingMaskIntoConstraints:NO];

[_btn_4 setTranslatesAutoresizingMaskIntoConstraints:NO];



CGSize winSize = [[iHappySDKSingle shareSingle] getScreenSize];

CGFloat tpo = _btn_1.frame.origin.y;

CGFloat hpod = _btn_1.frame.origin.x;

CGFloat btnH = _btn_1.frame.size.height;

CGFloat vpod = winSize.width*0.15-btnH;



NSNumber* tp = [NSNumber numberWithFloat:tpo];

NSNumber* hd = [NSNumber numberWithFloat:hpod];

NSNumber* vd = [NSNumber numberWithFloat:vpod];

NSNumber* bh = [NSNumber numberWithFloat:btnH];

NSNumber* btm = [NSNumber numberWithFloat:vpod*2];



NSDictionary *dict1 = NSDictionaryOfVariableBindings(_btn_1,_btn_2,_btn_3,_btn_4);

NSDictionary *metrics = @{@"hPadding":hd,@"vPadding":vd,@"top":tp,@"btm":btm,@"btnHeight":bh};

NSString *vfl1 = @"|-hPadding-[_btn_1]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1

options:0

metrics:metrics

views:dict1]];

NSString *vfl2 = @"|-hPadding-[_btn_2]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl2

options:0

metrics:metrics

views:dict1]];

NSString *vfl3 = @"|-hPadding-[_btn_3]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl3

options:0

metrics:metrics

views:dict1]];

NSString *vfl4 = @"|-hPadding-[_btn_4]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl4

options:0

metrics:metrics

views:dict1]];

NSString *vfl5 = @"V:|-(<=top)-[_btn_1(btnHeight)]-vPadding-[_btn_2(btnHeight)]-vPadding-[_btn_3(btnHeight)]-vPadding-[_btn_4(btnHeight)]-(>=btm)-|";

if (_btn_1.hidden) {

    vfl5 = @"V:|-(<=top)-[_btn_2(btnHeight)]-vPadding-[_btn_3(btnHeight)]-vPadding-[_btn_4(btnHeight)]-(>=btm)-|";

}

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl5

options:0

metrics:metrics

views:dict1]];

純淨程式碼UI正常佈局後,增加一個函式,進行自動佈局
水平居中佈局:NSLayoutAttributeCenterX
垂直居中佈局:NSLayoutAttributeCenterY
以及後面的佈局切換動畫。

  • (void)setAutoLayoutForKuang:(UIView*)imgv

{

UIView * view = self;

[imgv setTranslatesAutoresizingMaskIntoConstraints:NO];

NSDictionary *dict1 = NSDictionaryOfVariableBindings(imgv);

NSDictionary *metrics = @{@"width":[NSNumber numberWithFloat:imgv.frame.size.width],

                          @"height":[NSNumber numberWithFloat:imgv.frame.size.height],

                          @"top":[NSNumber numberWithFloat:imgv.frame.origin.y]

                          };

NSString *vfl1 = @"[imgv(width)]";

[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1

options:0

metrics:metrics

views:dict1]];

NSString *vfl2 = @"V:[imgv(height)]";

[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl2

options:0

metrics:metrics

views:dict1]];

[view addConstraint:[NSLayoutConstraint constraintWithItem:imgv attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];

[view addConstraint:[NSLayoutConstraint constraintWithItem:imgv attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];

//animation

[UIView animateWithDuration:0.25 animations:^{

    [imgv layoutIfNeeded];

}];

}

網上相關文章:

1、AutoLayout(自動佈局)入門 推薦
2、Autolayout及VFL經驗分享 此文包含有一個demo(快速訪問下載) 推薦
3、iOS 6 Auto Layout NSLayoutConstraint 介面佈局

相關推薦

IOS程式碼實現AutoLayout螢幕

關於AutoLayout,最早從iOS6開始引入使用。 主要功能是使用約束,對檢視進行相對佈局,以適應不同屏尺的變換。 網上大量的資料都在介紹xib和storyboard,如何使用AutoLayout,說純程式碼使用AutoLayout進行UI佈局的越來越少

iOS 程式碼iPhone6,6+

首先說下讓自己的程式支援iPhone6和6+,第一種使用官方提供的launch screen.xib,這個直接看官方文件即可,這裡不再多述;第二種方法是和之前iPhone5的類似,比較簡單,為iPhone6和6+新增兩張特殊的png iPhone6:命名:[email&

iOS學習筆記12--程式碼實現原生UITabBarController,手勢滑動切換檢視

下面提供一個思路,具體程式碼最後提供例子。 1、新建一個類,繼承自UITabBarController。在專案例子中對應: TabBarViewController 2、建立多個子檢視,具體個數看需求而定。專案例子中對應: FirstViewContro

AutoLayout自定義tableViewCell --- Masonry + UITableView+FDTemplateLayoutCell 程式碼實現

AutoLayout自定義tableViewCell — Masonry + UITableView+FDTemplateLayoutCell 純程式碼實現 在被frame虐的體無完膚的樓主,在新專案開始的時候毅然決定使用Autolayout來實現專案的絕

iOS調用系統通訊錄(iOS9、iOS10)(轉載)

tpi determine getc dispatch play bridge osi n) text2 由於系統的通訊錄在iOS9的時候提供了新的api,所以我們2種框架都使用。首先我們要導入框架: /// iOS 9前的框架 #import <Address

【轉】web app變革之rem(手機屏幕實現

理想 那種 內嵌 自己的 大屏幕 block 行業 尺寸 是我 以往web移動適配,常規寫法是:media only screen @media only screen and (min-device-width: 320px){ //針對iP

Android劉海屏庫NotchFit一鍵完美實現劉海!!!

    NotchFit是Android端的劉海適配庫。該庫使用簡單,劉海蔘數智慧判斷,使開發者無需再考慮手機系統和製造廠商的差異,一鍵智慧劉海蔘數回撥。你值得擁有!!! 下面對全屏劉海適配做個簡要說明: 程式碼實現方式: NotchFit.fit(this, Not

相容iOS 10 _升級xcode8_(一)

轉自:http://www.jianshu.com/p/0cc7aad638d9 字數2646  閱讀18217  評論103  喜歡515 1.Notification(通知) 自從N

相容iOS 10 _升級xcode8_(三)

轉自:http://www.cnblogs.com/godlovexq/p/5885212.html 1推送  xcode 升級到8之後很多人的推送接收不到了.獲取不到token了 一朋友搞了一小時沒找到原因. 只因看下圖吧....我發覺xcode 我不開啟他也能收到通知.

相容iOS 10 _升級xcode8_(二)

本文轉自:http://www.2cto.com/kf/201609/547169.html 一.工程改動 1. 工程配置 直接使用Xcode8Beta開啟工程後,你會發現你的provisonfile配置出了問題,這是由於在Xcode8中對工程配置有了一些小的改動。 在Xc

相容iOS 10 _升級xcode8_(四)

1.Notification(通知) 自從 Notification 被引入之後,蘋果就不斷的更新優化,但這些更新優化只是小打小鬧,直至現在iOS 10開始真正的進行大改重構,這讓開發者也體會到 UserNotifications 的易用,功能也

C# GDI繪製儀表盤(程式碼實現

純程式碼實現GDI繪製儀表盤,效果在程式碼下面。public partial class HalfDashboardUc : UserControl { /// <summary> /// 儀表盤背景圖片 /// </summar

FE之DR之線性降維:LDA&PCA演算法相關論文、主要思路、關鍵步驟、程式碼實現等相關圖之詳細攻略

FE之DR之LDA:LDA演算法相關論文、主要思路、關鍵步驟、程式碼實現等相關配圖之詳細攻略 LDA LDA演算法相關論文、主要思路 1、LDA的缺點:強依賴均值。以下是LDA搞不定的四種情況   LDA演算法關鍵步驟 1、LDA演算法推導 &n

ML之Clustering之普聚類演算法:普聚類演算法的相關論文、主要思路、關鍵步驟、程式碼實現等相關圖之詳細攻略

ML之Clustering之普聚類演算法:普聚類演算法的相關論文、主要思路、關鍵步驟、程式碼實現等相關配圖之詳細攻略   普聚類演算法的相關論文 1、論文推薦 Clustering by fast search and find of density peak.

非外掛程式碼實現WordPress增加瀏覽次數統計及顯示評論數

非外掛純程式碼實現WordPress增加瀏覽次數統計及顯示評論數 wp站點有一段時間了,之前優化了首頁載入速度,又感覺缺少瀏覽次數的統計很不方便,於是便折騰了一下,對瀏覽

Android AutoLayout問題解決方案

本文在鴻洋的AutoLayout上做了修改,原文地址https://blog.csdn.net/lmj623565791/article/details/49990941。 AutoLayout原本的使用方法和思想沒有發生變化,主要針對適配中出現的問題進行修改。如果你遇到了下邊的一些問題,可

Android 用程式碼實現複雜介面

                在開發Android應用時有時會遇到純程式碼實現複雜介面的需求,本文通過例項來演示,希望能對大家有所幫助介面截圖:XML佈局檔案:<?xml version="1.0" encoding="utf-8"?><ScrollView     xmlns:andro

關於Autolayout4S的比較好的方案

一般設計出圖是按照6s來出圖的。 那4s怎麼辦?只要是UI排滿一點。再加上出場率奇高的鍵盤。分分鐘4s裝不下了。 必須上Scrollview啊。 Autolayout 怎麼上Scrollview呢。優先選擇在xib上操作。你不會想寫程式碼的!!! 先單獨拉一個view出來,然後

蘋果ios應用的網路ipv6攻略

蘋果要求在2016年6月1日後新的app必須支援ipv6網路, 技術發展靠蘋果果然沒錯, 但開發者還是要開始忙起來了這裡介紹下Unity3D的適配的一些經驗 基本注意點 ios ipv6適配無需修改伺服器, 也就是說, 如果你的伺服器依然是ipv4的也是可以使用的 蘋果的適配方案是將ipv4的地址轉

Objective-C iOS程式碼佈局 一堆程式碼可以放這裡!

前言: 最近寫的文章都是創業類,好吧,今天好好寫寫技術類的文章! 不過分享的不是IOS相關的文章,畢竟這幾天在速成IOS,看的是objective-c,由於速成的很快,好累! 好在現在基本已經入了點門道了,這才看的懂新人的程式碼,才能提前感受程式碼危機。 對於IOS的速成口訣,回頭再分享,今天先分享一