Auto Layout和UILabel

原创 2015年05月25日 16:40:09

前段时间千牛iOS版本也从iOS 6.0开始支持,所以可以正式引入Auto Layout来进行界面布局。
这里记录下在UILabel上应用Auto Layout进行布局的过程。

一、业务场景

  1. 用三个UILabel展示一件商品的基本信息:标题,价格,销量;
  2. 标题排在最上面,左右两边至少留出20的边距,可以换行;
  3. 价格排在标题下面,左边与标题对齐,顶部和标题留出10的边距;
  4. 销量排在价格右边,字体略小,底部和价格对齐,左边留出10的边距;

类似如下布局:
_2015_05_25_2_13_02

二、准备工作

首先,新建一个界面如下,添加三个UILabel和一个UIButton:
_2015_05_25_2_17_54

接着,从淘宝网上找几件宝贝作为商品数据:

- (void)prepareTestData {
    self.data = @[@{@"title": @"【1元预定魔镜S超值礼包】数量有限先到先得(单拍不发货不退款)",
                    @"price": @"¥ 1.00",
                    @"soldQuantity": @"月销量1133"},

                  @{@"title": @"预售 lovo罗莱家纺出品床上用品全纯棉床单四件套件 多彩格拉斯哥 \
                        虎妈猫爸 剧中同款 全棉斜纹 手感柔软 活泼跳跃",
                    @"price": @"预售价 ¥299.00",
                    @"soldQuantity": @"月销量694"},

                  @{@"title": @"小狗扫地机器人家用智能扫地机清洁吸尘器薄静音全自动充电V-M611 \
                        高效智能清洁 顺丰包邮",
                    @"price": @"¥ 1299.00",
                    @"soldQuantity": @"月销量123"},

                  @{@"title": @"小狗吸尘器",
                    @"price": @"¥ 299.00",
                    @"soldQuantity": @"月销量233"}];
}

三个UILabel的作用是分别显示title、price和soldQuantity字段的,而UIButton是用来切换数据,从而观察三个UILabel在不同内容下的布局情况。
一开始的情况是:
_2015_05_25_3_39_01

三、约束描述

Auto Layout直译过来就是自动布局,是通过创建一些数学上的关系描述来计算出各个View的布局信息,从而能够创建出通用的界面布局来合理地响应变化。
数学上的关系描述形如priceLabel.top = titleLabel.bottom * 1 + 10,在Objective-C中以NSLayoutConstraint的形式存在:

/* 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:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

四、添加约束

我们先为titleLabel添加约束。
如果是用代码手动添加约束的话,第一步需要做的是:

self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;

从命名就可以理解,在使用Auto Layout进行布局时,我们就不要将AutoresizingMask转换为约束条件了,以免遇到一些不必要的冲突。

接着,我们按照业务场景来为titleLabel创建约束条件:

    NSLayoutConstraint *c1 = [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:150];

    NSLayoutConstraint *c2 = [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:20];

    NSLayoutConstraint *c3 = [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1 constant:-20];

第一个约束c1是指定titleLabel的top,因为业务场景没有描述,所以这里先设置了个150,保证可见;
第二个约束c2是保证左边距留20;
第三个约束c3是保证右边距最少要留20;
同时,我们在xib文件中将其Lines设置为0,表示支持多行。

这里的约束条件是描述self.titleLabelself.view之间的关系,那么应该将这些约束条件添加到这二者共有的superView上,即self.view,从而使其具有足够的布局信息:

[self.view addConstraints:@[c1, c2, c3]];

五、约束冲突

接着,我们直接运行程序,会遇到如下问题:

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSIBPrototypingLayoutConstraint:0x7c281f80 'IB auto generated at build time for view with fixed frame' V:|-(165)-[UILabel:0x7c283780]   (Names: '|':UIView:0x7c283a30 )>",
    "<NSLayoutConstraint:0x7c281230 V:|-(150)-[UILabel:0x7c283780]   (Names: '|':UIView:0x7c283a30 )>"
)

Will attempt to recover by breaking constraint 
<NSIBPrototypingLayoutConstraint:0x7c281f80 'IB auto generated at build time for view with fixed frame' V:|-(165)-[UILabel:0x7c283780]   (Names: '|':UIView:0x7c283a30 )>

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

日志输出很明显地指出不能同时满足多个约束条件,然后下面列出了具体的相互冲突的约束条件:

(
    "<NSIBPrototypingLayoutConstraint:0x7c281f80 'IB auto generated at build time for view with fixed frame' V:|-(165)-[UILabel:0x7c283780]   (Names: '|':UIView:0x7c283a30 )>",
    "<NSLayoutConstraint:0x7c281230 V:|-(150)-[UILabel:0x7c283780]   (Names: '|':UIView:0x7c283a30 )>"
)

参考Visual Format Language,可以看出上面的约束条件是垂直距离165,而下面的约束条件是垂直距离150,无法同时满足。

那么这个垂直距离165的约束是哪里来的呢?IB auto generated at build time for view with fixed frame——IB自动生成的。参考这个解决方案,我们先在IB中添加上足够的约束,并勾选上Remove at build time

_2015_05_25_3_42_37

六、多行文本

从上图可以看出,虽然在xib中制定了Lines为0,但实际布局中并没有换行,这是因为在Auto Layout中需要设置一个属性来制定最大宽度:

self.titleLabel.preferredMaxLayoutWidth = self.view.frame.size.width - 40;

这里是已知业务场景,左右两端都至少要留白20,所以直接减去40。而在一些场景中,如果无法实现得知preferredMaxLayoutWidth的值,那么就需要分成两步来做(参考Intrinsic Content Size of Multi-Line Text):

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [self.view layoutIfNeeded];
}

_2015_05_25_3_48_45

这样一来,titleLabel就支持多行了。

七、剩余约束

接下来,就是为另外两个UILabel添加约束了。同样地,也会遇到IB自动生成的约束冲突。按同样的方案解决冲突后运行:
_2015_05_25_4_09_32

_2015_05_25_4_09_40

可以看到现在三个UILabel可以很灵活地适应不同的内容了。
这里有个问题就是IB自动生成的约束冲突很烦人,如果配套创建了xib文件,可以考虑尽量使用IB添加约束条件,并根据需要将约束条件作为outlet链接到ViewController中,以便后续调整。

为了不至于展示重复的添加约束代码,这里改为以VFL格式来对label进行约束(销量label距离价格label左边距为10,底部对齐):

    NSDictionary *dict = NSDictionaryOfVariableBindings(_priceLabel, _soldQuantityLabel);
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"[_priceLabel]-10-[_soldQuantityLabel]" options:NSLayoutFormatAlignAllBottom metrics:nil views:dict];
    [self.view addConstraints:constraints];

考虑到Auto Layout的代码很容易重复(比如距离superView的左边距多少之类的约束),可以考虑建立起可复用代码作为库。

(iOS)使用autolayout进行复杂布局时 UILabel的相关trick

本文首发于CSDN:http://blog.csdn.net/madongchunqiu/article/details/47960745。给心急的同学先说说结论:(因为我也是一个心急的同学) 对于U...
  • sbt0198
  • sbt0198
  • 2016-09-23 21:59:01
  • 909

iOS 10 Auto Layout界面自动布局系列3-使用原生NSLayoutConstraint添加布局约束

本系列的第一篇文章介绍了自动布局的基本原理,第二篇文章通过一个简单的例子演示了如何使用Xcode的Interface Builder(简称IB)以可视化方式添加约束。本篇为该系列的第三篇文章,主要介绍...
  • pucker
  • pucker
  • 2015-04-16 16:05:15
  • 34073

iOS 自动布局 Auto Layout 入门 06 详情页面 (a) 歌手名称Label

这一节开始,我们做一个更复杂的程序。我们需要为音乐播放程序构建一个详情页面,这个页面用于显示歌手的名字和专辑的一些详细信息: 创建一个名为ArtistDetail的single view应用程序,...
  • yamingwu
  • yamingwu
  • 2015-03-12 20:45:10
  • 903

iOS富文本(二)初识Text Kit

概述 Text Kit 是建立在Core Text上的文本布局系统,虽然没有Core Text那么强大的文本处理功能,但是对于大多数常见的文本布局用Text Kit能够很简单的实现,而不是用Core...
  • GGGHub
  • GGGHub
  • 2015-11-30 16:40:49
  • 1771

TextKit学习(三)NSTextStorage,NSLayoutManager,NSTextContainer和UITextView

使用UITextView时用到的iOS7新增加的类:NSTextContainer、NSLayoutManager、NSTextStorage,主要说一说NSTextStorage。...
  • u010962810
  • u010962810
  • 2013-08-17 15:02:28
  • 11328

UILabel基本用法总结

//设置标签文本对齐方式 label.textAlignment = NSTextAlignmentLeft; 1.设置 标签文本 和 属性文本 //设置标签显示文本 NS...
  • lucy0325
  • lucy0325
  • 2014-02-14 14:59:16
  • 1780

iOS 自动布局 Auto Layout 入门 06 详情页面 (c) 对左侧标签进行排版

前面两节我们解决了歌手名label和按钮的布局问题,接下来我们对界面中的一列标签进行自动布局。 对label进行排版 选中5个label选择Align\Right Edges: 为这些label添...
  • yamingwu
  • yamingwu
  • 2015-03-13 21:26:47
  • 710

使用storyboard添加控件,用代码为控件添加约束时,出现NSIBPrototypingLayoutConstraint冲突的解决办法

iOS7之后,如果在storyboard或XIB中添加控件,并且没有在storyboard中为该控件设置约束,但是标注了使用AutoLayout的话, 在运行时期,系统会默认为该控件添加NSIBPr...
  • chy305chy
  • chy305chy
  • 2016-08-17 18:11:49
  • 246

Auto Layout和UILabel

前段时间千牛iOS版本也从iOS 6.0开始支持,所以可以正式引入Auto Layout来进行界面布局。 这里记录下在UILabel上应用Auto Layout进行布局的过程。一、业务场景 用三个U...
  • jasonblog
  • jasonblog
  • 2015-05-25 16:40:09
  • 5857

iOS UILabel详解

·UILable是iPhone界面最基本的控件,主要用来显示文本信息。 ·常用属性和方法有: 1、创建 CGRect rect = CGRectMake(100, 200, 50, 50); UILa...
  • zhaopenghhhhhh
  • zhaopenghhhhhh
  • 2013-11-14 22:20:40
  • 50872
收藏助手
不良信息举报
您举报文章:Auto Layout和UILabel
举报原因:
原因补充:

(最多只允许输入30个字)