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

前言

在之前 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 和 代码结合也很好用,强烈推荐大家使用,自认为是这几种方式中最简单最实用的方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354