iOS 自动布局和弹性盒子

当同事问到我这个问题时,我脑子中直接冒出了一个词“弹性盒子”。

问题:

有一个 Cell 中有 4 个并排排列的控件,布局如下图所示:

假设:

1、        这些控件高度和y坐标固定。

2、        蓝色控件x位置固定,但右端对齐于黑色控件。

3、        黑色、红色、绿色控件宽度固定,右端对齐于右侧的控件(绿色控件右对齐于cell 的右边)。

要求:

1、        当黑色、红色、绿色控件中的任意一个控件隐藏时,其余两个控件自动右移占据隐藏控件的控件,蓝色控件则自动布满剩下的宽度。以下是分别隐藏其中一个控件的效果:

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()将导致所有自动布局约束被重新计算。

时间: 2024-10-14 04:42:38

iOS 自动布局和弹性盒子的相关文章

css3弹性盒子温习

弹性盒子由弹性容器(Flex container)和弹性子元素(Flex item)组成. 弹性容器通过设置 display 属性的值为 flex 或 inline-flex将其定义为弹性容器. 弹性容器内包含了一个或多个弹性子元素. display: -webkit-box; /* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */display: -moz-box; /* 老版本语法: Firefox (buggy)

CSS3响应式布局之弹性盒子

CSS3弹性盒模型可以轻松的创建自适应浏览器流动窗口的布局或自适应字体大小的布局.同时该盒子决定了一个盒子在其他盒子的分布方式,及如何处理可用的空间. 自己写了一个弹性盒子的demo: 主要HTML代码: <div class="outer"> <div id="div1">1</div> <div id="div2">2</div> <div id="div3"

用css3弹性盒子模型实现九宫格布局

html结构: <div class="box"> <div class="top"> <div class="left">左上</div> <div class="center">上</div> <div class="right">右上</div> </div> <div class=&qu

css3弹性盒子模型之box-flex

css3弹性盒子模型之box-flex 浏览器支持 目前没有浏览器支持 box-flex 属性. Firefox 支持替代的 -moz-box-flex 属性. Safari.Opera 以及 Chrome 支持替代的 -webkit-box-flex 属 box-flex是css3新添加的盒子模型属性),它的出现打破了我们经常使用的浮动布局,实现垂直等高.水平均分.按比例划分.但是它有一定的局限性,在firefox.chrome这浏览器下需要使用它们的私有属性来定义:firefox(-moz)

css position弹性盒子测试

总结: 1.利用样式height:100%设置div高度为全屏时候必须设置所有的父元素,但是父元素那么多,不可控,所以此法不可行: 2.设置父框架的padding为100px,div进行float,padding有效:但div进行position,并配合left:0,padding失效: 3.弹性盒子模型需要position属性和两个相对立的属性,比如top和bottom,left和right. 4.要做出来自适应的三个模块,要会计算中间div进行position后left和right的值,此值

iOS 自动布局

自动布局框架 Masonry PureLayout FLKAutoLayout KeepLayout UIView+Autolayout 相关教程: iOS 开发实践之 Auto Layout Masonry介绍与使用实践(快速上手Autolayout) IOS自动布局之Autoresizing iOS中的相对布局:AutoLayout

Flexible 弹性盒子模型之CSS justify-content 属性

实例 在弹性盒对象的 <div> 元素中的各项周围留有空白: div { display: flex; justify-content: space-around; } 复制 效果预览 浏览器支持 表格中的数字表示支持该属性的第一个浏览器的版本号. 紧跟在 -webkit-, -ms- 或 -moz- 后的数字为支持该前缀属性的第一个版本. 属性           justify-content 29.021.0 -webkit- 11.0 28.018.0 -moz- 9.06.1 -we

[ css 弹性盒子模型 flex-shrink 属性 ] 弹性盒子模型flex布局中flex-shrink属性讲解及实例演示的区别

根据弹性盒子元素所设置的收缩因子作为比率来收缩空间 实例: <!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="utf-8" /> <title>-webkit-flex-basis_CSS参考手册_web前端开发参考手册系列</title> <meta name="author" conten

[ css 弹性盒子模型 flex-grow 属性 ] 弹性盒子模型flex布局中flex-grow属性讲解及实例演示的区别

根据弹性盒子元素所设置的扩展因子作为比率来分配剩余空间 实例: <!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="utf-8" /> <title>-webkit-flex-basis_CSS参考手册_web前端开发参考手册系列</title> <meta name="author" cont