美文网首页
iOS 在代码中实现约束的几种方式

iOS 在代码中实现约束的几种方式

作者: Superman168 | 来源:发表于2019-03-18 14:09 被阅读0次

    前言

    在之前 IOS xib 或 Storyboard xib view 创建布局及手动修改frame 这篇文章中简单说明了 xib view 的创建及修改 frame 问题,但是可能满足不了一些项目的需求,例如:在 View 创建完成添加到父视图后,需要在xib View 中某个位置添加多个自定义的小View,显然这个时候单纯修改 XibView frame 就无法满足了。

    效果如下:


    IMG_726AC6FDC4D9-1.jpeg

    外面的为一个 大的 XibView , 有背景颜色的为新添加的小的 View,点击添加途经点可以无限添加。

    解决方法一

    笨办法就是每次添加重新计算 影响 大的 XibView 的子控件的位置,然后重新设置,也算是一种方法,但是这样很麻烦。

    解决方法二

    苹果提供的 Autolayout ,用代码来添加约束,这也有几种不同的方式。

    1. 使用 苹果原生的 约束布局,NSLayouyConstraint。

    2. 使用基于 NSLayoutConstraint 封装的第三方布局框架 Masonry 。

    3. 使用基于 NSLayouyConstraint 的一个非常轻量级的三方框架 UIView+AutoLayout。
      先贴出github地址:https://github.com/jrturton/UIView-Autolayout

    NSLayouyConstraint 介绍

    NSLayouyConstraint,API 较繁琐,代码量很多,而且大多都是重复性的代码,以至于好多人都不想用这个框架。但使用还比较简单,下面细述。

    在给控件添加约束的过程中,主要是这个方法

            NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:addPointView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:carView.viaView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
            [addPointView addConstraint:height];
            [addPointView addConstraints:@[left,right]];
    

    一个是添加的数组,一个添加属性约束.

    两点必须说明:
    1:控件初始化的时候不要设置他 frame。
    2:也是重中之重的,不然会导致严重的性能问题,一定要对当前控件的
    translatesAutoresizingMaskIntoConstraints的属性设置为NO,不然无法使用。。。
    首先看看这两个约束的方法

    具体参数
    constraintWithItem:要设置的控件的名称
    attribute:要设置的控件的属性
    relatedBy:相对关系(NSLayoutRelationEqual)
    toItem:相对于哪个视图
    attribute:相对于哪个视图的属性
    multiplier:相对缩放比例
    constant 设置的固定值
    可以看 API 的注释

    /* Create constraints explicitly.  Constraints are of the form "view1.attr1 = view2.attr2 * multiplier + constant" 
     If your equation does not have a second view and attribute, use nil and NSLayoutAttributeNotAnAttribute.
     */
    +(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
    
    1. AutoLayout 代码约束—VFL(Visual Format Language)

    下面开始说约束的VFL代码

    /**
     *  VFL创建约束的API
     *
     *  @param format  传入某种格式构成的字符串,用以表达想要添加的约束,如@"H:|-margin-[redView(50)]",水平方向上,redView与父控件左边缘保持“margin”间距,redView的宽为50
     *  @param opts    对齐方式,是个枚举值
     *  @param metrics 一般传入以间距为KEY的字典,如: @{ @"margin":@20},KEY要与format参数里所填写的“margin”相同
     *  @param views   传入约束中提到的View,也是要传入字典,但是KEY一定要和format参数里所填写的View名字相同,如:上面填的是redView,所以KEY是@“redView”
     *
     *  @return 返回约束的数组
     */
    
    /* Create an array of constraints using an ASCII art-like visual format string.
     */
    + (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)views;
    

    NSDictionary * views = NSDictionaryOfVariableBindings(view1,view2); NSLayoutConstraint *constraint = [NSLayoutConstraint constraintsWithVisualFormat:@"|-30-[lastPointView]-30-|" options:0 metrics:nil views:<#(nonnull NSDictionary<NSString *,id> *)#> views]

    它的API短了一些,但是要筹齐参数是件很麻烦的事
    字典类型参数的views 是要添加约束的对象的集合,opts 为 NSLayoutFormatOptions 枚举。
    最重要的就是format参数,而这个参数的难点在于其书写格式:

    每句前面都要加@"H:"或者@"V:",分别代表着水平和垂直方向
    @"|"代表着边界
    @"-"用来表示间隙,一般以这样的形式出现@"-20-",这代表20的间距,也可以填写标识,如@"-margin-",之后设置替换参数metrics
    @"[]"中括号里放的就是要添加约束的View,如上边例子的redView,想要设置宽度或者度,就这样[redView(50)],水平方向(H:)填写这个数字代表的就是宽,垂直方向就是高(V:)

    小结

    • VFL实现Autolayout是有局限性的,使用起来并不是那么的方便,随心所欲。
    • 这里VFL仅供大家了解,建议大家使用 Masonry 这个第三方来实现Autolayout,Masonry 这个框架相信大家都用的很熟悉了。

    Masonry 简单使用及分析

    Github地址:
    https://github.com/SnapKit/Masonry

    Masonry基于NSLayoutConstraint封装的第三方布局框架Masonry使用起来非常方便,Masonry采取了链式编程的方式,代码理解起来非常清晰易懂,而且写完之后代码量看起来非常少,但是 Masonry 也有一些缺陷和问题。

    Masonry中的坑

    在使用Masonry进行约束时,有一些是需要注意的。

    1. 在使用Masonry添加约束之前,需要在addSubview之后才能使用,否则会导致崩溃。
    2. 在添加约束时初学者经常会出现一些错误,约束出现问题的原因一般就是两种:约束冲突和缺少约束。对于这两种问题,可以通过调试和log排查。
    3. 之前使用Interface Builder添加约束,如果约束有错误直接就可以看出来,并且会以红色或者黄色警告体现出来。而Masonry则不会直观的体现出来,而是以运行过程中崩溃或者打印异常log体现,所以这也是手写代码进行AutoLayout的一个缺点。
      这个问题只能通过多敲代码,积攒纯代码进行AutoLayout的经验,慢慢就用起来越来越得心应手了。
    4. 用 Masonry添加约束之后,可能需要用代码来获取或修改 frame,而立即获取 frame 是获取不到的,因为加载顺序的先后问题,需要延迟调用 dispatch_after 才可以。
      延迟 0 s 就可以。
    //定义一个C函数,用来简单封装一下dispatch_after
    void didLayout(void(^layout)(void)) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (layout) layout();
        });
    }
    

    具体怎么使用就不说了,大家应该都用的很熟练了,API 一看大概就明白了。

    UIView-Autolayout 的简单使用

    最后说一下 UIView-Autolayout 的简单使用,众所周知的Autolayout可以在storyboard里通过添加约束来实现,这样是比较简单的.如果用代码的话,是比较麻烦的,包括用VLF语言开发效率也不是很高.那么这里给大家推荐一个非常轻量级的三方框架,准确来说也不算是一个框架,它是给UIView添加一个分类,使用起来非常简单.

    类似如下:

            [addPointView autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:carView.viaView withOffset:0];
            [addPointView autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:carView.viaView withOffset:0];
            [addPointView autoPinEdge:ALEdgeRight toEdge:ALEdgeRight ofView:carView.viaView withOffset:0];
            [addPointView autoSetDimensionsToSize:CGSizeMake(ScreenW, 50)];
    

    只是一个添加了一个 category ,API 一看就明白了,使用也很顺手,用 XibView 和 代码结合也很好用,强烈推荐大家使用,自认为是这几种方式中最简单最实用的方式。

    相关文章

      网友评论

          本文标题:iOS 在代码中实现约束的几种方式

          本文链接:https://www.haomeiwen.com/subject/dhbbuqtx.html