AutoLayout的那些事(一)

来源:互联网 发布:张氏短打淘宝 编辑:程序博客网 时间:2024/06/06 18:14

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(){

        letthen = CFAbsoluteTimeGetCurrent()

        addConstraintsToButton()

        letnow = CFAbsoluteTimeGetCurrent()

        print(now - then)

    }

 

    requiredinit?(coder aDecoder: NSCoder){

        fatalError("init(coder:) has not been implemented")

    }

 

    letbuttonTag = 200

    func initManyButton(){

        for index in0...1000{

            letbutton = UIButton(type:.System)

            button.tag = buttonTag + index

            self.addSubview(button)

        }

    }

    func addConstraintsToButton(){

        for index in0...1000{

            ifletbutton = self.viewWithTag(index+buttonTag){

                button.snp_makeConstraints{makein

                    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后会让视图失去约束,走投无路。例如我自定义转场时就遇到了这样的问题,转场后的视图并不在视图的正中间。


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

0 0
原创粉丝点击