欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

AutoLayout VFL格式约束

程序员文章站 2022-05-07 08:18:37
...

 

AutoLayout  VFL可视化格式约束

一、VFL简介

iOS的自动布局约束主要有三种方式,

1.可视化界面中添加布局,比如在Storyboard xib。

2.代码添加当个约束

3.VFL格式约束

VFL添加约束的优点

1.可以一行代码添加多个约束和多个对象

2.动态性大

3.不依赖其他框架。

4.只需要直观的从垂直和水平方向考虑

现在在代码添加约束中有很多优秀的框架,它们的内部大都是用NSLayoutConstraint,在编写基础控件时,为了减少对其他库的依赖,最好使用原生的约束。

二、VFL语法

语法标记

AutoLayout VFL格式约束

标记示例

AutoLayout VFL格式约束

使用参数options

参与控件的 基础对齐方式
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),   //中心X对齐
    NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY),   //中心y对齐
    NSLayoutFormatAlignAllLastBaseline = (1 << NSLayoutAttributeLastBaseline),  // ????
    NSLayoutFormatAlignAllBaseline NS_SWIFT_UNAVAILABLE("Use 'alignAllLastBaseline' instead") = NSLayoutFormatAlignAllLastBaseline,  //基于底线对齐
    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,  
    
    /* choose only one spacing format
     */
    NSLayoutFormatSpacingEdgeToEdge API_AVAILABLE(ios(11.0),tvos(11.0)) = 0 << 19, // default
    
    /* Valid only for vertical layouts. Between views with text content the value
     will be used to determine the distance from the last baseline of the view above
     to the first baseline of the view below. For views without text content the top
     or bottom edge will be used in lieu of the baseline position.
     The default spacing "]-[" will be determined from the line heights of the fonts
     involved in views with text content, when present.
     */
    NSLayoutFormatSpacingBaselineToBaseline API_AVAILABLE(ios(11.0),tvos(11.0)) = 1 << 19,
    
    NSLayoutFormatSpacingMask API_AVAILABLE(ios(11.0),tvos(11.0)) = 0x1 << 19,
};

三、使用实例

下面是VFL约束实例(Objective-C),扩展UITableView  为其添加空白占位图提示

1.取消自动转换约束

iOS系统会自动将frame位置转换为约束布局,所以需要手动取消,否则自己添加的约束无效。

    [imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [label setTranslatesAutoresizingMaskIntoConstraints:NO];
    [placeholderView setTranslatesAutoresizingMaskIntoConstraints:NO];

2.绑定视图 

方便在VisualFormat字符串  []  中使用,找到对应的控件。

 //  绑定视图  在 [] 中使用 这样才能找到相应的控件
    NSDictionary *views =  NSDictionaryOfVariableBindings(imageView,label); //注意这是一个宏定义方法 等效与 @{@"imageView":imageView ,@"label":label}【具有自定义别名功能】;

3.设置参数(度量)字典  动态变量绑定

 // 设置参数(度量)字典  变量数值动态化
    NSDictionary *metris = @{
                             @"imageViewTop":@((self.bounds.size.height - image.size.height*2)/2),
                             @"imageViewLeft":@((self.bounds.size.width - image.size.width*2)/2),
                             @"imageViewHeight":@(image.size.height*2),
                             @"imageViewWidth":@(image.size.width*2),
                             };

4.通过VFL语法添加约束

// 通过VFL语法添加约束组
    //placeholderView水平方向约束
    NSArray<NSLayoutConstraint *> *placeholderHConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-imageViewLeft-[imageView(imageViewHeight)]" options:0 metrics:metris views:views];
    //placeholderView和label垂直方向约束
    NSArray *placeholderVConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-imageViewTop-[imageView(imageViewWidth)]-20-[label]" options:0 metrics:metris views:views];
    //label水平方向约束
    NSArray *labelHConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[label]-0-|" options:0 metrics:metris views:views];

5.将约束添加到父视图

// 将约束添加在父视图上
    [placeholderView addConstraints:labelHConstraints];
    [placeholderView addConstraints:placeholderHConstraints];
    [placeholderView addConstraints:placeholderVConstraints];

完整代码

#import "UITableView+LF.h"

static NSInteger const placeholderViewTag = 18181818 ;

@implementation UITableView (LF)
/**
 * 添加空白占位图
 * @param image 占位图
 * @param title 图片底部标题
 * @param block 配置label 
 */
-  (void)addPlaceholderImage:(nullable UIImage *)image bottomTitle:(nullable NSString *)title block:(void (^)(UILabel *label)) block{
    UIView *placeholderView = [[UIView alloc] init];
    placeholderView.tag = placeholderViewTag;
    [self addSubview:placeholderView];

    UIImageView *imageView = [[UIImageView alloc] init];
    imageView.image = image;
    [placeholderView addSubview:imageView];
    UILabel *label = [UILabel new];
    label.text = title ;
    label.textAlignment = NSTextAlignmentCenter;
    label.numberOfLines = 1;
    label.font = [UIFont systemFontOfSize:14];
    label.textColor = [UIColor darkGrayColor];
    if (block) {
         block(label);
    }
    [placeholderView addSubview:label];
    
    //使用VFL约束
    // 取消自动转换约束
    [imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [label setTranslatesAutoresizingMaskIntoConstraints:NO];
    [placeholderView setTranslatesAutoresizingMaskIntoConstraints:NO];
    
    //  绑定视图  在 [] 中使用 这样才能找到相应的控件
    NSDictionary *views =  NSDictionaryOfVariableBindings(imageView,label); //注意这是一个宏定义方法 等效与 @{@"imageView":imageView ,@"label":label}【具有自定义别名功能】;
    
    // 设置参数(度量)字典  变量数值动态化
    NSDictionary *metris = @{
                             @"imageViewTop":@((self.bounds.size.height - image.size.height*2)/2),
                             @"imageViewLeft":@((self.bounds.size.width - image.size.width*2)/2),
                             @"imageViewHeight":@(image.size.height*2),
                             @"imageViewWidth":@(image.size.width*2),
                             };
    
    
    // 通过VFL语法添加约束组
    //placeholderView水平方向约束
    NSArray<NSLayoutConstraint *> *placeholderHConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-imageViewLeft-[imageView(imageViewHeight)]" options:0 metrics:metris views:views];
    //placeholderView和label垂直方向约束
    NSArray *placeholderVConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-imageViewTop-[imageView(imageViewWidth)]-20-[label]" options:0 metrics:metris views:views];
    //label水平方向约束
    NSArray *labelHConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[label]-0-|" options:0 metrics:metris views:views];
    
    // 将约束添加在父视图上
    [placeholderView addConstraints:labelHConstraints];
    [placeholderView addConstraints:placeholderHConstraints];
    [placeholderView addConstraints:placeholderVConstraints];
    
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[placeholderView(width)]" options:0 metrics:@{@"width":@(self.bounds.size.width)} views:NSDictionaryOfVariableBindings(placeholderView)]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[placeholderView(height)]" options:0 metrics:@{@"height":@(self.bounds.size.height)} views:NSDictionaryOfVariableBindings(placeholderView)]];
    
//    // 疑问???  为什么下面的约束会导致 placeholderView高度和宽度为0
//    
//    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[placeholderView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(placeholderView)]];
//    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[placeholderView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(placeholderView)]];
    
    
    
}

解决两个控件挤压问题

比如水平线上有两个label.文字较长,可能会导致某个label被压缩,比如这里我们需要红label不压缩。

普通解决办法:将红色label固定宽度大于等于

缺点:万一红label内容没有那么多,岂不是浪费空间了。

可以通过设置控件的优先级priority

ContentHuggingPriority:抗拉伸值,默认值251,值越大,越不容易被拉伸。

ContentCompressionResistancePriority:抗压缩值,默认呢为750,值越大,越不容易被压缩。

比如下图,我想让红色的label完整显示出来,你就可以将红label的抗压缩优先级设置高过与绿色label.

AutoLayout VFL格式约束
默认优先级显示效果

 

选中控件,选择Size Inspector,找到constains模块,修改优先级

AutoLayout VFL格式约束
设置抗压缩优先级

 

当然,也可以通过代码设置:

label.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: UILayoutConstraintAxis.horizontal) //设置水平抗压缩优先级
label.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: UILayoutConstraintAxis.horizontal) //设置水平抗拉伸优先级

修正后的效果图:

AutoLayout VFL格式约束
保持红label不被压缩

 

这个优先级和约束的优先级是同一比较水平的,如果你想让控件保持不拉伸不压缩就将它的优先级设置高于其他约束。