你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS 自動布局和彈性盒子

iOS 自動布局和彈性盒子

編輯:IOS開發綜合

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

問題:

有一個 Cell 中有 4 個並排排列的控件,布局如下圖所示:

\

假設:<喎?/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"/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()將導致所有自動布局約束被重新計算。


  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved