iOS 8 Auto Layout界面自动布局系列4-使用VFL添加布局约束


原文链接:http://www.itnose.net/detail/6249005.html

本系列第一篇文章介绍了自动布局的基本原理,第二篇通过一个简单的例子演示了如何使用IB以可视化方式创建自动布局约束,第三篇使用代码直接创建NSLayoutConstraint实例来定义自动布局约束。本篇文章在第三篇文章的基础上,使用Visual Format Language(暂且翻译为可视化格式语言,简称VFL)创建约束。
在第三篇文章中,我们仅仅创建了4个视图,就需要创建将近20个NSLayoutConstraint实例,而且每次创建NSLayoutConstraint实例时都需要传入7个参数(firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant),非常繁琐而且容易出错。在实际项目中,视图的层次会更复杂,约束的数量就会成倍增加,有没有办法既直观又简单地创建约束?那你不妨试试VFL,这也是这篇文章的主题。

VFL的使用非常简单直观,不过既然是一门语言,必然就有其语法要求。。。
又要学语法。。。你是逗我吗?
好吧,我们还是以之前的例子边做边讲吧。
纵屏

横屏

打开Xcode,新建项目,选择iOS -> Application -> Single View Application。项目命名为AutoLayoutByVFL,语言任意选择(本篇文章使用的是Objective-C),设备选择Universal。下载苹果Logo图片apple.jpg,并将其拖入项目中。文件下载地址:
http://yunpan.cn/cfmJB82dfSwf6(提取码:4049)

界面上方用来显示苹果Logo图片的是一个UIImageView,其具有如下4个约束:
- logoImageView左侧与父视图左侧对齐
- logoImageView右侧与父视图右侧对齐
- logoImageView顶部与父视图顶部对齐
- logoImageView高度为父视图高度一半

将ViewController类的viewDidLoad方法修改如下:

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIImageView* logoImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"apple.jpg"]];
    logoImageView.translatesAutoresizingMaskIntoConstraints = NO;
    logoImageView.contentMode = UIViewContentModeScaleAspectFit;
    [self.view addSubview:logoImageView];

    //水平方向上,logoImageView左侧与父视图左侧对齐,logoImageView右侧与父视图右侧对齐
    NSArray* hConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[logoImageView]-0-|" options:0 metrics:nil views:@{@"logoImageView": logoImageView}];
    [NSLayoutConstraint activateConstraints:hConstraintArray];

    //垂直方向上,logoImageView顶部与父视图顶部对齐
    NSArray* vConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[logoImageView]" options:0 metrics:nil views:@{@"logoImageView": logoImageView}];
    [NSLayoutConstraint activateConstraints:vConstraintArray];

    //logoImageView高度为父视图高度一半
    NSLayoutConstraint* heightConstraint = [NSLayoutConstraint constraintWithItem:logoImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:0.5f constant:0.0f];
    heightConstraint.active = YES;
}

viewDidLoad方法首先调用了NSLayoutConstraint类的一个静态方法constraintsWithVisualFormat:…..,根据传入的VFL字符串生成若干约束,该方法定义如下:

+ (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views;

参数format是一个符合VFL语法的字符串。上述代码中的H:表示本VFL字符串描述的是水平方向的约束,与之相对的是V:表示垂直方向。如果VFL字符串没有指明H:还是V:,则默认为水平方向。|表示父视图。VFL要求所有视图的名字必须放在中括号之内,[logoImageView]指代的就是logoImageView。-0-表示的是间距值为0。
所以@”H:|-0-[logoImageView]-0-|”表示在水平方向上,logoImageView左侧与其父视图左侧的间距为0,logoImageView右侧与其父视图右侧的间距为0。@”V:|-0-[logoImageView]”表示在垂直方向上,logoImageView顶部与其父视图顶部的间距为0。
另外说一句,-0-可以不写,也就是说如果间距为0则不用明确写出,所以@”H:|-0-[logoImageView]-0-|”可以精简为@”H:|[logoImageView]|”,@”V:|-0-[logoImageView]”可以精简为@”V:|[logoImageView]”,是不是很直观?
参数opts表示布局的对齐方式与方向:

typedef NS_OPTIONS(NSUInteger, NSLayoutFormatOptions)
{
    NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft),
    NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight),
    NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop),
    NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom),
    NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading),
    NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing),
    NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX),
    NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY),
    NSLayoutFormatAlignAllBaseline = (1 << NSLayoutAttributeBaseline),
    NSLayoutFormatAlignAllLastBaseline = NSLayoutFormatAlignAllBaseline,
    NSLayoutFormatAlignAllFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0) = (1 << NSLayoutAttributeFirstBaseline),

    NSLayoutFormatAlignmentMask = 0xFFFF,

    /* choose only one of these three */
    NSLayoutFormatDirectionLeadingToTrailing = 0 << 16, // default
    NSLayoutFormatDirectionLeftToRight = 1 << 16,
    NSLayoutFormatDirectionRightToLeft = 2 << 16,  

    NSLayoutFormatDirectionMask = 0x3 << 16,  
};

参数metrics是一个字典。其中的键(Key)是字符串,出现在VFL语句中;值(Value)是NSNumber对象。在解析VFL字符串时,编译器将自动把键替换为其对应的值,目的是使VFL更容易明白和理解。
另外,在解析VFL时,UIKit需要知道VFL字符串中的视图名称究竟对应哪个真实的视图,视图映射字典参数views就用来提供这个信息。其中的键(Key)是字符串,出现在VFL语句中;值(Value)是UIView对象。上述代码中的@{@”logoImageView”: logoImageView}表示的就是字符串@”logoImageView”对应视图logoImageView。
不过有个很遗憾的事实要告诉你,VFL并不能表达所有的约束,例如“logoImageView高度为父视图高度一半”这样的具有比例关系的约束,就无法使用VFL表达出来,所以这时我们只能直接创建NSLayoutConstraint实例了,就像上面的代码那样。
接着我们添加UIScrollView,在viewDidLoad方法中添加如下代码:

    UIScrollView* scrollView = [UIScrollView new];
    scrollView.translatesAutoresizingMaskIntoConstraints = NO;
    scrollView.backgroundColor = [UIColor blueColor]; //为了方便查看效果,暂时将scrollView背景色设置为蓝色
    [self.view addSubview:scrollView];

    //水平方向上,scrollView左侧与父视图左侧对齐,scrollView右侧与父视图右侧对齐
    NSArray* hScrollViewConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(scrollView)];
    [NSLayoutConstraint activateConstraints:hScrollViewConstraintArray];

    //垂直方向上,scrollView顶部与logoImageView底部对齐,scrollView底部与父视图底部对齐
    NSArray* vScrollViewConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[logoImageView][scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(logoImageView, scrollView)];
    [NSLayoutConstraint activateConstraints:vScrollViewConstraintArray];

需要额外说明的是,这里调用了一个NSDictionaryOfVariableBindings宏,它能够方便我们构建字典参数。简单说来,NSDictionaryOfVariableBindings(scrollView)就等于@{@”scrollView”: scrollView},NSDictionaryOfVariableBindings(logoImageView, scrollView)就等于@{@”logoImageView”: logoImageView, @”scrollView”: scrollView}。
另外,在垂直方向上,我们可以把之前的@”V:|[logoImageView]”与刚才的@”V:[logoImageView][scrollView]|”合并为一句@”V:|[logoImageView][scrollView]|”,就不需要分别创建heightConstraint与vScrollViewConstraintArray了,达到进一步精简的目的。

接着我们添加scrollView中的两个UILabel对象,在viewDidLoad方法中添加如下代码:

    UILabel* nameLabel = [UILabel new];
    nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
    nameLabel.text = @"苹果公司";
    nameLabel.backgroundColor = [UIColor greenColor];
    [scrollView addSubview:nameLabel];

    UILabel* descriptionLabel = [UILabel new];
    descriptionLabel.translatesAutoresizingMaskIntoConstraints = NO;
    descriptionLabel.text = @"苹果公司(Apple Inc. )是美国的一家高科技公司。由史蒂夫·乔布斯、斯蒂夫·沃兹尼亚克和罗·韦恩(Ron Wayne)等三人于1976年4月1日创立,并命名为美国苹果电脑公司(Apple Computer Inc. ), 2007年1月9日更名为苹果公司,总部位于加利福尼亚州的库比蒂诺。\n苹果公司创立之初主要开发和销售的个人电脑,截至2014年致力于设计、开发和销售消费电子、计算机软件、在线服务和个人计算机。苹果的Apple II于1970年代助长了个人电脑革命,其后的Macintosh接力于1980年代持续发展。该公司硬件产品主要是Mac电脑系列、iPod媒体播放器、iPhone智能手机和iPad平板电脑;在线服务包括iCloud、iTunes Store和App Store;消费软件包括OS X和iOS操作系统、iTunes多媒体浏览器、Safari网络浏览器,还有iLife和iWork创意和生产力套件。苹果公司在高科技企业中以创新而闻名世界。\n苹果公司1980年12月12日公开招股上市,2012年创下6235亿美元的市值记录,截至2014年6月,苹果公司已经连续三年成为全球市值最大公司。苹果公司在2014年世界500强排行榜中排名第15名。2013年9月30日,在宏盟集团的“全球最佳品牌”报告中,苹果公司超过可口可乐成为世界最有价值品牌。2014年,苹果品牌超越谷歌(Google),成为世界最具价值品牌 。";
    descriptionLabel.numberOfLines = 0;
    descriptionLabel.backgroundColor = [UIColor yellowColor];
    [scrollView addSubview:descriptionLabel];


    //水平方向上,nameLabel左侧与父视图左侧对齐,nameLabel右侧与父视图右侧对齐,nameLabel宽度与logoImageView宽度相同
    NSArray* hNameLabelConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameLabel(==logoImageView)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(nameLabel, logoImageView)];
    [NSLayoutConstraint activateConstraints:hNameLabelConstraintArray];

    //水平方向上,descriptionLabel左侧与父视图左侧对齐,descriptionLabel右侧与父视图右侧对齐,descriptionLabel宽度与logoImageView宽度相同
    NSArray* hDescriptionLabelConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[descriptionLabel(==logoImageView)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel, logoImageView)];
    [NSLayoutConstraint activateConstraints:hDescriptionLabelConstraintArray];

    //垂直方向上,nameLabel顶部与父视图顶部对齐,nameLabel高度为20,nameLabel底部与descriptionLabel顶部对齐,descriptionLabel底部与父视图底部对齐
    NSArray* vLabelConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[nameLabel(20)][descriptionLabel]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(nameLabel, descriptionLabel)];
    [NSLayoutConstraint activateConstraints:vLabelConstraintArray];

其中@”H:|[nameLabel(==logoImageView)]|”表示nameLabel宽度与logoImageView宽度相等,@”V:|[nameLabel(20)][descriptionLabel]|”表示nameLabel的宽度为20。到此我们就完成了这个例子,在此附上全部代码:

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIImageView* logoImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"apple.jpg"]];
    logoImageView.translatesAutoresizingMaskIntoConstraints = NO;
    logoImageView.contentMode = UIViewContentModeScaleAspectFit;
    [self.view addSubview:logoImageView];

    UIScrollView* scrollView = [UIScrollView new];
    scrollView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:scrollView];

    //水平方向上,logoImageView左侧与父视图左侧对齐,logoImageView右侧与父视图右侧对齐
    NSArray* hConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[logoImageView]|" options:0 metrics:nil views:@{@"logoImageView": logoImageView}];
    [NSLayoutConstraint activateConstraints:hConstraintArray];

    //垂直方向上,logoImageView顶部与父视图顶部对齐,logoImageView底部与scrollView顶部对齐,scrollView底部与父视图底部对齐
    NSArray* vConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[logoImageView][scrollView]|" options:0 metrics:nil views:@{@"logoImageView": logoImageView, @"scrollView": scrollView}];
    [NSLayoutConstraint activateConstraints:vConstraintArray];

    //logoImageView高度为父视图高度一半
    NSLayoutConstraint* heightConstraint = [NSLayoutConstraint constraintWithItem:logoImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:0.5f constant:0.0f];
    heightConstraint.active = YES;

    //水平方向上,scrollView左侧与父视图左侧对齐,scrollView右侧与父视图右侧对齐
    NSArray* hScrollViewConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(scrollView)];
    [NSLayoutConstraint activateConstraints:hScrollViewConstraintArray];

    UILabel* nameLabel = [UILabel new];
    nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
    nameLabel.text = @"苹果公司";
    nameLabel.backgroundColor = [UIColor greenColor];
    [scrollView addSubview:nameLabel];

    UILabel* descriptionLabel = [UILabel new];
    descriptionLabel.translatesAutoresizingMaskIntoConstraints = NO;
    descriptionLabel.text = @"苹果公司(Apple Inc. )是美国的一家高科技公司。由史蒂夫·乔布斯、斯蒂夫·沃兹尼亚克和罗·韦恩(Ron Wayne)等三人于1976年4月1日创立,并命名为美国苹果电脑公司(Apple Computer Inc. ), 2007年1月9日更名为苹果公司,总部位于加利福尼亚州的库比蒂诺。\n苹果公司创立之初主要开发和销售的个人电脑,截至2014年致力于设计、开发和销售消费电子、计算机软件、在线服务和个人计算机。苹果的Apple II于1970年代助长了个人电脑革命,其后的Macintosh接力于1980年代持续发展。该公司硬件产品主要是Mac电脑系列、iPod媒体播放器、iPhone智能手机和iPad平板电脑;在线服务包括iCloud、iTunes Store和App Store;消费软件包括OS X和iOS操作系统、iTunes多媒体浏览器、Safari网络浏览器,还有iLife和iWork创意和生产力套件。苹果公司在高科技企业中以创新而闻名世界。\n苹果公司1980年12月12日公开招股上市,2012年创下6235亿美元的市值记录,截至2014年6月,苹果公司已经连续三年成为全球市值最大公司。苹果公司在2014年世界500强排行榜中排名第15名。2013年9月30日,在宏盟集团的“全球最佳品牌”报告中,苹果公司超过可口可乐成为世界最有价值品牌。2014年,苹果品牌超越谷歌(Google),成为世界最具价值品牌 。";
    descriptionLabel.numberOfLines = 0;
    descriptionLabel.backgroundColor = [UIColor yellowColor];
    [scrollView addSubview:descriptionLabel];

    //水平方向上,nameLabel左侧与父视图左侧对齐,nameLabel右侧与父视图右侧对齐,nameLabel宽度与logoImageView宽度相同
    NSArray* hNameLabelConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[nameLabel(==logoImageView)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(nameLabel, logoImageView)];
    [NSLayoutConstraint activateConstraints:hNameLabelConstraintArray];

    //水平方向上,descriptionLabel左侧与父视图左侧对齐,descriptionLabel右侧与父视图右侧对齐,descriptionLabel宽度与logoImageView宽度相同
    NSArray* hDescriptionLabelConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[descriptionLabel(==logoImageView)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel, logoImageView)];
    [NSLayoutConstraint activateConstraints:hDescriptionLabelConstraintArray];

    //垂直方向上,nameLabel顶部与父视图顶部对齐,nameLabel高度为20,nameLabel底部与descriptionLabel顶部对齐,descriptionLabel底部与父视图底部对齐
    NSArray* vLabelConstraintArray = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[nameLabel(20)][descriptionLabel]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(nameLabel, descriptionLabel)];
    [NSLayoutConstraint activateConstraints:vLabelConstraintArray];
}

与第三篇文章中逐个去创建NSLayoutConstraint对象相比,是不是简单直观了不少?程序最终项目文件链接:
http://yunpan.cn/cVE8i8WmnpJwv(提取码:d348)
本篇文章我们初步了解了VFL的基本使用方式,关于VFL的具体语法格式请参看苹果的《Auto Layout Guide》,链接:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010853
在本系列后续文章中,我将继续介绍自动布局中的动画实现、事件处理、调试,以及iOS 8中新添加的Size Class的使用,敬请期待吧。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值