masonry编写约束,调试约束冲突

本文深入解析Masonry布局库,涵盖优先级调整、系统兼容、冲突调试技巧及数据装箱方法,助您掌握高效布局实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看一个例子:

    [self.valueLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
        UIView *superView = self.contentView;
        make.left.equalTo(self.titleLabel.mas_right).offset(24.0f);
        make.top.equalTo(self.titleLabel.mas_top);
        make.bottom.equalTo(self.titleLabel.mas_bottom);
        make.right.equalTo(superView.mas_right).offset(-padding);
    }];

在这个例子中,每个约束的priority的默认值是1000

然后,再看一个例子:

    [self.valueLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
        UIView *superView = self.contentView;
        make.left.equalTo(self.titleLabel.mas_right).offset(24.0f);
        make.top.equalTo(self.titleLabel.mas_top);
        make.bottom.equalTo(self.titleLabel.mas_bottom);
        make.right.equalTo(superView.mas_right).offset(-padding).priorityHigh();
    }];

在这个例子中,右边距使用了priorityHigh(),看定义,hight的值是750

    typedef UILayoutPriority MASLayoutPriority;
    static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired;
    static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh;
    static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500;
    static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow;
    static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel;

 

static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750; // This is the priority level with which a button resists compressing its content.

于是乎,写约束的时候,要注意下,不要以为priorityHight() 就是最高级别了,其实最高级别是 required(1000)。
优先级在做一些微妙的调整时,可以起到四两拨千斤的效果;



作者:zhangyin
链接:https://www.jianshu.com/p/05c1ca37b5a5
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

文章阅读的 Masonry 的版本为1.1.0。

这个类做了一些系统上的兼容,以及提供了一些公共方法。

1.系统兼容

 
  1. #if TARGET_OS_IPHONE || TARGET_OS_TV

  2.  
  3. #import <UIKit/UIKit.h>

  4. #define MAS_VIEW UIView

  5. #define MAS_VIEW_CONTROLLER UIViewController

  6. #define MASEdgeInsets UIEdgeInsets

  7.  
  8. typedef UILayoutPriority MASLayoutPriority;

  9. static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired;

  10. static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh;

  11. static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500;

  12. static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow;

  13. static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel;

  14.  
  15. #elif TARGET_OS_MAC

  16.  
  17. #import <AppKit/AppKit.h>

  18. #define MAS_VIEW NSView

  19. #define MASEdgeInsets NSEdgeInsets

  20.  
  21. typedef NSLayoutPriority MASLayoutPriority;

  22. static const MASLayoutPriority MASLayoutPriorityRequired = NSLayoutPriorityRequired;

  23. static const MASLayoutPriority MASLayoutPriorityDefaultHigh = NSLayoutPriorityDefaultHigh;

  24. static const MASLayoutPriority MASLayoutPriorityDragThatCanResizeWindow = NSLayoutPriorityDragThatCanResizeWindow;

  25. static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 501;

  26. static const MASLayoutPriority MASLayoutPriorityWindowSizeStayPut = NSLayoutPriorityWindowSizeStayPut;

  27. static const MASLayoutPriority MASLayoutPriorityDragThatCannotResizeWindow = NSLayoutPriorityDragThatCannotResizeWindow;

  28. static const MASLayoutPriority MASLayoutPriorityDefaultLow = NSLayoutPriorityDefaultLow;

  29. static const MASLayoutPriority MASLayoutPriorityFittingSizeCompression = NSLayoutPriorityFittingSizeCompression;

  30.  
  31. #endif

  32. 复制代码

在这部分里,主要对三个方面做了兼容:

  • 不同系统不同头文件的引用。
  • 不同系统上不同类名的统一。
  • 不同系统上不同布局优先级的统一。

2.调试约束冲突

在设置约束的时候难免会出现约束冲突的情况,比如在下面的代码中,我即设置了 greenView 的左右边距,又设置了它的宽度:

 
  1. UIView *redView = [UIView new];

  2. redView.backgroundColor = [UIColor redColor];

  3. [self.view addSubview:redView];

  4.  
  5. [redView mas_makeConstraints:^(MASConstraintMaker *make) {

  6. make.size.mas_equalTo(CGSizeMake(200.0, 100.0));

  7. make.top.equalTo(self.view).offset(200.0);

  8. make.centerX.equalTo(self.view);

  9. }];

  10.  
  11.  
  12. UIView *greenView = [UIView new];

  13. greenView.backgroundColor = [UIColor greenColor];

  14. [self.view addSubview:greenView];

  15.  
  16. [greenView mas_makeConstraints:^(MASConstraintMaker *make) {

  17. make.height.mas_equalTo(200.0);

  18. make.top.equalTo(redView.mas_bottom).offset(30.0);

  19. make.left.equalTo(self.view).offset(15.0);

  20. make.right.equalTo(self.view).offset(-15.0);

  21. make.width.mas_equalTo(750.0);

  22. }];

  23. 复制代码

这时,如果运行的话,控制台就会打印一堆约束冲突的信息:

 
  1. (

  2. <MASLayoutConstraint:0x6000000b8ea0 UIView:0x7fb48ed33dc0.left == UIView:0x7fb48ec03fd0.left + 15>,

  3. <MASLayoutConstraint:0x6000000b8f60 UIView:0x7fb48ed33dc0.right == UIView:0x7fb48ec03fd0.right - 15>,

  4. <MASLayoutConstraint:0x6000000b9080 UIView:0x7fb48ed33dc0.width == 750>,

  5. <NSLayoutConstraint:0x6040002819f0 UIView:0x7fb48ec03fd0.width == 375>

  6. )

  7. 复制代码

虽然,信息中很明确的告诉你那些约束冲突了,但是 UIView:0x7fb48ed33dc0 是哪个视图,UIView:0x7fb48ec03fd0 又是哪个视图?一脸懵逼是不是?约束少的时候还能好找一些,一旦视图复杂起来,就很难查找了。

这个时候,Masonry 提供的这个宏就派上用场了:

 
  1. #define MASAttachKeys(...) \

  2. { \

  3. NSDictionary *keyPairs = NSDictionaryOfVariableBindings(__VA_ARGS__); \

  4. for (id key in keyPairs.allKeys) { \

  5. id obj = keyPairs[key]; \

  6. NSAssert([obj respondsToSelector:@selector(setMas_key:)], \

  7. @"Cannot attach mas_key to %@", obj); \

  8. [obj setMas_key:key]; \

  9. } \

  10. }

  11. 复制代码

在使用时,你只需要添加上这么一句代码:

 
  1. MASAttachKeys(redView, greenView, self.view);

  2. 复制代码

再次运行,你就会发现控制带打印的内容变化了:

 
  1. (

  2. <MASLayoutConstraint:0x6000000b0380 UIView:greenView.left == UIView:self.view.left + 15>,

  3. <MASLayoutConstraint:0x6000000b0440 UIView:greenView.right == UIView:self.view.right - 15>,

  4. <MASLayoutConstraint:0x6000000b0560 UIView:greenView.width == 750>,

  5. <NSLayoutConstraint:0x60000028b1d0 UIView:self.view.width == 375>

  6. )

  7. 复制代码

原来的 UIView:0x7fb48ed33dc0 变成了 greenView, 而 UIView:0x7fb48ec03fd0 变成了 self.view,这样是不是就一目了然啊,再进行调试就方便了很多。

其实这个宏只做了两件事:一件是对系统提供的宏 NSDictionaryOfVariableBindings(...) 做了封装;另一件是利用 Masonry 提供的分类 View+MASAdditions 将用户设置的 key (例如:redView) 保存。

3.对象的哈希值

 
  1. #define MAS_NSUINT_BIT (CHAR_BIT * sizeof(NSUInteger))

  2. #define MAS_NSUINTROTATE(val, howmuch) ((((NSUInteger)val) << howmuch) | (((NSUInteger)val) >> (MAS_NSUINT_BIT - howmuch)))

  3. 复制代码

一眼看过去,全是不认的宏和函数,但是我们一点点的看:

  • CHAR_BIT:这个宏代表一个 char 对象所占的位数,因为一个 char 就是一个字节(byte),可也说它代表了一个字节所占的位数,一般是8位。
  • sizeof():sizeof 是 C/C++ 中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。

到这,我们第一个宏就能看懂了:它计算的是当前设备上 NSUInteger 类型在内存中占用的位数。

看懂了第一个宏,第二个宏也就看懂了:将 val 按照 howmuch 值进行左右反转。例如:val 如果是 0110 0010howmuch 为 4。计算就是 0010 0000 | 0000 0110,结果为 0010 0110

这两个宏的作用是:在自定义类 MASViewAttribute 中生成 hash 值。

4.数据装箱

当我们在使用 Masonry 愉快的布局时,经常会用到以下写法:

 
  1. make.height.mas_equalTo(200.0);

  2. 复制代码

 
  1. make.size.mas_equalTo(CGSizeMake(160.0, 90.0));

  2. 复制代码

 
  1. make.edges.mas_equalTo(UIEdgeInsetsMake(0, 10, 20, 30));

  2. 复制代码

我们发现 mas_equalTo() 这个宏对参数并不挑剔,几乎是传啥数据类型都可以,这就要归功于下面我们将要看的内容了。


 
  1. #define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))

  2. 复制代码

首先从宏开始看起:MASBoxValue(value) 只需要传递一个参数,在宏内,获取了传入的 value 的 OC 内部类型字符串,然后和 value 一起传给了 _MASBoxValue 函数。


 
  1. static inline id _MASBoxValue(const char *type, ...) {

  2. va_list v;

  3. va_start(v, type);

  4. id obj = nil;

  5. if (strcmp(type, @encode(id)) == 0) {

  6. id actual = va_arg(v, id);

  7. obj = actual;

  8. } else if (strcmp(type, @encode(CGPoint)) == 0) {

  9. CGPoint actual = (CGPoint)va_arg(v, CGPoint);

  10. obj = [NSValue value:&actual withObjCType:type];

  11. } else if (strcmp(type, @encode(CGSize)) == 0) {

  12. CGSize actual = (CGSize)va_arg(v, CGSize);

  13. obj = [NSValue value:&actual withObjCType:type];

  14. } else if (strcmp(type, @encode(MASEdgeInsets)) == 0) {

  15. MASEdgeInsets actual = (MASEdgeInsets)va_arg(v, MASEdgeInsets);

  16. obj = [NSValue value:&actual withObjCType:type];

  17. } else if (strcmp(type, @encode(double)) == 0) {

  18. double actual = (double)va_arg(v, double);

  19. obj = [NSNumber numberWithDouble:actual];

  20. } else if (strcmp(type, @encode(float)) == 0) {

  21. float actual = (float)va_arg(v, double);

  22. obj = [NSNumber numberWithFloat:actual];

  23. } else if (strcmp(type, @encode(int)) == 0) {

  24. int actual = (int)va_arg(v, int);

  25. obj = [NSNumber numberWithInt:actual];

  26. } else if (strcmp(type, @encode(long)) == 0) {

  27. long actual = (long)va_arg(v, long);

  28. obj = [NSNumber numberWithLong:actual];

  29. } else if (strcmp(type, @encode(long long)) == 0) {

  30. long long actual = (long long)va_arg(v, long long);

  31. obj = [NSNumber numberWithLongLong:actual];

  32. } else if (strcmp(type, @encode(short)) == 0) {

  33. short actual = (short)va_arg(v, int);

  34. obj = [NSNumber numberWithShort:actual];

  35. } else if (strcmp(type, @encode(char)) == 0) {

  36. char actual = (char)va_arg(v, int);

  37. obj = [NSNumber numberWithChar:actual];

  38. } else if (strcmp(type, @encode(bool)) == 0) {

  39. bool actual = (bool)va_arg(v, int);

  40. obj = [NSNumber numberWithBool:actual];

  41. } else if (strcmp(type, @encode(unsigned char)) == 0) {

  42. unsigned char actual = (unsigned char)va_arg(v, unsigned int);

  43. obj = [NSNumber numberWithUnsignedChar:actual];

  44. } else if (strcmp(type, @encode(unsigned int)) == 0) {

  45. unsigned int actual = (unsigned int)va_arg(v, unsigned int);

  46. obj = [NSNumber numberWithUnsignedInt:actual];

  47. } else if (strcmp(type, @encode(unsigned long)) == 0) {

  48. unsigned long actual = (unsigned long)va_arg(v, unsigned long);

  49. obj = [NSNumber numberWithUnsignedLong:actual];

  50. } else if (strcmp(type, @encode(unsigned long long)) == 0) {

  51. unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long);

  52. obj = [NSNumber numberWithUnsignedLongLong:actual];

  53. } else if (strcmp(type, @encode(unsigned short)) == 0) {

  54. unsigned short actual = (unsigned short)va_arg(v, unsigned int);

  55. obj = [NSNumber numberWithUnsignedShort:actual];

  56. }

  57. va_end(v);

  58. return obj;

  59. }

  60. 复制代码

这个函数的实现逻辑很简单:就是根据传入参数类型的不同,装箱成 NSValue 或 NSNumber 类型的对象。

其中有一点需要注意的是:可变参数的实现。

我们平常在写代码的过程中,经常会接触到可变参数,最常用的莫过于 NSLog(),在使用时,我们可以传递很多参数。比方说下面的代码中,我们就传递了 4 个参数:

 
  1. NSLog(@"%@%@%@", @"1", @"2", @"3");

  2. 复制代码

那我们想要自己实现可变参数的方法应该怎么做?这就要用到 C 语言提供的 API 了:va_listva_startva_argva_end

我们来写两个方法玩耍一下:

  1. 先写一个能打印所有传入参数的方法:
 
  1. - (void)print:(NSString *)string, ... {

  2.  
  3. if (!string) {

  4. return;

  5. }

  6.  
  7. NSLog(@"%@", string);

  8.  
  9. va_list args;

  10. va_start(args, string);

  11.  
  12. NSString *printString;

  13.  
  14. while ((printString = va_arg(args, NSString *))) {

  15. NSLog(@"%@", printString);

  16. }

  17.  
  18. va_end(args);

  19. }

  20. 复制代码

使用方法:

 
  1. [self print:@"1", @"2", @"3", @"4", @"5", @"6", @"7", nil];

  2. 复制代码

打印结果:

 
  1. 2018-08-07 17:47:26.312814+0800 WTMasonryDemo[11100:994417] 1

  2. 2018-08-07 17:47:26.312972+0800 WTMasonryDemo[11100:994417] 2

  3. 2018-08-07 17:47:26.313080+0800 WTMasonryDemo[11100:994417] 3

  4. 2018-08-07 17:47:26.313170+0800 WTMasonryDemo[11100:994417] 4

  5. 2018-08-07 17:47:26.313255+0800 WTMasonryDemo[11100:994417] 5

  6. 2018-08-07 17:47:26.313354+0800 WTMasonryDemo[11100:994417] 6

  7. 2018-08-07 17:47:26.313460+0800 WTMasonryDemo[11100:994417] 7

  8. 复制代码

  1. 再写一个类似于 NSLog() 函数的方法:
 
  1. - (void)log:(NSString *)string, ... {

  2.  
  3. if (!string) {

  4. return;

  5. }

  6.  
  7. va_list args;

  8. va_start(args, string);

  9.  
  10. NSString *logString = [[NSString alloc] initWithFormat:string arguments:args];

  11. NSLog(@"%@", logString);

  12.  
  13. va_end(args);

  14. }

  15. 复制代码

使用方法:

 
  1. [self log:@"123%@%@%@", @"4", @"5", @"6"];

  2. 复制代码

打印结果:

 
  1. 2018-08-07 17:50:23.797442+0800 WTMasonryDemo[11139:999893] 123456

  2. 复制代码

5.MASLayoutConstraint

这个类是 NSLayoutConstraint 的子类,它的作用是当我们使用上面提到的宏 MASAttachKeys 调试约束冲突时,通过在控制台打印出的内容,更容易区分那些约束是通过 Masonry 设置的。

该类只添加了一个属性:

 
  1. @property (nonatomic, strong) id mas_key;

  2. 复制代码

这个属性是由于保存标识该约束对象的 key。

https://blog.csdn.net/weixin_34221073/article/details/88120938

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值