Auto Layout 使用心得

Auto Layout 使用心得(一)—— 初體驗

本系列文章將從一個慢慢摸索中的新手的角度介紹 Auto Layout,講述我在這兩個月的學習中對它一點一滴的感覺,最終目的是讓你們在閱讀完以後可以本身上手使用,並完成絕大多數簡單的佈局約束。html


此係列文章代碼倉庫在 https://github.com/johnlui/AutoLayout ,有不明白的地方能夠參考個人 Auto Layout 設置哦,下載到本地打開就能夠了。ios


簡介

Auto Layout 是蘋果在 Xcode 5 (iOS 6) 中新引入的佈局方式,旨在解決 3.5 寸和 4 寸屏幕的適配問題。屏幕適配工做在 iPhone 6 及 plus 發佈之後變得更加劇要,並且以往的「笨辦法」的工做量大幅增長,因此不少人開始學習使用 Auto Layout 技術。c++

初體驗

0. 開發環境

本系列文章的開發環境爲:git

  • OS X 10.10.3
    github

  • Xcode Version 6.3.1 (6D1002)編程

1. 新建應用

新建一個 Single View Application,命名爲 AutoLayout,以下:swift

Image

點擊選中 Main.storyboard,右側內容以下:app

Image

一、2 兩個按鈕將會在將來的開發中產生巨大的做用,他們將擁有本系列文章的全局名稱:按鈕1,按鈕2。請先記下他們的位置。ide

2. 直接上手,開始使用

這也是我對學習新的軟件編程技術的基本學習方法:有一個具體客觀驅動的目標,例如作一個真正要給客戶用的軟件,而不是「爲了學習新技術提升本身」這類僞目標。函數

讓咱們直接上手:繪製一個距離左右邊都有必定距離、固定高度、垂直居中的按鈕,叫「Swift on iOS」。

1. 第一步,從右側拖過來一個按鈕,置於頁面最中間。會有參考線出現,這一步很容易:

Image

2. 選中這個 button,將按鈕背景色和前景色進行以下設置:  

Image

3. 將按鈕左側邊界往左拖動直到自動吸附,留下必定的距離。右側進行一樣操做:  

Image

4. 選中這個 button,修改文字爲 Swift on iOS:  

Image

5. 選中這個 button,點擊 按鈕2 ,選擇這一項:  

Image

這時候 button 周圍會出現一些藍色的線條,這些就是 Auto Layout 的約束項。

3. 大功告成,查看效果

3.5:  

Image

4:  

Image

4.7:  

Image

5.5:  

Image

4. 分析

選中這個 button,在右側查看自動生成的約束項:  

Image

只有三項,這三項的意思分別是:和父視圖縱向居中對齊、右側和父視圖對齊、左側和父視圖對齊。

咱們很容易就能理解這樣能夠定位一個按鈕,可是總感受少了點什麼。實際上這三個自動生成的約束項並不能描述一個 button 的位置,由於少了一個關鍵的屬性:button 的高度。之後咱們會詳細地討論。

5. 核心思想

本質分析

Auto Layout 的本質是依靠 某幾項約束條件 來達到對某一個元素的定位。咱們能夠在某個地方只使用一個約束,以達到一個小目的,例如防止內容遮蓋、防止邊界溢出等。但個人最佳實踐證實,若是把頁面上每個元素的位置都用 Auto Layout 進行 「嚴格約束」 的話,那麼 Auto Layout 能夠幫咱們省去很是多的計算 frame 的代碼。

「嚴格約束」 是什麼?

簡單來講,嚴格約束就是對某一個元素的絕對定位,讓它在任一屏幕尺寸下都有着惟一的位置。這裏的絕對定位不是定死的位置,而是對一個元素 完善的約束條件

讓咱們看圖說話:

Image


  1. 咱們要在一個直角座標系裏描述一個矩形。

  2. 那麼只須要指定這個矩形的位置和大小。

  3. 那麼只要給出上圖中的四個值便可:到左邊界的距離,到上邊界的距離,寬度,高度。

  4. 這四個約束是最簡單的狀況。在對一個元素進行嚴格約束時,請直接在腦中構建這個元素,而且加上幾條約束條件,若是他沒法縮放和動彈,那麼嚴格約束就是成功的!

  5. 必須牢記,使用 Auto Layout 時最重要的是:對頁面上每個元素都進行嚴格約束,不嚴格的約束是萬惡之源


Auto Layout 使用心得(二)—— 實現三等分


此係列文章代碼倉庫在 https://github.com/johnlui/AutoLayout ,有不明白的地方能夠參考個人 Auto Layout 設置哦,下載到本地打開就能夠了。


準備

上一篇文章中,咱們共同進行了 Auto Layout 的初體驗,在本篇咱們將一塊兒嘗試用 Auto Layout 實現三等分。

Auto Layout 的本質原理

Auto Layout 的本質是用一些約束條件對元素進行約束,從而讓他們顯示在咱們想讓他們顯示的地方。

約束主要分爲如下幾種(歡迎補充):

  1. 相對於父 view 的約束。如:距離上邊距 10,左邊距 10。

  2. 相對於前一個元素的約束。如:距離上一個元素 20,距離左邊的元素 5 等。

  3. 對齊類約束。如:跟父 view 左對齊,跟上一個元素居中對齊等。

  4. 相等約束。如:跟父 view 等寬。

三等分設計思路

許多人剛開始接觸 Auto Layout,可能會覺得它只能實現上面的一、2功能,其實後面三、4兩個功能纔是強大、特別的地方。接下來咱們將嘗試設計橫向三等分:

  1. 第一個元素距離左邊必定距離。

  2. 最後一個元素距離右邊必定距離。

  3. 三者高度恆定,寬度相等。(此處咱們設置爲高度恆定(height 屬性),若是你須要的是固定長寬比,則須要設定 Aspect Ratio 屬性)

  4. 1和二、2和3的橫向間距固定。

乾貨,實現過程的動圖:

動圖

運行結果

4 寸:

Image

4.7 寸:

Image

縱向三等分實現方式相似,你們能夠本身嘗試一下哦~


Auto Layout 使用心得(三)—— 自定義 cell 並使用 Auto Layout


此係列文章代碼倉庫在 https://github.com/johnlui/AutoLayout ,有不明白的地方能夠參考個人 Auto Layout 設置哦,下載到本地打開就能夠了。


簡介

本篇中咱們將嘗試自定義一個 UITableViewCell,並使用 Auto Layout 對其進行約束。

自定義 cell 基礎

在前面的項目中,咱們採用 StoryBoard 來組織頁面,StoryBoard 能夠視爲許多個 xib 的集合,因此咱們能夠獲得兩個信息:

  1. 這個項目經過初始化主 StoryBoard 文件來展示 APP,而 UIViewController 類文件是經過 StoryBoard 文件的綁定來初始化並完成功能的。

  2. 咱們能夠建立新的 StoryBoard 文件或者新的 xib 文件來構造 UI,而後動態地加載進頁面。

建立文件

咱們能夠一次性建立 xib 文件和類的代碼文件。

新建 Cocoa Touch Class:

Image

設置和下圖相同便可:

Image


檢查成果

Image

分別選中上圖中的 一、2 兩處,檢查 3 處是否已經自動綁定爲 firstTableViewCell,若是沒有綁定,請先檢查選中的元素確實是 2,而後手動綁定便可。

完成綁定工做

切換一頁,以下圖進行 Identifier 設置:

Image


新建 Table View Controller 頁面

新建一個 Table View Controller 頁面,並把咱們以前建立的 Swift on iOS 那個按鈕的點擊事件綁定過去,咱們獲得:

Image

而後建立一個名爲 firstTableViewController 的 UITableViewController 類,建立流程跟前面基本一致。不要建立 xib。而後選中 StoryBoard 中的 Table View Controller(選中以後有藍色邊框包裹),在右側對它和 firstTableViewController 類進行綁定:

Image


調用自定義 cell

修改 firstTableViewController 類中的有效代碼以下:

import UIKit class firstTableViewController: UITableViewController {     override func viewDidLoad() {         super.viewDidLoad()         var nib = UINib(nibName: "firstTableViewCell", bundle: nil)         self.tableView.registerNib(nib, forCellReuseIdentifier: "firstTableViewCell")     }     override func didReceiveMemoryWarning() {         super.didReceiveMemoryWarning()     }     // MARK: - Table view data source     override func numberOfSectionsInTableView(tableView: UITableView) -> Int {         return 1     }     override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {         return 10     }     override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {         let cell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell", forIndexPath: indexPath) as firstTableViewCell         cell.textLabel?.text = indexPath.row.description         return cell     } }

viewDidLoad() 中添加的兩行代碼是載入 xib 的操做。最下面的三個 func 分別是定義:

  1. self.tableView 中有多少個 section

  2. 每一個 section 中分別有多少個條目

  3. 實例化每一個條目,提供內容

若是你獲得如下頁面,說明你調用自定義 cell 成功了!

Image


給自定義 cell 添加元素並使用 Auto Layout 約束

首先向 Images.xcassets 中隨意加入一張圖片。

而後在左側文件樹中選中 firstTableViewCell.xib,從右側組件庫中拖進去一個 Image View,而且在右側將其尺寸設置以下圖右側:

Image

給 ImageView 添加約束:

Image

選中該 ImageView(左箭頭所示),點擊自動 Auto Layout(右箭頭所示),便可。

給 ImageView 設置圖片:

Image

再從右側組件庫中拖入一個 UILabel,吸附到最右側,垂直居中,爲其添加自動約束,這一步再也不贅述。

在 firstTableViewCell 類中綁定 xib 中拖進去的元素

選中 firstTableViewCell.xib,切換到雙視圖,直接進行拖動綁定:

Image

綁定完成!

約束 cell 的高度

在 firstTableViewController 中添加如下方法:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {     return 50 }

給自定義的 UILabel 添加內容

修改 firstTableViewController 中如下函數爲:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {     let cell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell", forIndexPath: indexPath) as firstTableViewCell     cell.firstLabel.text = indexPath.row.description     return cell }

查看結果

4.0 寸:

Image

4.7 寸:

Image


若是你獲得以上結果,那麼恭喜你自定義 cell 並使用 Auto Layout 成功!

Auto Layout 使用心得(四)—— 22 行代碼實現拖動回彈


此係列文章代碼倉庫在 https://github.com/johnlui/AutoLayout ,有不明白的地方能夠參考個人 Auto Layout 設置哦,下載到本地打開就能夠了。


簡介

本文中,咱們將一塊兒使用 UIPanGestureRecognizer 和 Auto Layout,經過 22 行代碼實現拖動回彈效果。

搭建界面

刪除首頁中間的按鈕,添加一個 View ,設置一種背景色便於辨認,而後對其進行絕對約束:

Image

拖動一個 UIPanGestureRecognizer 到該 View 上:

Image

界面搭建完成。

屬性綁定

切換到雙向視圖,分別右鍵拖動 UIPanGestureRecognizer 和該 View 的 Top Space 的 Auto Layout 屬性到 ViewController 中綁定:

Image

而後將 UIPanGestureRecognizer 右鍵拖動綁定:

Image

編寫代碼

class ViewController: UIViewController {          var middleViewTopSpaceLayoutConstant: CGFloat!     var middleViewOriginY: CGFloat!          @IBOutlet weak var middleView: UIView!     @IBOutlet weak var middleViewTopSpaceLayout: NSLayoutConstraint!     @IBOutlet var panGesture: UIPanGestureRecognizer!     override func viewDidLoad() {         super.viewDidLoad()                  panGesture.addTarget(self, action: Selector("pan"))         middleViewTopSpaceLayoutConstant = middleViewTopSpaceLayout.constant         middleViewOriginY = middleView.frame.origin.y     }     override func didReceiveMemoryWarning() {         super.didReceiveMemoryWarning()         // Dispose of any resources that can be recreated.     }          func pan() {         if panGesture.state == UIGestureRecognizerState.Ended {             UIView.animateWithDuration(0.4, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in                 self.middleView.frame.origin.y = self.middleViewOriginY                 }, completion: { (success) -> Void in                     if success {                         self.middleViewTopSpaceLayout.constant = self.middleViewTopSpaceLayoutConstant                     }             })             return         }         let y = panGesture.translationInView(self.view).y         middleViewTopSpaceLayout.constant = middleViewTopSpaceLayoutConstant + y     } }

查看效果

Image


22 行代碼,拖動回彈效果完成!


Auto Layout 使用心得(五)—— 根據文字、圖片自動計算 UITableViewCell 高度

此係列文章代碼倉庫在 https://github.com/johnlui/AutoLayout ,有不明白的地方能夠參考個人 Auto Layout 設置哦,下載到本地打開就能夠了。

簡介

本文中,咱們將一塊兒使用 Auto Layout 技術,讓 UITableViewCell 的高度隨其內部的 UILabel 和 UIImageView 的內容自動變化。

搭建界面

恢復以前刪除的按鈕

放置一個按鈕,恢復到 firstTableViewController 的鏈接:

Image

別忘了添加約束讓他居中哦。

修改 firstTableViewCell

將 firstTableViewCell 的尺寸設置爲 600 * 81,將 logo 的尺寸設置爲 80 * 80。將 logo 的約束脩改成以下圖所示:

Image

修改 label 的尺寸和位置,添加約束以下圖:

Image

給 ViewController 增長 UINavigationController 嵌套

爲了便於返回。操做以下圖:

Image

查看結果

Image

根據 label 自動計算 firstTableViewCell 高度

選中 label,設置 lines 行數爲 0,表示不限長度自動折行:

Image

修改 label 的文字內容讓其超出一行:

import UIKit class firstTableViewController: UITableViewController {          var labelArray = Array<String>() // 用於存儲 label 文字內容     override func viewDidLoad() {         super.viewDidLoad()         var nib = UINib(nibName: "firstTableViewCell", bundle: nil)         self.tableView.registerNib(nib, forCellReuseIdentifier: "firstTableViewCell")                  // 循環生成 label 文字內容         for i in 1...10 {             var text = ""             for j in 1...i {                 text += "Auto Layout"             }             labelArray.append(text)         }     }     override func didReceiveMemoryWarning() {         super.didReceiveMemoryWarning()     }     // MARK: - Table view data source     override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {         return 50     }     override func numberOfSectionsInTableView(tableView: UITableView) -> Int {         return 1     }     override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {         return labelArray.count     }     override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {         let cell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell", forIndexPath: indexPath) as! firstTableViewCell         cell.firstLabel.text = labelArray[indexPath.row]         return cell     } }

如今到了最關鍵的時刻,驅動 UITableViewCell 適應 Label 內容:

1. 使用 estimatedHeightForRowAtIndexPath 替代 heightForRowAtIndexPath

estimatedHeightForRowAtIndexPath 是 iOS 7 推出的新 API。若是列表行數有一萬行,那麼 heightForRowAtIndexPath 就會在列表顯示以前計算一萬次,而 estimatedHeightForRowAtIndexPath 只會計算當前屏幕中顯示着的幾行,會大大提升數據量很大時候的性能。

2. 新建一個 prototypeCell 成員變量以複用,並在 viewDidLoad 中初始化

class firstTableViewController: UITableViewController {          var labelArray = Array<String>() // 用於存儲 label 文字內容          var prototypeCell: firstTableViewCell!     override func viewDidLoad() {         super.viewDidLoad()         var nib = UINib(nibName: "firstTableViewCell", bundle: nil)         self.tableView.registerNib(nib, forCellReuseIdentifier: "firstTableViewCell")                  // 初始化 prototypeCell 以便複用         prototypeCell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell") as! firstTableViewCell          ......

3. 計算出高度

override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {     let cell = prototypeCell     cell.firstLabel.text = labelArray[indexPath.row]     return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 1 }

4. 查看效果

Image

超級大坑

上面讓 firstTableViewCell 根據 label 自動計算高度的過程當中,有一個超級大坑:若是給左側 UIImageView 賦的圖片較大(大於 80px),將看到以下奇怪的結果:

Image

這只是由於圖片把 UITableViewCell 撐大了,並非咱們的計算沒有效果。

解決大坑:進攻是最好的防守!根據圖片自動計算 firstTableViewCell 高度

首先,把圖片的渲染模式改爲 Aspect Fit:

Image

給 Images.xcassets 增長三張圖片,命名爲 0、一、2,尺寸從小到大:

Image

給 cellForRowAtIndexPath 增長代碼:

if indexPath.row < 3 {     cell.logoImageView.image = UIImage(named: indexPath.row.description) }

查看效果:

Image

前兩個 cell 看起來比較正常,第三個爲何多出了那麼多空白?這就是使用 Auto Layout 限制圖片寬度爲 80px 的原生問題:寬度雖然限制了,高度卻依然是原圖的高度。解決辦法也很簡單:若是圖片寬度大於 80px,就重繪一張 80px 寬度的圖片填充進去。

新建一個 Group(虛擬文件夾),叫 Extensions,並在其內部新建 UIImage.swift 文件,內容以下:

import UIKit extension UIImage {     func resizeToSize(size: CGSize) -> UIImage {         UIGraphicsBeginImageContext(size)         self.drawInRect(CGRectMake(0, 0, size.width, size.height))         let newImage = UIGraphicsGetImageFromCurrentImageContext()         UIGraphicsEndImageContext()                  return newImage     } }

給 UIImage 類擴展了一個名爲 resizeToSize 的方法,返回一個按照要求的大小重繪過的 UIImage 對象。修改 cellForRowAtIndexPath 的代碼爲:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {     let cell = tableView.dequeueReusableCellWithIdentifier("firstTableViewCell", forIndexPath: indexPath) as! firstTableViewCell     cell.firstLabel.text = labelArray[indexPath.row]          var image = UIImage(named: (indexPath.row % 3).description)!     if image.size.width > 80 {         image = image.resizeToSize(CGSizeMake(80, image.size.height * (80 / image.size.width)))     }     cell.logoImageView.image = image     return cell }

搞定!

查看效果

Image

從上圖能夠看出,cell 已經能夠根據圖片和文字中比較高的一個徹底自適應。

致謝

感謝 《動態計算UITableViewCell高度詳解》,給我提供了許多基礎知識和靈感。

Auto Layout 使用心得(六)—— 製造炫酷的下拉刷新動畫

此係列文章代碼倉庫在 https://github.com/johnlui/AutoLayout ,有不明白的地方能夠參考個人 Auto Layout 設置哦,下載到本地打開就能夠了。

簡介

本文中,咱們將一塊兒使用 Auto Layout 技術製造一個炫酷的下拉刷新動畫。Auto Layout 除了在佈局的時候比較繁瑣之外,還有一個常常被人吐槽的點:讓許多 UIView.animateWithDuration 失效,甚至在界面上出現 「反方向動畫」 的視覺效果。本文中咱們將主要講述製造下拉刷新動畫的過程,關於 Auto Layout 與動畫的詳細配合咱們之後再一塊兒仔細探究。

實現首頁總體下拉回彈效果

創建接受下拉事件的主 View

使用一個跟窗體同樣很大的 mainView 把目前首頁的五個元素包括,並補全 Auto Layout 佈局。層次結構改變會讓除尺寸約束以外的全部約束消失。

Image

修改 UI 綁定及動畫函數

UI 和代碼的綁定修改爲以下:

Image

從新給 mainView 綁定上 panGesture,從 mainView 向 panGesture 拖動:

Image

修改存儲值的變量名稱和初始化代碼:

... var mainViewTopSpaceLayoutConstraintValue: CGFloat! var mainViewOriginY: CGFloat! ... mainViewTopSpaceLayoutConstraintValue = topLayoutConstraintOfMainView.constant mainViewOriginY = mainView.frame.origin.y ...

修改手勢觸發的目標函數(增長冒號):

panGesture.addTarget(self, action: Selector("pan:"))

改寫動畫目標函數:

func pan(recongnizer: UIPanGestureRecognizer) {     if panGesture.state == UIGestureRecognizerState.Ended {         UIView.animateWithDuration(0.4, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in             recongnizer.view?.frame.origin.y = self.mainViewOriginY             }, completion: { (success) -> Void in                 if success {                     self.topLayoutConstraintOfMainView.constant = self.mainViewTopSpaceLayoutConstraintValue                 }         })         return     }     let y = panGesture.translationInView(self.view).y     topLayoutConstraintOfMainView.constant = mainViewTopSpaceLayoutConstraintValue + y }

查看新的拖動回彈效果

Image

創建動畫視圖

創建動畫主視圖

拖動一個 View 到 mainView 上面,目的是置於 mainView 圖層之下。在 Xcode 裏,最上面的元素位於 UI 的最底層,這個順序跟 PS 正好相反。修改其 frame,添加 Auto Layout 約束,以下圖:

Image

將 UI 和代碼綁定:

Image

動畫的時候咱們須要該主視圖進行必定程度的下移,故將其 「到父視圖頂部的距離」 的約束也進行綁定。(此圖爲後期補充,請自動忽略其子元素)

填充動畫元素

將動畫主視圖更名爲 HiddenTopView。爲了方便的填充動畫元素,須要先將 HiddenTopView 移動到 mainView 之下,以便將圖層顯示在 mainView 之上,不被其遮擋,沒法編輯。我填充了三個圖片資源,所有加上約束。具體約束你們能夠自由發揮。效果以下:

Image

因爲我使用了白雲元素,故將最大的 view 的背景色填充爲灰色,將 HiddenTopView 背景色設置爲透明。

查看佈局效果

Image

動畫規劃

咱們計劃整個下拉刷新動畫分爲四個部分。下拉時:

  1. 整個 HiddenTopView 下移

  2. 小云彩從屏幕右側飛入

  3. 大雲彩持續作往復運動

  4. 下拉距離超過必定值時,中間子元素進行必定程度的放大

完善準備工做

分析須要獲取的約束

「動畫規劃」中,1 上面已經準備過,二、3 須要將小云、大雲的橫向定位參數向代碼綁定,4 只需綁定中間元素便可。

綁定三個子元素及其關鍵約束到代碼

Image

Make them move!

因爲實現動畫效果的代碼描述起來過於麻煩,請直接看代碼:https://github.com/johnlui/AutoLayout/blob/1420fddee57d22ebd443656fb3158c7dede84b56/AutoLayout/ViewController.swift

查看效果

Image

相關文章
相關標籤/搜索