转自:http://www.cocoachina.com/ios/20160530/16522.html

本文投稿文章,作者:MangoMade(简书

AutoLayout非常强大也非常易用,可读性也很强,加上各种第三方AutoLayout库,让你布起局来犹如绷掉链子的狗!根本停不下来!以前的

label.frame.origin.y + label.frame.size.height + 10

如今只用:

button.snp_makeConstraints{
    $0.top.equalTo(label.snp_bottom).offset(10)
}

真是好用得不要不要。

可是,我在使用AutoLayout却遇到不少坑,翻阅了不少博客网站才找到了我认为的比较不错的解决方案。我把这些内容贴出来,如果其中有误,可以在下方留言指出,希望大家能够多多交流共同进步。

本文主要分四部分:

  • updateViewConstraints与updateConstraints篇

  • AutoLayout与Frame篇

  • AutoLayout动画篇

  • AutoLayout比例设置

其中‘篇’字体现了本文作者对逼格的追求。

updateViewConstraints与updateConstraints篇

基本用法

updateViewConstraints与updateConstraints是AutoLayout出现后新增的api,updateConstraints主要功能是更新view的约束,并会调用其所有子视图的该方法去更新约束。

而updateViewConstraints的出现方便了viewController,不用专门去重写controller的view,当view的updateConstraints被调用时,该view若有controller,该controller的updateViewConstraints便会被调用。

两个方法都需要在方法实现的最后调用父类的该方法。并且这两个方法不建议直接调用。

在使用过程中我发现这两个方法有时候不会被系统调用。后来我看到public class func requiresConstraintBasedLayout() -> Bool方法的描述:

constraint-based layout engages lazily when someone tries to use it (e.g., adds a constraint to a view). If you do all of your constraint set up in -updateConstraints, you might never even receive updateConstraints if no one makes a constraint. To fix this chicken and egg problem, override this method to return YES if your view needs the window to use constraint-based layout.

大意是说,视图并不是主动采用constraint-based的。在非constraint-based的情况下-updateConstraints,可能一次都不会被调用,解决这个问题需要重写该类方法并返回true。

这里要注意,如果一个view或controller是由interface builder初始化的,那么这个实例的updateViewConstraints或updateConstraints方法便会被系统自动调用,起原因应该就是对应的requiresConstraintBasedLayout方法返回true。而纯代码初始化的视图requiresConstraintBasedLayout方法默认返回false。

所以在纯代码自定义一个view时,想把约束写在updateConstraints方法中,就一定要重写requiresConstraintBasedLayout方法,返回true。

至于纯代码写的viewController如何让其updateViewConstraints方法被调用。我自己的解决办法是手动调用其view的setNeedsUpdateConstraints方法。

How to use updateConstraints?

文档中对于这两个方法提的最多的就是,重写这两个方法,在里面设置约束。所以一开始我认为这两个方法是苹果提供给我们专门写约束的。于是便开始尝试使用。

直到后来在UIView中看到这样一句话:

You should only override this method when changing constraints in place is too slow, or when a view is producing a number of redundant changes.

“你只因该在添加约束过于慢的时候,或者一次要修改大量约束的情况下重写次方法。”

简直是让人觉得又迷茫又坑爹。updateConstraints方法到底应该何时使用

后来看到how to use updateConstraints这篇文章。给出了一个合理的解释:

  • 尽量将约束的添加写到类似于viewDidLoad的方法中。

  • updateConstraints并不应该用来给视图添加约束,它更适合用于周期性地更新视图的约束,或者在添加约束过于消耗性能的情况下将约束写到该方法中。

  • 当我们在响应事件时(例如点击按钮时)对约束的修改如果写到updateConstraints中,会让代码的可读性非常差。

关于性能,我也做了一个简单的测试:

class MMView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.grayColor()
        initManyButton()
        //初始化时添加约束
        test() //每次只有一个test()不被注释就好
    }
    override func touchesBegan(touches: Set, withEvent event: UIEvent?) {
        //响应事件时添加约束
        //test()
    }
    override func updateConstraints() {
        //updateConstraints中添加约束
        //test()
        super.updateConstraints()
    }
    func test(){
        let then = CFAbsoluteTimeGetCurrent()
        addConstraintsToButton()
        let now = CFAbsoluteTimeGetCurrent()
        print(now - then)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    let buttonTag = 200
    func initManyButton(){
        for index in 0...1000{
            let button = UIButton(type: .System)
            button.tag = buttonTag + index
            self.addSubview(button)
        }
    }
    func addConstraintsToButton(){
        for index in 0...1000{
            if let button = self.viewWithTag(index+buttonTag){
                button.snp_makeConstraints{ make in
                    make.center.equalTo(self)
                    make.size.equalTo(self)
                }
            }
        }
    }
}

分别将设置约束写在init中、写在updateConstraints中、写在事件响应方法中 的时间消耗进行测试,对1000个button添加约束,每个添加4个约束。

  • init中,时间消耗约为0.37秒

  • 写在updateconstraints中,时间消耗约为0.52秒

  • 写在事件响应方法中,时间消耗约为0.77秒

所以,结论,还是将约束的设置写在viewDidLoad中或者init中。没事儿尽量不去碰updateConstraints。除非对性能有要求。

关于UIView的translatesAutoresizingMaskIntoConstraints属性

最近在对AutoLayout的学习中发现,很多人似乎对translatesAutoresizingMaskIntoConstraints的误解非常大,很多时候遇到问题总有人会在下面回答到:把translatesAutoresizingMaskIntoConstraints设置成false就可以解决问题。。。实际上并没有什么用。

那么这个属性到底是做什么的呢?

其实这个属性的命名已经把这个属性的功能解释的非常清楚了。

除了AutoLayout,AutoresizingMask也是一种布局方式。这个想必大家都有了解。默认情况下,translatesAutoresizingMaskIntoConstraints = true , 此时视图的AutoresizingMask会被转换成对应效果的约束。这样很可能就会和我们手动添加的其它约束有冲突。此属性设置成false时,AutoresizingMask就不会变成约束。也就是说 当前 视图的 AutoresizingMask失效了。

那我们什么时候需要设置这个属性呢?

当我们用代码添加视图时,视图的translatesAutoresizingMaskIntoConstraints属性默认为true,可是AutoresizingMask属性默认会被设置成.None。也就是说如果我们不去动AutoresizingMask,那么AutoresizingMask就不会对约束产生影响。

当我们使用interface builder添加视图时,AutoresizingMask虽然会被设置成非.None,但是translatesAutoresizingMaskIntoConstraints默认被设置成了false。所以也不会有冲突。

反而有的视图是靠AutoresizingMask布局的,当我们修改了translatesAutoresizingMaskIntoConstraints后会让视图失去约束,走投无路。例如我自定义转场时就遇到了这样的问题,转场后的视图并不在视图的正中间。

所以,这个属性,基本上我们也不用设置它。

AutoLayout与Frame篇

在使用AutoLayout的时候你可能也会同时也会用到frame,比如需要用到layer的时候。

那么你可能会遇到这种情况,想让layer的尺寸是由其它视图尺寸设定的,而这个视图又是由约束控制布局的。如果将layer的初始化与view的初始化放在一个方法中,类似于viewDidLoad的方法中

layer.bounds = CGRectMake(0,0,view.bounds.size.widith * 0.5,50)

那么很可能最终layer的宽度是0。

这是因为约束被设置之后它并不会立即对view作出改变,而是要等到layout时,才会对视图的尺寸进行修改。而layout通常是在视图已经加载到父视上时。

所以我们如果在viewDidLoad中设置了约束,要等到viewDidAppear时view的尺寸才会真正改变。

那么,如果需要既用约束布局,又用frame布局,如果能让它们很好的协作呢?

一个很好的解决办法是:吧frame设置写到layoutSubviews中或者写到viewDidLayoutSubviews中即可。因为约束生效时view的center或者bounds就会被修改,center或者bounds被修改时layoutSubview,就会被调用,随后viewDidLayoutSubviews就回被调用。这个时候,设置约束的视图frame就不再是(0,0,0,0)了

如果我们必须要将约束和frame写在同一方法中,写完约束就设置frame,而不是想把frame的设置写到layoutSubview中(比如我们设置好约束后马上就想根据约束的结果计算高度),那么我们还可以在设置完约束之后手动调用layoutIfNeeded方法,让视图立即layout,更新frame。在这之后就可以拿到设置约束的视图的尺寸了。

AutoLayout动画篇

这篇的内容非常简单,就是介绍约束布局的视图如何进行位移动画。

如果我们的一个视图是通过设置frame来布局的,那么我们在位移动画时直接改变frame就可以了。很简单。

可是在约束布局的视图中,设置frame这个办法就无效了。那我们怎么办?

网上有很多人的办法就是:拿到想要做动画的约束,在动画之前对约束进行修改,在动画的block中调用setNeedsLayout方法。

这个方法我觉得非常的麻烦,为了方便地拿到约束,我们通常还需要把约束设置成属性,动画一多那岂不就是完蛋了?

一种更好的方法就是设置视图的transform属性。

比如我想要让视图做一个x轴+50的位移,

self.view.transform = CGAffineTransformMakeTranslation(50, 0)

这样设置即可。CGAffineTransformMakeTranslation这个方法就是设置位置。

AutoLayout比例设置

如果我们用autoLayout想把一个视图的中心设置到屏幕横向和纵向的1/4处:

button.snp_makeConstraints{ make in
    make.centerX.equalTo(self.view).multipliedBy(0.25)
    make.centerY.equalTo(self.view).multipliedBy(0.25)
}

这就相当于

button.center = CGPointMake(self.view.bounds.size.width * 0.25 ,self.view.bounds.size.height * 0.25)

那么AutoLayout中的倍数,具体表示什么呢?

let view = UIView()
self.view.addSubview(view)
var bottomConstraint : Constraint!
view.snp_makeConstraints { (make) in
    make.height.equalTo(50)
    make.width.equalTo(50)
    make.centerX.equalTo(self.view.snp_centerX)
    bottomConstraint = make.bottom.equalTo(self.view.snp_centerY).constraint
}
self.view.layoutIfNeeded()
print(view.frame)
//打印结果 y:318 height:50 和为368
bottomConstraint.uninstall()
view.snp_makeConstraints { (make) in
    make.bottom.equalTo(self.view.snp_centerY).multipliedBy(1.5)
}
self.view.layoutIfNeeded()
print(view.frame)
//打印结果 y:318 height:50 和为552,刚好是368的1.5倍
//所以我们可以得出结论:某条边的约束的倍数代表着这条边到相对边的距离的倍数
//上面代码中的1.5倍让bottom边到y = 0边的距离变成了1.5倍

AutoLayout的那些事儿的更多相关文章

  1. 关于AutoLayout(自动布局)那些事儿

    这是博主的WWDC2012笔记系列中的一篇,完整的笔记列表可以参看这里.如果您是首次来到本站,也许您会有兴趣通过RSS,或者通过页面左侧的邮件订阅的方式订阅本站. AutoLayout在去年的WWDC ...

  2. 说说Makefile那些事儿

    说说Makefile那些事儿 |扬说|透过现象看本质 工作至今,一直对Makefile半知半解.突然某天幡然醒悟,觉得此举极为不妥,只得洗心革面从头学来,以前许多不明觉厉之处顿时茅塞顿开,想想好记性不 ...

  3. 总结iOS开发中的断点续传那些事儿

    前言 断点续传概述 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始.当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会从头下载,这样很 ...

  4. setTimeout那些事儿

    一.setTimeout那些事儿之单线程 一直以来,大家都在说Javascript是单线程,浏览器无论在什么时候,都且只有一个线程在运行JavaScript程序. 但是,不知道大家有疑问没——就是我们 ...

  5. Javascript中关于cookie的那些事儿

    Javascript-cookie 什么是cookie? 指某些网站为了辨别用户身份.进行session跟踪而储存在用户本地终端上的数据(通常经过加密).简单点来说就是:浏览器缓存. cookie由什 ...

  6. 使用Autolayout实现UITableView的Cell动态布局和高度动态改变

    本文翻译自:stackoverflow 有人在stackoverflow上问了一个问题: 1 如何在UITableViewCell中使用Autolayout来实现Cell的内容和子视图自动计算行高,并 ...

  7. Masonry介绍与使用实践:快速上手Autolayout

    1 MagicNumber -> autoresizingMask -> autolayout 以上是纯手写代码所经历的关于页面布局的三个时期 在iphone1-iphone3gs时代 w ...

  8. webpack那些事儿

    webpack那些事儿01-webpack到底是什么 webpack那些事儿02-从零开始 webpack那些事儿03-热插拔 hot webpack那些事儿04-spa项目实战分析 webpack那 ...

  9. 利用代码添加autolayout约束

    1.概述 通常我们通过storyboard能够完成的,代码也能够完成,所以这里介绍下代码实现约束的添加,通常我们不这么干(在不使用第三方框架的情况下,使用系统自带的类添加约束特别繁琐),所以这里仅仅简 ...

随机推荐

  1. 『题解』洛谷P2170 选学霸

    更好的阅读体验 Portal Portal1: Luogu Description 老师想从\(N\)名学生中选\(M\)人当学霸,但有\(K\)对人实力相当,如果实力相当的人中,一部分被选上,另一部 ...

  2. .NET手撸绘制TypeScript类图——下篇

    .NET手撸绘制TypeScript类图--下篇 在上篇的文章中,我们介绍了如何使用.NET解析TypeScript,这篇将介绍如何使用代码将类图渲染出来. 注:以防有人错过了,上篇链接如下:http ...

  3. 201871010114-李岩松《面向对象程序设计(java)》第一周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  4. 《计算机网络 自顶向下方法》 第6章 链路层和局域网 Part2

    待补充完善 Web 页面的请求历程 应用层     报文.主机 运输层     报文段. 网络层     数据报.路由器.IP 地址 链路层     以太网帧.交换机.MAC 地址 步骤 1)到 4) ...

  5. Redis实战--Jedis实现分布式锁

    echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!!! 分布式 ...

  6. SqlServer2005 查询 第二讲 distinct

    今天我们来说distinct关键字的用法. distinct - distinct:我们可以这样理解成[过滤重复值] - select distinct deptno from emp // -- 首 ...

  7. Python项目开发公用方法--excel生成方法

    在实际开发中,我们有时会遇到数据导出的需求.一般的,导出的文件格式为Excel形式. 那么,excel的生成就适合抽离出一个独立的公用方法来实现: def generate_excel(excel_n ...

  8. 迁移桌面程序到MS Store(12)——WPF使用UWP InkToolbar和InkCanvas

    我们在<迁移桌面程序到MS Store(4)——桌面程序调用Win10 API>提到了对Win10 API的调用,但仍存在无法在WPF中使用UWP控件的问题,虽然都是XAML控件,但却是两 ...

  9. Running serveral https server on a single IP address

    Nginx 在一个IP上配置多个https server时,默认只会发送默认server name的证书.这是由ssl 协议本身行为导致的:先建立ssl connection,后发送http请求.即n ...

  10. VSCode, Django, and Anaconda开发环境集成配置[Windows]

    之前一直是在Ubuntu下进行Python和Django开发,最近换了电脑,把在Virtual Box 下跑的Ubuntu开发机挪过来总是频繁崩溃,索性就尝试把开发环境挪到Windows主力机了. 不 ...