AutoLayout之关于苹果原生约束的探索

来源:互联网 发布:网络捕鱼平台出租代理 编辑:程序博客网 时间:2024/06/04 19:03

AutoLayout(自动布局),在我们的项目中,我更喜欢把它称为约束。iOS实现约束有几种方式:原生约束api、VFL、IB、第三方约束工具(Masonry、UIView+AutoLayout),这里花一点篇幅来讲述苹果原生的约束。

github:https://github.com/yangqingren/LBAutoLayout

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

大概的意思就是view1的约束等于(等于or不超过or不低于)view2的约束的n倍+常量。

- (UILabel *)lbLabel {    if (!_lbLabel) {        _lbLabel = [[UILabel alloc] init];        _lbLabel.text = @"我是用来测试约束的Label";        _lbLabel.backgroundColor = [UIColor yellowColor];        _lbLabel.translatesAutoresizingMaskIntoConstraints = NO;    }    return _lbLabel;}- (void)viewDidLoad {    [super viewDidLoad];    self.view.translatesAutoresizingMaskIntoConstraints = NO;    [self.view addSubview:self.lbLabel];    // 设置label的top等于self.view的top,倍数为1,offset为偏移80    NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.lbLabel                              relatedBy:NSLayoutRelationEqual                                 toItem:self.view                              attribute:NSLayoutAttributeTop                             multiplier:1                               constant:80];    // 设置label的left等于self.view的left,倍数为1,offset为偏移80    NSLayoutConstraint *leftConstraint =     [NSLayoutConstraint constraintWithItem:self.lbLabel                                 attribute:NSLayoutAttributeLeft                                 relatedBy:NSLayoutRelationEqual                                    toItem:self.view                                 attribute:NSLayoutAttributeLeft                                multiplier:1                                  constant:80];    [self.view addConstraints:@[topConstraint, leftConstraint]];    // Do any additional setup after loading the view, typically from a nib.}

可以看到,只要设置label的top约束和left约束,即可完成布局,也就是说,label(button)约束布局时自带宽高,宽高由其text填充,关于这个,我后面再讲。

由于label的这种特性,我们可以使用简单的约束就可以完成以下的这几种布局:

   // shadowLabel的right等于lbLabel的left,达到宽度自适应    NSLayoutConstraint *shadowConstraint =     [NSLayoutConstraint constraintWithItem:self.shadowLabel                                 attribute:NSLayoutAttributeLeft                                  relatedBy:NSLayoutRelationEqual                                     toItem:self.lbLabel                                  attribute:NSLayoutAttributeRight                                 multiplier:1                                   constant:0];

或者

   // shadowLabel的top等于lbLabel的bottom,达到高度自适应    NSLayoutConstraint *shadowConstraint =     [NSLayoutConstraint constraintWithItem:self.shadowLabel                                  attribute:NSLayoutAttributeTop                                  relatedBy:NSLayoutRelationEqual                                     toItem:self.lbLabel                                  attribute:NSLayoutAttributeBottom                                 multiplier:1                                   constant:0];

当然也可以设置固定长度

    NSLayoutConstraint *widthConstraint =     [NSLayoutConstraint constraintWithItem:self.lbLabel                                  attribute:NSLayoutAttributeWidth                                  relatedBy:NSLayoutRelationEqual                                     toItem:nil                                  attribute:NSLayoutAttributeNotAnAttribute                                 multiplier:1                                   constant:150];

补充:关于AutoLayout与Frame的思考
由于给view设置约束的时候,并没有马上生成相应的frame,这会使得如果对使用约束布局的view获取相应的bounds等操作的时候,获取到的只有CGSizeZero,这里提供一些思路与方案:

// 1.使用layoutIfNeeded,让刚刚设置约束的view立即去布局,之后就有对应的size了[view layoutIfNeeded];
// 2.在layoutSubviews里进行获取- (void)layoutSubviews {    [super layoutSubviews];    NSLog(@"self.bounds=%@",self.bounds);}// 但是layoutSubviews会多次调用,这里需要十分注意

上面说到label自带宽高,这里拓展讲解一下

UIView都有一个属性,叫做intrinsicContentSize

这里写图片描述

当label(button) setText的时候,系统根据字体字体大小与长度设置一个满足label的size,此时会触发一个叫intrinsicContentSize的方法,用这个方法,可以对一些不定字符长度的控件进行设置,如以下这个按钮

- (UIButton *)button {    if (!_button) {        _button = [UIButton buttonWithType:UIButtonTypeCustom];        [_button setTitle:@"Log in" forState:UIControlStateNormal];        _button.layer.cornerRadius = 4;        _button.backgroundColor = [UIColor grayColor];    }    return _button;}- (void)viewDidLoad {    [super viewDidLoad];    [self.view addSubview:self.button];    [self.button mas_makeConstraints:^(MASConstraintMaker *make) {        make.center.mas_equalTo(self.view);    }];    // Do any additional setup after loading the view, typically from a nib.}

你会看到它的size刚好紧贴着text,效果很差,我们可以通过重写intrinsicContentSize方法,满足布局要求

#import "LBSizeButton.h"@interface LBSizeButton ()@end@implementation LBSizeButton-(CGSize)intrinsicContentSize {    CGSize size = [super intrinsicContentSize];    return CGSizeMake(size.width + 10 * 2, size.height + 2 * 2);}@end

这里写图片描述

这里写图片描述

github:https://github.com/yangqingren/LBAutoLayout

下次有空写一些关于用Masonry进行布局探索的实例

end

原创粉丝点击