iOS9-by-Tutorials-學習筆記六:UIStackView-Auto-Layout-Changes

來源:互聯網
上載者:User

iOS9-by-Tutorials-學習筆記六:UIStackView-Auto-Layout-Changes
iOS9-by-Tutorials-學習筆記六:UIStackView-Auto-Layout-Changes

今天這是第六篇筆記,現在回過頭去看,我也沒有想到自己能夠更新到第六篇。我算是一個比較懶的人了,現在已經不太喜歡動手敲代碼了。在寫這幾篇筆記的時候,我需要一邊看英文的文檔,一邊測試代碼,還得考慮怎麼能夠寫明白。這裡有點說明,我的英語水平只是四級,語文水平只能用呵呵評價了,文章中的語句難免會有不通順的地方,希望能夠把語義表述清楚。

閑話多了,回到正題,這篇文章介紹UIStackView和一些Auto Layout的改變。

UIStackView我個人理解是為瞭解決使用Storyboard添加的約束需要經常變化的情況。我想我們可能都在開發中遇到過修改約束的情況,一般是把約束與一個outlet的約束link起來,然後代碼修改,但是這個操作起來是不方便的。UIStackView通過修改一些簡單的屬性,例如alignment, distribution, and spacing,從而讓UIStackView根據我們的修改自動調整內部的顯示。

Auto Layout的改變主要是介紹layout anchors和layout guides。

Getting Started

開啟本章的配套的工程VacationSpots,在iPhone 6模擬器上運行,能夠看到APP有一些UI的問題,不要擔心,在後面將會修複這些問題。簡單梳理一下問題如下:
1. 圖中標出的內容沒有在垂直方向置中

點擊列表中的London Cell進入詳情頁面,最下面的三個按鈕沒有平均分配空間:

點擊WEATHER旁邊的hide按鈕,內容是被隱藏了,但是留下了一塊空白,下面的內容沒有移動上來:

WHAT TO SEE 部分在WHY VISIT的下面會更加合理一點。

現在已經瞭解了這些問題,下面開始用UIStackView來修改這些問題。開啟Main.storyboard,查看如下的Controller scene:

能夠注意到上面的每個對應的控制項都有背景顏色,這個只是為了協助我們查看這些屬性的變化。這些背景顏色在啟動並執行時候都會被去掉,通過如下代碼,如果你想讓它們在啟動並執行時候也顯示注釋掉這些代碼就可以:<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjwvYmxvY2txdW90ZT4NCjxwcmUgY2xhc3M9"brush:java;">// SpotInfoViewController.swift override func viewDidLoad() { super.viewDidLoad() // Clear background colors from labels and buttons for view in backgroundColoredViews { view.backgroundColor = UIColor.clearColor() } ........ }

在Storyboard中的的控制項都通過outlet與SpotInfoViewController.swift中對應的屬性進行了關聯。在storyboard中顯示的名字對應SpotInfoViewController.swift對應的變數。

Your first stack view

我們先用Stack View解決我們問題列表中底部按鈕的問題。使用UIStackView能夠在一個座標抽上分配位置和控制項之間的空間。幸運的是將Views嵌入到UIStackView中並沒有太難。在Storyboard中的Spot Info View Controller選擇如下三個按鈕控制項:

選擇好三個按鈕後,在Storyboard中點擊如下的按鈕:

當Views被嵌入UIStackView之後,Views的約束都被移除了,同時需要設定UIStackView的約束。選中UIStackView,然後按照添加約束:

這裡有個選擇UIStackView的技巧,由於UIStackView是在按鈕的後面,很不好選中,我們可以按住Shift,右擊,在出現的菜單中列出來當前點擊位置所有的View,我們可以選擇UIstackView。另一種方法我們可以在outline view中選擇。

設定好約束後,能夠看到按鈕顯示如下,第一個按鈕被展開了,填充滿了UIStackView的剩餘空間。UIstackView有一個Distribution屬性,用於控制Views怎麼在UIStackView中顯示,現在設定的是Fill,即將會填充滿UIStackView。為了這個目的,UIstackView將會根據View的ccontent hugging優先順序去展開View,最低的將會被展開。如果優先順序一樣,將會展開第一個。

我們的目的是讓View間的距離相等,在Attributes inspector中修改Distribution為Equal Spacing。

運行APP,能夠看到我們的按鈕顯示正確了:

一些思考

思考一下你使用Auto Layout,通過約束實現上面的要求,那將是一種什麼令人”愉悅”的行為。可能你很熟悉Auto Layout,認為這些東西都很簡單,那麼你在考慮一下如果我們後面有需求添加一個按鈕,刪除一個按鈕呢,怎麼辦呢?約束刪除了重新添加嗎?如果使用UIStackView,這些將變得比較簡單,只要我們添加或者刪除View,其他的工作UIstackView就會幫我們做了。

UISTackView的更加深入的講解,將會在下一篇文章中繼續介紹。這裡先介紹一下Auto Layout的新特性:layout anchors和layout guides。

Layout anchors

Layout anchors提供了我們一種簡單的建立約束的方式。

想象一下我們在iOS 9之前建立一個約束,簡直就是天書,但是在iOS 9中使用Layout anchors將會簡單好多,下面是兩種的對比:

// iOS9以前let constraint = NSLayoutConstraint(item: topLabel, attribute: .Bottom, relatedBy: .Equal, toItem: bottomLabel, attribute: .Top, multiplier: 1, constant: 8)// iOS 9let constraint = topLabel.bottomAnchor.constraintEqualToAnchor(bottomLabel.topAnchor, constant: 8)

Layout anchors不僅理解起來簡單,而且寫起來也簡單了。

對應於我們在iOS 9以前添加約束時候的attribute,基本都有與之對應的anchor,例如top對應topAnchor,bottom對應bottomAnchor等。Layout Anchor都是直接或者間接繼承自NSLayoutAnchor,上面只是示範了一下相等的情況,我們都知道在約束中又大於小於等,下面列出NSLayoutAnchor的介面檔案,從介面檔案中能夠清楚的瞭解到對應的方法:

import Foundationimport UIKit/*  NSLayoutAnchor.h    Copyright (c) 2015, Apple Inc. All rights reserved.*//* An NSLayoutAnchor represents an edge or dimension of a layout item.  Its concrete  subclasses allow concise creation of constraints.      Instead of invoking  +[NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]  directly, you can instead do something like this: [myView.topAnchor constraintEqualToAnchor:otherView.topAnchor constant:10]; The -constraint* methods are available in multiple flavors to support use of different relations and omission of unused options. */@available(iOS 9.0, *)public class NSLayoutAnchor : NSObject {    /* These methods return an inactive constraint of the form thisAnchor = otherAnchor.     */    public func constraintEqualToAnchor(anchor: NSLayoutAnchor!) -> NSLayoutConstraint!    public func constraintGreaterThanOrEqualToAnchor(anchor: NSLayoutAnchor!) -> NSLayoutConstraint!    public func constraintLessThanOrEqualToAnchor(anchor: NSLayoutAnchor!) -> NSLayoutConstraint!    /* These methods return an inactive constraint of the form thisAnchor = otherAnchor + constant.     */    public func constraintEqualToAnchor(anchor: NSLayoutAnchor!, constant c: CGFloat) -> NSLayoutConstraint!    public func constraintGreaterThanOrEqualToAnchor(anchor: NSLayoutAnchor!, constant c: CGFloat) -> NSLayoutConstraint!    public func constraintLessThanOrEqualToAnchor(anchor: NSLayoutAnchor!, constant c: CGFloat) -> NSLayoutConstraint!}/* Axis-specific subclasses for location anchors: top/bottom, leading/trailing, baseline, etc. */@available(iOS 9.0, *)public class NSLayoutXAxisAnchor : NSLayoutAnchor {}@available(iOS 9.0, *)public class NSLayoutYAxisAnchor : NSLayoutAnchor {}/* This layout anchor subclass is used for sizes (width & height). */@available(iOS 9.0, *)public class NSLayoutDimension : NSLayoutAnchor {    /* These methods return an inactive constraint of the form         thisVariable = constant.    */    public func constraintEqualToConstant(c: CGFloat) -> NSLayoutConstraint!    public func constraintGreaterThanOrEqualToConstant(c: CGFloat) -> NSLayoutConstraint!    public func constraintLessThanOrEqualToConstant(c: CGFloat) -> NSLayoutConstraint!    /* These methods return an inactive constraint of the form         thisAnchor = otherAnchor * multiplier.    */    public func constraintEqualToAnchor(anchor: NSLayoutDimension!, multiplier m: CGFloat) -> NSLayoutConstraint!    public func constraintGreaterThanOrEqualToAnchor(anchor: NSLayoutDimension!, multiplier m: CGFloat) -> NSLayoutConstraint!    public func constraintLessThanOrEqualToAnchor(anchor: NSLayoutDimension!, multiplier m: CGFloat) -> NSLayoutConstraint!    /* These methods return an inactive constraint of the form         thisAnchor = otherAnchor * multiplier + constant.    */    public func constraintEqualToAnchor(anchor: NSLayoutDimension!, multiplier m: CGFloat, constant c: CGFloat) -> NSLayoutConstraint!    public func constraintGreaterThanOrEqualToAnchor(anchor: NSLayoutDimension!, multiplier m: CGFloat, constant c: CGFloat) -> NSLayoutConstraint!    public func constraintLessThanOrEqualToAnchor(anchor: NSLayoutDimension!, multiplier m: CGFloat, constant c: CGFloat) -> NSLayoutConstraint!}

在上面的介面檔案中,我們能夠清楚的瞭解到NSLayoutAnchor有三個子類:NSLayoutXAxisAnchor,NSLayoutYAxisAnchor,NSLayoutDimension。下面列出了UIView的Anchor都是對應的那種類型:

extension UIView {    /* Constraint creation conveniences. See NSLayoutAnchor.h for details.     */    @available(iOS 9.0, *)    public var leadingAnchor: NSLayoutXAxisAnchor { get }    @available(iOS 9.0, *)    public var trailingAnchor: NSLayoutXAxisAnchor { get }    @available(iOS 9.0, *)    public var leftAnchor: NSLayoutXAxisAnchor { get }    @available(iOS 9.0, *)    public var rightAnchor: NSLayoutXAxisAnchor { get }    @available(iOS 9.0, *)    public var centerXAnchor: NSLayoutXAxisAnchor { get }    @available(iOS 9.0, *)    public var topAnchor: NSLayoutYAxisAnchor { get }    @available(iOS 9.0, *)    public var bottomAnchor: NSLayoutYAxisAnchor { get }    @available(iOS 9.0, *)    public var firstBaselineAnchor: NSLayoutYAxisAnchor { get }    @available(iOS 9.0, *)    public var lastBaselineAnchor: NSLayoutYAxisAnchor { get }    @available(iOS 9.0, *)    public var centerYAnchor: NSLayoutYAxisAnchor { get }    @available(iOS 9.0, *)    public var widthAnchor: NSLayoutDimension { get }    @available(iOS 9.0, *)    public var heightAnchor: NSLayoutDimension { get }}

上面UIView的Anchor屬性被分成了三類,同樣我們在設定屬性的時候,也要求同類的屬性的才能設定,比如ViewA和ViewB之間的約束,ViewA-topAnchor和ViewB-bottomAnchor是可以的,ViewA-topAnchor和ViewB-leftAnchor就是不允許的,如果這樣的話編譯器會警告,運行時也會報錯。

注意: whyVisitLabel.topAnchor.constraintEqualToAnchor(whatToSeeLabel.leftAnchor) 這個按照上面的說法應該會報錯的,但是我在啟動並執行時候也沒有報錯,可能是我這裡只是隨便寫出一個做測試的原因,後續我會繼續實驗一下這個知識點,然後再改正。

Layout guides

有時候我們想設定兩個View之間的空間,需要在兩個View之間添加一個不可見的View(dummy view),然後在設定約束。Layout guide可以理解為一個隱形的不可見View,我們能夠使用它的矩形邊緣來布局,我們可以像我們使用View一樣設定約束。使用Layout guide的好處是輕量,而且不會在view的層級中,也不會參與事件的響應過程。layout guide也包含除了firstBaselineAnchor和lastBaselineAnchor之外的View所有的Anchor。

Fixing the alignment bug

下面就利用layout guide來修複列表頁面文字內容上下不置中的問題。看是我們設定的約束,我們設定label距離上面的距離是15,當下面的label顯示一行的時候是正常的,如果要是兩行了,由於上面的約束是固定的,最終就變成了不置中的效果了。

在iOS 9之前,我們想解決這個問題可以把兩個label放置到一個container view容器中,設定這個container view為劇中,這裡面的container view就是不可見的view即dummy view。現在在iOS 9上我們可以使用layout guide來代替這個view。

目前只能通過代碼來添加layout guide。開啟VacationSpotCell.swift檔案,修改對應代碼:

override func awakeFromNib() {    super.awakeFromNib()    // TODO: Add layoutGuide code here to center the name and locationName labels vertically    // 建立layou guide    let layoutGuide = UILayoutGuide()    contentView.addLayoutGuide(layoutGuide)    // 設定layout guide的約束    let topConstraint = layoutGuide.topAnchor.constraintEqualToAnchor(nameLabel.topAnchor)    let bottomConstraint = layoutGuide.bottomAnchor.constraintEqualToAnchor(locationNameLabel.bottomAnchor)    let centerConstraint = layoutGuide.centerYAnchor.constraintEqualToAnchor(contentView.centerYAnchor)    // 啟用layout guide的約束    NSLayoutConstraint.activateConstraints([topConstraint, bottomConstraint, centerConstraint])  }

運行APP發現,部分文字被截斷了:

處理截斷問題

這個原因是我們設定layout guide置中,但是nameLabel(上面的)與super view top的約束還存在,造成了下面的label被擠壓了,這時候我們只要刪除掉這個約束就可以了。但是刪除了之後storyboard會提示錯誤,這時候我們可以使用佔位約束,這個主要是為了使storyboard不報錯,在啟動並執行時候並不會使用。

最近一直在加班,斷斷續續整理了好久終於整理完了這篇文章了,可能會有錯誤,還請大家指出。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.