iOS 自動布局和彈性盒子

來源:互聯網
上載者:User

iOS 自動布局和彈性盒子

當同事問到我這個問題時,我腦子中直接冒出了一個詞“彈性盒子”。

問題:

有一個 Cell 中有 4 個並排排列的控制項,布局如所示:


假設:<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD48cD4gPC9wPjxwPjGhoiAgICAgICAg1eLQqb/YvP6437bIus151/ix6rnMtqihozwvcD48cD4yoaIgICAgICAgIMC2yau/2Lz+eM671sO5zLaoo6y1q9PStsu21Mbr09q62smrv9i8/qGjPC9wPjxwPjOhoiAgICAgICAgutrJq6GiuuzJq6GiwszJq7/YvP6/7bbIucy2qKOs09K2y7bUxuvT2tPSsuC1xL/YvP6jqMLMyau/2Lz+09K21Mbr09pjZWxsILXE09Kx36OpoaM8L3A+PHA+IDwvcD48cD7Sqsfzo7o8L3A+PHA+MaGiICAgICAgICC1sbrayauhorrsyauhosLMyau/2Lz+1tC1xMjO0uLSu7j2v9i8/tL+stjKsaOsxuTT4MG9uPa/2Lz+19S2r9PS0sbVvL7d0v6y2L/YvP61xL/YvP6jrMC2yau/2Lz+1PLX1LavsrzC+sqjz8K1xL/ttsiho9LUz8LKx7fWsfDS/rLYxuTW0NK7uPa/2Lz+tcTQp7n7o7o8L3A+PGltZyBzcmM9"http://www.2cto.com/uploadfile/2015/1213/20151213030114863.jpg" alt="\" />

2、 依次類推,當隱藏其中任意2個控制項和3個控制項全都隱藏的效果如所示:


如果是 HTML5,這個問題用“彈性盒子”來解決是再合適不過了。但是“彈性盒子”是 CSS 3.0中新增的內容,iOS 並不支援彈性盒子,我們只能自己來解決這個問題。

幸好 iOS 有自動布局,我們可以用自動布局來解決這個問題(當然還需要一點點代碼)。

一、 UI 設計

開啟故事板,向viewcontrollerz中拖入4個UIView,和3個按鈕,如所示:


這個4個 UIView 和 3個 UIButton 分別是幹什麼的,相信你已經能一目瞭然了。按鈕先不管,先看看4個View。

藍色view的自動布局約束是這樣的:

top:24,leading:16,height:24,trailing:10

黑色、紅色、綠色 view 的布局約束都是一樣的:

width:37,height:24,top:24,trailing:10

四個UIView 分別串連至如下 IBOutlet:

藍色 v1

黑色 v2

紅色 v3

綠色 v4

三個按鈕的點擊事件則分別串連到三個IBAction:

@IBActionfunc hideGray(sender: AnyObject) {

hide(v2)

}

@IBAction func hideRed(sender: AnyObject) {

hide(v3)

}

@IBAction func hideGreen(sender: AnyObject) {

hide(v4)

}

hide()方法待會介紹。

一、 彈性盒子設計

當黑色、紅色、綠色view隱藏時(即hidden 屬性為true),自動釋放其佔據的空間,我們需要讓它們的布局約束根據hidden屬性進行改變。

從上面我們可以得知,它們的自動布局約束主要是如下幾個:

width、height、leading、trailing。

這幾個布局跟View所佔據的空間有密切關係。其中,height我們不用管,因為它們當width=0 時它們的佔據的空間就已經釋放了,height值是多少就無關緊要了。

那麼也就是說,當view隱藏時,我們讓view的width、leading、trailing同時為0,就釋放了view所佔據的空間。

因此,我們需要在運行時擷取width、leading、trailing這三個約束,並根據hidden屬性修改它們。那麼我們能夠在運行時獲得View的指定約束嗎?答案是肯定的。

我們知道,UIView有一個 constraints()方法,返回一個NSLayoutConstraints數組,包含了其所有的width、height是屬於view的constrains,而leading、trailing則是屬於superview的。我們可以通過遍曆這兩個數組來找到我們想要的約束。

我們用一個UIView的擴充來實現這個目的:

extension UIView{

func widthConstraint()->NSLayoutConstraint?{

for constraint in self.constraints() {

let firstItem = constraint.firstItem as? UIView

if firstItem == self && constraint.firstAttribute ==NSLayoutAttribute.Width{

println("I gotit:\(constraint)")

return constraint as?NSLayoutConstraint

}

}

return nil

}

func leadingConstraint()->NSLayoutConstraint?{

if self.superview == nil {

return nil

}

for constraint in self.superview!.constraints() {// 這個約束是在superview 中了

let firstItem = constraint.firstItem as? UIView

let secondItem = constraint.secondItem as? UIView

if firstItem == self && constraint.firstAttribute ==NSLayoutAttribute.Leading{

println("I gotit:\(constraint)")

return constraint as?NSLayoutConstraint

}

}

return nil

}

func trailingConstraint()->NSLayoutConstraint?{

if self.superview == nil {

return nil

}

for constraint in self.superview!.constraints() {// 這個約束是在superview 中了

let firstItem = constraint.firstItem as? UIView

if firstItem == self && constraint.firstAttribute ==NSLayoutAttribute.Trailing{

println("I gotit:\(constraint)")

return constraint as?NSLayoutConstraint

}

}

return nil

}

}

然後我們來設計一個彈性盒子,用來管理這三個View。彈性盒子類的主要目的,是將這些View的三個約束的值儲存到一個地方(比如說字典中),然後當某個View的hidden屬性設為false時,將約束恢複至原來的值並顯示出來。

class FlexibleBox:NSObject{

structViewSpace:Printable{

var widthConstant:CGFloat = 0

var leadConstant:CGFloat = 0

var trailConstant:CGFloat = 0

var description: String {

return "width-\(widthConstant)\nleading -

\(leadConstant)\ntrailing- \(trailConstant)"

}

}

var cachedConstraints = [UIView:ViewSpace]()

func addViews(views:[UIView]){

for view in views {

addView(view)

}

}

func addView(v:UIView){

var space = ViewSpace()

if let constraint = v.trailingConstraint() {

space.trailConstant = constraint.constant

}

if let constraint = v.leadingConstraint() {

space.leadConstant = constraint.constant

}

if let constraint = v.widthConstraint() {

space.widthConstant = constraint.constant

}

cachedConstraints[v]=space

println("\(space)")

}

func freeViewSpace(v:UIView){

v.widthConstraint()?.constant = 0

v.leadingConstraint()?.constant = 0

v.trailingConstraint()?.constant = 0

}

func resumeViewSpace(v:UIView){

let space = cachedConstraints[v] ?? ViewSpace()

v.trailingConstraint()?.constant = space.trailConstant

v.leadingConstraint()?.constant = space.leadConstant

v.widthConstraint()?.constant = space.widthConstant

}

deinit{

cachedConstraints.removeAll(keepCapacity: false)

}

}

二、 使用彈性盒子

在View Controller 中聲明一個彈性盒子:

let flexBox = FlexibleBox()

然後在viewDidLoad方法中:

flexBox.addViews([v2,v3,v4])

然後但點擊按鈕時,調用如下方法隱藏(或取消隱藏)一個View:

func toggleViewHiddenStatus(v:UIView){

if v.hidden == false {

flexBox.freeViewSpace(v)

}else{

flexBox.resumeViewSpace(v)

}

v.hidden = !v.hidden

self.view.setNeedsLayout()

}

最後一句self.view.setNeedsLayout()將導致所有自動布局約束被重新計算。


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.