Autolayout - NSLayoutAnchor

iOS9引入了NSLayoutAnchor来固定视图的约束。NSLayoutAnchor是工厂类可以快速创建布局对象,它的出现可以让代码变得更加简洁,而且可以有静态检查能力,并提供了额外的约束正确保证。并且Apple推荐使用NSLayoutAnchor进行布局而不是使用NSLayoutConstraint。

NSLayoutAnchor

NSLayoutAnchor有3个子类:NSLayoutXAxisAnchor(X轴)水平方向约束、NSLayoutYAxisAnchor(Y轴)垂直方向约束、NSLayoutDimension(尺寸)宽和高,在实际的开发中我们经常使用子类而不是NSLayoutAnchor,添加约束设置参数时将使用他们。对于view可以使用的类型如下:

extension UIView {
    /* Constraint creation conveniences. See NSLayoutAnchor.h for details.
     */
    @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相对于另外一个view的Anchor时,需要保证Anchor是相同的子类,即一种类型的Anchor只允许绑定到另外一个相同类型的Anchor上。因为在添加约束设置参数时,会对参数进行检查,这样能够保证避免无效的约束。例如:编译器将拒绝把NSLayoutXAxisAnchor类的leadingAnchor的约束添加到NSLayoutYAxisAnchor类的topAnchor,因为类型不一样。


注意:约束leading/trailing anchors到left/right anchors在运行时是会崩溃的,Autolayout并不允许你混合leading与left anchors或者trailing和right anchors。

有了NSLayoutAnchor我们将不在需要像以前那样创建

NSLayoutConstraint(item: avatarView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0)

仅仅只需要直接使用,代码简洁了很多,添加约束方法会自动为我们返回NSLayoutConstraint对象。

avatarView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true

UIView的layoutMarginsGuide属性布局边距约束

UIView并没有提供anchor属性用于布局margin,相反我们可以使用UILayoutGuide提供的layoutMarginsGuide属性进行布局,如:

view.addSubview(redView)
redView.translatesAutoresizingMaskIntoConstraints = false

redView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
redView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
redView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor).isActive = true
redView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor).isActive = true

效果如下:


UILayoutGuide

UILayoutGuide可以理解为布局参展,一个矩形区域用于和AutoLayout进行交互,其相关功能依赖于NSLayoutAnchor来实现。UILayoutGuide也包含了一组NSLayoutAnchor属性,与UIView基本上差不多,二者布局方式也差不多,并且可以混合使用:即一个约束中的两个元素,一个来自视图,一个来自布局参照。

使用UILayoutGuide来取代占位视图(dummy view)也可以用做容器视图,封装视图,一个占位视图是一个空视图(empty view)并不包含任意的可见元素,仅仅是用于定义一个矩形区域,例如:如果你想在两个视图之间约束一个大小,那么可以使用占位视图来代表间距。占位视图也与缺点:1)需要创建对象和维护 2)大量的占位视图存在于视图结构中会影响性能 3)占位视图可能拦截触摸事件


创建LayoutGuide

1)初始化UILayoutGuide对象

2)调用view的addLayoutGuide方法添加layout guide对象

3)使用AutoLayout定义layout guide对象的位置和大小


创建3个等间距的视图

 func setupConstraints() {
        let space1 = UILayoutGuide()
        view.addLayoutGuide(space1)

        let space2 = UILayoutGuide()
        view.addLayoutGuide(space2)

        space1.widthAnchor.constraint(equalTo: space2.widthAnchor).isActive = true
        yellowView.trailingAnchor.constraint(equalTo: space1.leadingAnchor).isActive = true
        redView.leadingAnchor.constraint(equalTo: space1.trailingAnchor).isActive = true
        redView.trailingAnchor.constraint(equalTo: space2.leadingAnchor).isActive = true
        greenView.leadingAnchor.constraint(equalTo: space2.trailingAnchor).isActive = true

        yellowView.translatesAutoresizingMaskIntoConstraints = false

        let yellowViewLeading = yellowView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10)
        let yellowViewCenterY = yellowView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        let yellowViewWidth = yellowView.widthAnchor.constraint(equalToConstant: 100)
        let yellowViewHeight = yellowView.heightAnchor.constraint(equalToConstant: 100)
        NSLayoutConstraint.activate([yellowViewLeading, yellowViewCenterY, yellowViewWidth, yellowViewHeight])

        redView.translatesAutoresizingMaskIntoConstraints = false
        let redViewCenterY = redView.centerYAnchor.constraint(equalTo: yellowView.centerYAnchor)
        let redViewWidth =  redView.widthAnchor.constraint(equalTo: yellowView.widthAnchor)
        let redViewHight = redView.heightAnchor.constraint(equalTo: yellowView.heightAnchor)
        NSLayoutConstraint.activate([redViewCenterY, redViewWidth, redViewHight])

        greenView.translatesAutoresizingMaskIntoConstraints = false
        let greenViewTrailing = greenView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10)
        let greenViewCenterY = greenView.centerYAnchor.constraint(equalTo: redView.centerYAnchor)
        let greenViewWidth =  greenView.widthAnchor.constraint(equalTo: redView.widthAnchor)
        let greenViewHight = greenView.heightAnchor.constraint(equalTo: redView.heightAnchor)
        NSLayoutConstraint.activate([greenViewTrailing, greenViewCenterY, greenViewWidth, greenViewHight])
    }

效果如下:



Demo简单添加两个视图并添加相应的约束

class ViewController: UIViewController {
    lazy var yellowView: UIView = {
        let view = UIView()
        view.backgroundColor = UIColor.yellow
        return view
    }()
    lazy var redView: UIView = {
        let view = UIView()
        view.backgroundColor = UIColor.red
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
        setupConstraints()
    }

    func setupView() {
        view.addSubview(yellowView)
        view.addSubview(redView)
    }

    func setupConstraints() {
        // 告诉系统使用auto layout而不是使用frame
        yellowView.translatesAutoresizingMaskIntoConstraints = false

        // isActive为true表示立马生效约束
        yellowView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 50).isActive = true
        yellowView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        yellowView.widthAnchor.constraint(equalToConstant: 100).isActive = true
        yellowView.heightAnchor.constraint(equalToConstant: 100).isActive = true

        redView.translatesAutoresizingMaskIntoConstraints = false
        redView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -50).isActive = true
        redView.centerYAnchor.constraint(equalTo: yellowView.centerYAnchor).isActive = true
        redView.widthAnchor.constraint(equalTo: yellowView.widthAnchor).isActive = true
        redView.heightAnchor.constraint(equalTo: yellowView.heightAnchor).isActive = true
    }
}

代码非常简单:

1)添加子视图到父视图

2)设置子视图的translateAutoresizingMaskIntoConstraints为false,禁止自动缩放,并使用AutoLayout布局,默认为true,在IB中自动设置为false

3)添加约束,并且调用isActive方法生效约束,因为默认情况下使用constraint方法添加的约束是没有使用的。当然如果不使用isActive方法可以,可以整体生效约束,也可以父视图的addConstraints方法即可,使用跟NSLayoutConstraint类一样,因为返回的就是NSLayoutConstraint对象。如下:

  func setupConstraints() {
        // 告诉系统使用auto layout而不是使用frame
        yellowView.translatesAutoresizingMaskIntoConstraints = false
        let yellowViewLeading = yellowView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 50)
        let yellowViewCenterY = yellowView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        let yellowViewWidth = yellowView.widthAnchor.constraint(equalToConstant: 100)
        let yellowViewHeight = yellowView.heightAnchor.constraint(equalToConstant: 100)

        NSLayoutConstraint.activate([yellowViewLeading, yellowViewCenterY, yellowViewWidth, yellowViewHeight])
        //view.addConstraints([yellowViewLeading, yellowViewCenterY, yellowViewWidth, yellowViewHeight])

        redView.translatesAutoresizingMaskIntoConstraints = false
        let redViewTrailing = redView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -50)
        let redViewCenterY = redView.centerYAnchor.constraint(equalTo: yellowView.centerYAnchor)
        let redViewWidth =  redView.widthAnchor.constraint(equalTo: yellowView.widthAnchor)
        let redViewHeight = redView.heightAnchor.constraint(equalTo: yellowView.heightAnchor)
        
         NSLayoutConstraint.activate([redViewTrailing, redViewCenterY, redViewWidth, redViewHeight])
        //view.addConstraints([redViewTrailing, redViewCenterY, redViewWidth, redViewHeight])
    }

效果如下:


参考:

NSLayoutAnchor

UILayoutGuide


猜你喜欢

转载自blog.csdn.net/longshihua/article/details/80080104