iOS 关于 AutoLayout 的初级探索

AutoLayout 是什么?

首先看字面意思就是自动布局,恩,对,AutoLayout讲的就是 iOS 中对于视图布局的自动布局。那么视图如何选择使用autolayout呢? 简单地说,它是经过约束来实现的。 约束就告诉自动布局引擎,咱们但愿它在此视图上执行布局以及布置视图的方式。 那么如今咱们了解一下什么是约束。数组

Constraints(约束)

NSLayoutConstraint是继承自NSObject的一个类,也就是用来作约束的一个类。约束是对于视图而言的。 视图能够有许多约束,那么咱们怎么给视图添加或者移除约束呢?bash

如今来讲两种方法给视图添加或者移除约束:less

**第一种方法:**经过UIView的实例方法来添加和移除自身的约束:(具体看下边 UIView 的扩展)布局

extension UIView {
  @available(iOS 6.0, *)
  open var constraints: [NSLayoutConstraint] { get }

    
  @available(iOS 6.0, *)
  open func addConstraint(_ constraint: NSLayoutConstraint)


  @available(iOS 6.0, *)
  open func addConstraints(_ constraints:
                                     [NSLayoutConstraint]) 


  @available(iOS 6.0, *)
  open func removeConstraint(_ constraint: 
                                      NSLayoutConstraint)

  @available(iOS 6.0, *)
  open func removeConstraints(_ constraints:
                                     [NSLayoutConstraint]) 

}


复制代码

第二种方法: 经过NSLayoutConstraint的一个类方法来给视图添加约束,具体以下: 在看NSLayoutConstraint的实例化方法的时候,会发现他还有类方法,咱们看下面两个方法:ui

// 添加约束
  @available(iOS 8.0, *)
  open class func activate(_ constraints:
                                      [NSLayoutConstraint])


// 移除约束
  @available(iOS 8.0, *)
  open class func deactivate(_ constraints:
                                      [NSLayoutConstraint])
复制代码

实例化NSLayoutConstraint

咱们能够看到以上两种方法都须要一个NSLayoutConstraint类型的参数,如何实例化一个NSLayoutConstraint对象呢? 那么咱们来看一下 NSLayoutConstraint 的初始化方法:spa

public convenience init(   item view1:  Any, 
                      attribute attr1:  NSLayoutAttribute,            
                   relatedBy relation:  NSLayoutRelation, 
                         toItem view2:  Any?, attribute 
                                attr2:  NSLayoutAttribute, 
                           multiplier:  CGFloat, 
                           constant c:  CGFloat)           
复制代码

初始化方法中有不少参数,接下来咱们来说一下各个参数的意义:code

item: 能够看到他后边还有一个 view1 ,我的认为通常的他就是一个 UIView 或者其子类对象,是要进行约束的那个视图orm

attribute: 是一个NSLayoutAttribute枚举,能够看到他的枚举值有 left、right、bottom、top 等,这个参数就是第一个参数中 view 所要进行的约束的位置cdn

relatedBy: 是一个NSLayoutRelation枚举,他的枚举值有 lessThanOrEqual(小于等于)、equal(等于)、 greaterThanOrEqual(大于等于)。 这个参数是用来指定 view1和接下来那个参数 view2两个视图之 间的约束关系的对象

toItem: 和第一个参数同样,这个主要就是来和第一个 view 作参照的那个 视图

attribute: 和第二个参数同样,是来表示第一个视图对第二个视图的 参考位置 ,上下左右 仍是 center等

multiplier: 乘数的意思,CGFloat类型。是来计算两个视图之间位置关系的 一个重要因素

constant: 常数, CGFloat类型。也是计算两个视图位置关系的重要因素

这几个参数所构造出来的NSLayoutConstraint实例,添加到视图以后的位置关系究竟是怎样的呢,能够用下面 这个公式 来计算得出:

item的attribute relatedBy toItem的attribute * multiplier + constant

简化以后就是:

A 视图    =      B视图  *  multiplier   +   constant;
复制代码

公式中的等于号能够根据 relatedBy 参数来变为 >= 或者 <=

接下来咱们来实现添加约束

第一种方法view 的实例方法

let viewItem = UIView()
	// 必定要禁止 autoresize
	viewItem.translatesAutoresizingMaskIntoConstraints = false
	viewItem.backgroundColor = UIColor.brown
	// 必须先添加到 View上 而后再去作约束 由于约束若是要用到父视图 不提早添加 怎么知道谁是父视图
	view.addSubview(viewItem)
	//  添加和父视图view X 中心对齐 
	let centerXConstrains = NSLayoutConstraint( item: viewItem, 
                                           attribute: .centerX, 
                                           relatedBy: .equal, 
                                              toItem: view, 
                                           attribute: .centerX, 
                                          multiplier: 1, constant: 0)

	//  添加和父视图 Y 中心对其的约束
	let centerYConstrains = NSLayoutConstraint( item: viewItem,
                                           attribute: .centerY, 
                                           relatedBy: .equal, 
                                              toItem: view, 
                                           attribute: .centerY, 
                                          multiplier: 1, 
                                            constant: 0)

	//  添加和父视图 宽的关系 为1:2约束
	let heightConstrains = NSLayoutConstraint( item: viewItem, 
                                          attribute: .width , 
                                          relatedBy: .equal, 
                                             toItem: view, 
                                          attribute: .width, 
                                         multiplier: 1 / 2, 
                                           constant: 0)
	//  添加自身视图宽 高的关系 为1:1 的约束
	let widthConstrains = NSLayoutConstraint( item: viewItem,
                                         attribute: .height , 
                                         relatedBy: .equal,
					                        toItem:  viewItem, 
				                         attribute: .width, 
				                        multiplier: 1, 
					                      constant: 0)
	// 必定要分清楚约束是相对于谁加的  加给谁的
  //		view.addConstraint(centerXConstrains)
  //		view.addConstraint(centerYConstrains)
  //		view.addConstraint(heightConstrains)
	
// 此处能够向上边注释的那样一个一个添加,也能够以一个数组来一块儿添加	
view.addConstraints([centerXConstrains,
                     centerYConstrains,
                      heightConstrains])
viewItem.addConstraint(widthConstrains)

复制代码

以上代码实现了一个中心点在父视图中心,宽等于父视图宽一半的一个正方形。细心的人会看到 addConstraint方法有的是 view 调用,有的是 viewItem 调用。的确约束具体加给谁也是有原则的。

具体总结以下:

1.若是两个视图(也就是参数 item 和 toItem)是父子关系,设置子控件的约束,约束添加到父控件上

2.若是两个视图(也就是参数 item 和 toItem)是兄弟关系,设置两兄弟的约束,约束会添加到第一个共同的父控件上

3.若是两个视图(也就是参数 item 和 toItem)是同一个视图,约束会添加到本身上

使用第二种方法 :NSLayoutConstraint的类方法实现约束 在上边咱们看了NSLayoutConstraint的类方法,很明显他是 iOS8 以后才适用的,这个方法参数是一个NSLayoutConstraint数组。 那么NSLayoutConstraint的实例化和上边同样,只是最终加给视图约束的方法不一样,具体实现以下:

let view1 = UIView()
view1.backgroundColor = UIColor.blue
view1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(view1)
// view1 和 viewItem 的 Y 中心对称		
let topContrians = NSLayoutConstraint(item: view1,       
                                 attribute: .top,  
                                 relatedBy: .equal,
                                    toItem: viewItem, 
                                 attribute: .centerY, 
                                multiplier: 1, 
                                  constant: 0)
// view1 和 viewItem 的 bottom 对其				
let bottomContrians = NSLayoutConstraint(item: view1, 
                                    attribute: .bottom,
                                    relatedBy: .equal, 
                                       toItem: viewItem, 
                                    attribute: .bottom, 
                                   multiplier: 1, 
                                     constant: 0)
// view1 和 viewItem 的 width 相等	
let widthContrains = NSLayoutConstraint(item: view1, 
                                   attribute: .width, 
                                   relatedBy: .equal,
                                      toItem: viewItem, 
                                   attribute: .width, 
                                  multiplier: 1, 
                                    constant: 0)
// view1 和 viewItem 的 leading 对齐		
let leadingContrains = NSLayoutConstraint(item: view1, 
                                     attribute: .leading, 
                                     relatedBy: .equal, 
                                        toItem: viewItem, 
                                     attribute: .leading, 
                                    multiplier: 1, 
                                      constant: 0)
// iOS8 之后 NSLayoutConstraint 的类方法 也能够把约束添加到视图上,并且省掉了判断添加到那个视图上的问题,避免了上面例子中由于视图添加错误而致使的崩溃
   NSLayoutConstraint.activate([topContrians,bottomContrians,widthContrains,leadingContrains])

复制代码

最终添加给视图约束的方法就是这个类方法。

Anchor notation 作约束

从iOS 9开始,又一种新的方式来作约束。 其 本质也是经过上边两种方法给视图添加约束,只不过是获取NSLayoutConstraint的实例使用了Anchor,就是经过 Anchor 来实现。

如下几个属性就是 View 的 Anchor notation扩展,主要也是视图的上下左右等约束点的Anchor

extension UIView {

    @available(iOS 9.0, *)
    open var leadingAnchor: NSLayoutXAxisAnchor { get }

    @available(iOS 9.0, *)
    open var trailingAnchor: NSLayoutXAxisAnchor { get }

    @available(iOS 9.0, *)
    open var leftAnchor: NSLayoutXAxisAnchor { get }

    @available(iOS 9.0, *)
    open var rightAnchor: NSLayoutXAxisAnchor { get }

    @available(iOS 9.0, *)
    open var topAnchor: NSLayoutYAxisAnchor { get }

    @available(iOS 9.0, *)
    open var bottomAnchor: NSLayoutYAxisAnchor { get }

    @available(iOS 9.0, *)
    open var widthAnchor: NSLayoutDimension { get }

    @available(iOS 9.0, *)
    open var heightAnchor: NSLayoutDimension { get }

    @available(iOS 9.0, *)
    open var centerXAnchor: NSLayoutXAxisAnchor { get }

    @available(iOS 9.0, *)
    open var centerYAnchor: NSLayoutYAxisAnchor { get }

    @available(iOS 9.0, *)
    open var firstBaselineAnchor: NSLayoutYAxisAnchor { get }

    @available(iOS 9.0, *)
    open var lastBaselineAnchor: NSLayoutYAxisAnchor { get }
}

复制代码

能够看到 View 的 Anchor 属性都是 NSLayoutYAxisAnchor类型的,那么NSLayoutYAxisAnchor是什么呢?

@available(iOS 9.0, *)
open class NSLayoutAnchor<AnchorType : AnyObject> : NSObject {

    open func constraint(equalTo anchor: 
           NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint

    open func constraint(greaterThanOrEqualTo anchor: 
           NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint

    open func constraint(lessThanOrEqualTo anchor:
           NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint

    open func constraint(equalTo anchor: 
            NSLayoutAnchor<AnchorType>,
                  constant c: CGFloat) -> NSLayoutConstraint

    open func constraint(greaterThanOrEqualTo anchor: 
            NSLayoutAnchor<AnchorType>,
                  constant c: CGFloat) -> NSLayoutConstraint

    open func constraint(lessThanOrEqualTo anchor: 
          NSLayoutAnchor<AnchorType>,
                  constant c: CGFloat) -> NSLayoutConstraint
}

@available(iOS 9.0, *)
open class NSLayoutXAxisAnchor:
               NSLayoutAnchor<*NSLayoutXAxisAnchor*> {
        }
复制代码

NSLayoutXAxisAnchor就是NSLayoutAnchor的一个子类,NSLayoutAnchor有6个constraint的方法,具体看一下参数就知道有什么区别了,这里就不一一赘述了。每一个方法均可以返回一个 NSLayoutConstraint 类型的实例。那么咱们就可使用返回的实例,利用上边的两种方法给视图添加约束了,具体以下:(此处用的 第二种类方法)

let view2 = UIView()
view.addSubview(view2)
view2.translatesAutoresizingMaskIntoConstraints = false
view2.backgroundColor = UIColor.cyan	NSLayoutConstraint.activate(

[view2.leadingAnchor.constraint(equalTo:   
                                 view1.leadingAnchor),

view2.trailingAnchor.constraint(equalTo:
                                 view1.trailingAnchor),

view2.topAnchor.constraint(equalTo: 
                                 view1.bottomAnchor),

view2.heightAnchor.constraint(equalTo: 
                                 view1.heightAnchor)])

复制代码

Visual format natation 来作约束

visual format 是一种基于文原本缩略的建立约束的速记(简写)方法 ,具备 容许同时描述多个约束的优势,而且特别适合于当水平或垂直地布置一系列视图的状况。 例子:"V:|[v2(10)]"

各个符号的意义:

V:表示正在的垂直维度; 若是是H:水平维度

|:竖线(|)表示父视图

v2: 视图的名称显示在方括号中,v2就是视图的名称

10:视图名称以后的小括号中的数字,表示视图的大小。 V的话就是高,H 就是宽,根据视图的排列方向而定

那么怎么样使用visual format呢? 咱们来看一下NSLayoutConstraint的另外一个类方法:

open class func constraints(withVisualFormat
                         format: String, 
                   options opts: NSLayoutFormatOptions = [],
                        metrics: [String : Any]?, 
                views: [String : Any])-> [NSLayoutConstraint]
复制代码

这个类方法最终返回了一个NSLayoutConstraint数组,那么咱们又可使用上边的两个方法给视图添加约束了

要使用visual format,必须提供一个字典,将visual format 字符串提到的每一个视图的字符串名称对应到实际视图,例如:

建立两个视图v1和 v2:

let v1 = UIView()
		v1.backgroundColor = UIColor.blue
		v1.translatesAutoresizingMaskIntoConstraints = false
		view.addSubview(v1)
		
		let v2 = UIView()
		v2.backgroundColor = UIColor.cyan
		v2.translatesAutoresizingMaskIntoConstraints = false
		view.addSubview(v2)
复制代码

而后就建立所谓的字典:

// 字典d 就是将字符串 v1对应实际的视图 v1 ,"v2"对应 v2.了解字典的就很
   容易懂什么意思
let d = ["v1":v1,"v2": v2]
复制代码

具体使用咱们经过一段代码来看:(如下代码中注释是对方法的第一个String 类型的参数作出的解释)

NSLayoutConstraint.activate([

  //左右两边都有 | 就是说水平方向父视图的两边 距离都为零
	NSLayoutConstraint.constraints(withVisualFormat:
				"H:|[v1]|", metrics: nil, views: d), 

  // 左边有 | 就是说竖直方向父视图的上方  距离为0 高度为100
	NSLayoutConstraint.constraints(withVisualFormat:
				"V:|[v1(100)]", metrics: nil, views: d),  

  // 左边有 | 就是说水平方向 距离父视图的左边为0 且宽度为140
	NSLayoutConstraint.constraints(withVisualFormat:
				"H:|[v2(40)]", metrics: nil, views: d),

  // 右边有 | 就是说竖直方向距离父视图下方为40 高度为120
	NSLayoutConstraint.constraints(withVisualFormat:
				"V:[v2]-(40)-|", metrics: nil, views: d), 

  // 没有  |(竖线),也就是说这个约束和父视图没有关系了。
    是[v1]-0-[v2]这样的一个形式,这说的是V(竖直)方向上v1和v2相聚为20
	NSLayoutConstraint.constraints(withVisualFormat: 
  "V:[v1]-20-[v2]", metrics: nil, views: d)].flatMap{$0})
复制代码

上边这种方法不经常使用,也就不作太多的解释了

总结

AutoLayout 就是要用约束来实现的,既然要用约束就离不开 NSLayoutConstraint这个类,最终获得NSLayoutConstraint的类实例,而后经过上边的两种方法任何一种就能够把约束添加到视图上,完成自动布局了。

建议仍是用类方法,出错的几率更低一些。

布局约束的重要计算公式:

View1    =       View2   *   multiplier   +   constant
复制代码

若是你会 StoryBoard随便拖进去一个控件作约束: 能够看到约束的这个界面,一目了然:

约束.png

哪两个视图(ViewItem 和 SuperView)在作约束, 参考的是视图的哪一个位置(CenterY), 两个视图之间的关系(Equal) 常量关系(Constant) 优先级 (Priority) 倍数关系 (Multiplier)

计算公式:

First Item    =       Second Item  *  Multiplier   +   Constant复制代码
相关文章
相关标签/搜索