先看一个例子:
[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.系统兼容
-
#if TARGET_OS_IPHONE || TARGET_OS_TV
-
#import <UIKit/UIKit.h>
-
#define MAS_VIEW UIView
-
#define MAS_VIEW_CONTROLLER UIViewController
-
#define MASEdgeInsets UIEdgeInsets
-
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;
-
#elif TARGET_OS_MAC
-
#import <AppKit/AppKit.h>
-
#define MAS_VIEW NSView
-
#define MASEdgeInsets NSEdgeInsets
-
typedef NSLayoutPriority MASLayoutPriority;
-
static const MASLayoutPriority MASLayoutPriorityRequired = NSLayoutPriorityRequired;
-
static const MASLayoutPriority MASLayoutPriorityDefaultHigh = NSLayoutPriorityDefaultHigh;
-
static const MASLayoutPriority MASLayoutPriorityDragThatCanResizeWindow = NSLayoutPriorityDragThatCanResizeWindow;
-
static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 501;
-
static const MASLayoutPriority MASLayoutPriorityWindowSizeStayPut = NSLayoutPriorityWindowSizeStayPut;
-
static const MASLayoutPriority MASLayoutPriorityDragThatCannotResizeWindow = NSLayoutPriorityDragThatCannotResizeWindow;
-
static const MASLayoutPriority MASLayoutPriorityDefaultLow = NSLayoutPriorityDefaultLow;
-
static const MASLayoutPriority MASLayoutPriorityFittingSizeCompression = NSLayoutPriorityFittingSizeCompression;
-
#endif
-
复制代码
在这部分里,主要对三个方面做了兼容:
- 不同系统不同头文件的引用。
- 不同系统上不同类名的统一。
- 不同系统上不同布局优先级的统一。
2.调试约束冲突
在设置约束的时候难免会出现约束冲突的情况,比如在下面的代码中,我即设置了 greenView
的左右边距,又设置了它的宽度:
-
UIView *redView = [UIView new];
-
redView.backgroundColor = [UIColor redColor];
-
[self.view addSubview:redView];
-
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
-
make.size.mas_equalTo(CGSizeMake(200.0, 100.0));
-
make.top.equalTo(self.view).offset(200.0);
-
make.centerX.equalTo(self.view);
-
}];
-
UIView *greenView = [UIView new];
-
greenView.backgroundColor = [UIColor greenColor];
-
[self.view addSubview:greenView];
-
[greenView mas_makeConstraints:^(MASConstraintMaker *make) {
-
make.height.mas_equalTo(200.0);
-
make.top.equalTo(redView.mas_bottom).offset(30.0);
-
make.left.equalTo(self.view).offset(15.0);
-
make.right.equalTo(self.view).offset(-15.0);
-
make.width.mas_equalTo(750.0);
-
}];
-
复制代码
这时,如果运行的话,控制台就会打印一堆约束冲突的信息:
-
(
-
<MASLayoutConstraint:0x6000000b8ea0 UIView:0x7fb48ed33dc0.left == UIView:0x7fb48ec03fd0.left + 15>,
-
<MASLayoutConstraint:0x6000000b8f60 UIView:0x7fb48ed33dc0.right == UIView:0x7fb48ec03fd0.right - 15>,
-
<MASLayoutConstraint:0x6000000b9080 UIView:0x7fb48ed33dc0.width == 750>,
-
<NSLayoutConstraint:0x6040002819f0 UIView:0x7fb48ec03fd0.width == 375>
-
)
-
复制代码
虽然,信息中很明确的告诉你那些约束冲突了,但是 UIView:0x7fb48ed33dc0
是哪个视图,UIView:0x7fb48ec03fd0
又是哪个视图?一脸懵逼是不是?约束少的时候还能好找一些,一旦视图复杂起来,就很难查找了。
这个时候,Masonry
提供的这个宏就派上用场了:
-
#define MASAttachKeys(...) \
-
{ \
-
NSDictionary *keyPairs = NSDictionaryOfVariableBindings(__VA_ARGS__); \
-
for (id key in keyPairs.allKeys) { \
-
id obj = keyPairs[key]; \
-
NSAssert([obj respondsToSelector:@selector(setMas_key:)], \
-
@"Cannot attach mas_key to %@", obj); \
-
[obj setMas_key:key]; \
-
} \
-
}
-
复制代码
在使用时,你只需要添加上这么一句代码:
-
MASAttachKeys(redView, greenView, self.view);
-
复制代码
再次运行,你就会发现控制带打印的内容变化了:
-
(
-
<MASLayoutConstraint:0x6000000b0380 UIView:greenView.left == UIView:self.view.left + 15>,
-
<MASLayoutConstraint:0x6000000b0440 UIView:greenView.right == UIView:self.view.right - 15>,
-
<MASLayoutConstraint:0x6000000b0560 UIView:greenView.width == 750>,
-
<NSLayoutConstraint:0x60000028b1d0 UIView:self.view.width == 375>
-
)
-
复制代码
原来的 UIView:0x7fb48ed33dc0
变成了 greenView
, 而 UIView:0x7fb48ec03fd0
变成了 self.view
,这样是不是就一目了然啊,再进行调试就方便了很多。
其实这个宏只做了两件事:一件是对系统提供的宏 NSDictionaryOfVariableBindings(...)
做了封装;另一件是利用 Masonry
提供的分类 View+MASAdditions
将用户设置的 key
(例如:redView
) 保存。
3.对象的哈希值
-
#define MAS_NSUINT_BIT (CHAR_BIT * sizeof(NSUInteger))
-
#define MAS_NSUINTROTATE(val, howmuch) ((((NSUInteger)val) << howmuch) | (((NSUInteger)val) >> (MAS_NSUINT_BIT - howmuch)))
-
复制代码
一眼看过去,全是不认的宏和函数,但是我们一点点的看:
- CHAR_BIT:这个宏代表一个
char
对象所占的位数,因为一个char
就是一个字节(byte),可也说它代表了一个字节所占的位数,一般是8位。 - sizeof():
sizeof
是C/C++
中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。
到这,我们第一个宏就能看懂了:它计算的是当前设备上 NSUInteger
类型在内存中占用的位数。
看懂了第一个宏,第二个宏也就看懂了:将 val
按照 howmuch
值进行左右反转。例如:val
如果是 0110 0010
,howmuch
为 4
。计算就是 0010 0000 | 0000 0110
,结果为 0010 0110
。
这两个宏的作用是:在自定义类 MASViewAttribute
中生成 hash
值。
4.数据装箱
当我们在使用 Masonry
愉快的布局时,经常会用到以下写法:
-
make.height.mas_equalTo(200.0);
-
复制代码
-
make.size.mas_equalTo(CGSizeMake(160.0, 90.0));
-
复制代码
-
make.edges.mas_equalTo(UIEdgeInsetsMake(0, 10, 20, 30));
-
复制代码
我们发现 mas_equalTo()
这个宏对参数并不挑剔,几乎是传啥数据类型都可以,这就要归功于下面我们将要看的内容了。
-
#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))
-
复制代码
首先从宏开始看起:MASBoxValue(value)
只需要传递一个参数,在宏内,获取了传入的 value
的 OC 内部类型字符串,然后和 value
一起传给了 _MASBoxValue
函数。
-
static inline id _MASBoxValue(const char *type, ...) {
-
va_list v;
-
va_start(v, type);
-
id obj = nil;
-
if (strcmp(type, @encode(id)) == 0) {
-
id actual = va_arg(v, id);
-
obj = actual;
-
} else if (strcmp(type, @encode(CGPoint)) == 0) {
-
CGPoint actual = (CGPoint)va_arg(v, CGPoint);
-
obj = [NSValue value:&actual withObjCType:type];
-
} else if (strcmp(type, @encode(CGSize)) == 0) {
-
CGSize actual = (CGSize)va_arg(v, CGSize);
-
obj = [NSValue value:&actual withObjCType:type];
-
} else if (strcmp(type, @encode(MASEdgeInsets)) == 0) {
-
MASEdgeInsets actual = (MASEdgeInsets)va_arg(v, MASEdgeInsets);
-
obj = [NSValue value:&actual withObjCType:type];
-
} else if (strcmp(type, @encode(double)) == 0) {
-
double actual = (double)va_arg(v, double);
-
obj = [NSNumber numberWithDouble:actual];
-
} else if (strcmp(type, @encode(float)) == 0) {
-
float actual = (float)va_arg(v, double);
-
obj = [NSNumber numberWithFloat:actual];
-
} else if (strcmp(type, @encode(int)) == 0) {
-
int actual = (int)va_arg(v, int);
-
obj = [NSNumber numberWithInt:actual];
-
} else if (strcmp(type, @encode(long)) == 0) {
-
long actual = (long)va_arg(v, long);
-
obj = [NSNumber numberWithLong:actual];
-
} else if (strcmp(type, @encode(long long)) == 0) {
-
long long actual = (long long)va_arg(v, long long);
-
obj = [NSNumber numberWithLongLong:actual];
-
} else if (strcmp(type, @encode(short)) == 0) {
-
short actual = (short)va_arg(v, int);
-
obj = [NSNumber numberWithShort:actual];
-
} else if (strcmp(type, @encode(char)) == 0) {
-
char actual = (char)va_arg(v, int);
-
obj = [NSNumber numberWithChar:actual];
-
} else if (strcmp(type, @encode(bool)) == 0) {
-
bool actual = (bool)va_arg(v, int);
-
obj = [NSNumber numberWithBool:actual];
-
} else if (strcmp(type, @encode(unsigned char)) == 0) {
-
unsigned char actual = (unsigned char)va_arg(v, unsigned int);
-
obj = [NSNumber numberWithUnsignedChar:actual];
-
} else if (strcmp(type, @encode(unsigned int)) == 0) {
-
unsigned int actual = (unsigned int)va_arg(v, unsigned int);
-
obj = [NSNumber numberWithUnsignedInt:actual];
-
} else if (strcmp(type, @encode(unsigned long)) == 0) {
-
unsigned long actual = (unsigned long)va_arg(v, unsigned long);
-
obj = [NSNumber numberWithUnsignedLong:actual];
-
} else if (strcmp(type, @encode(unsigned long long)) == 0) {
-
unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long);
-
obj = [NSNumber numberWithUnsignedLongLong:actual];
-
} else if (strcmp(type, @encode(unsigned short)) == 0) {
-
unsigned short actual = (unsigned short)va_arg(v, unsigned int);
-
obj = [NSNumber numberWithUnsignedShort:actual];
-
}
-
va_end(v);
-
return obj;
-
}
-
复制代码
这个函数的实现逻辑很简单:就是根据传入参数类型的不同,装箱成 NSValue
或 NSNumber
类型的对象。
其中有一点需要注意的是:可变参数的实现。
我们平常在写代码的过程中,经常会接触到可变参数,最常用的莫过于 NSLog()
,在使用时,我们可以传递很多参数。比方说下面的代码中,我们就传递了 4 个参数:
-
NSLog(@"%@%@%@", @"1", @"2", @"3");
-
复制代码
那我们想要自己实现可变参数的方法应该怎么做?这就要用到 C 语言提供的 API 了:va_list
、va_start
、va_arg
、va_end
。
我们来写两个方法玩耍一下:
- 先写一个能打印所有传入参数的方法:
-
- (void)print:(NSString *)string, ... {
-
if (!string) {
-
return;
-
}
-
NSLog(@"%@", string);
-
va_list args;
-
va_start(args, string);
-
NSString *printString;
-
while ((printString = va_arg(args, NSString *))) {
-
NSLog(@"%@", printString);
-
}
-
va_end(args);
-
}
-
复制代码
使用方法:
-
[self print:@"1", @"2", @"3", @"4", @"5", @"6", @"7", nil];
-
复制代码
打印结果:
-
2018-08-07 17:47:26.312814+0800 WTMasonryDemo[11100:994417] 1
-
2018-08-07 17:47:26.312972+0800 WTMasonryDemo[11100:994417] 2
-
2018-08-07 17:47:26.313080+0800 WTMasonryDemo[11100:994417] 3
-
2018-08-07 17:47:26.313170+0800 WTMasonryDemo[11100:994417] 4
-
2018-08-07 17:47:26.313255+0800 WTMasonryDemo[11100:994417] 5
-
2018-08-07 17:47:26.313354+0800 WTMasonryDemo[11100:994417] 6
-
2018-08-07 17:47:26.313460+0800 WTMasonryDemo[11100:994417] 7
-
复制代码
- 再写一个类似于
NSLog()
函数的方法:
-
- (void)log:(NSString *)string, ... {
-
if (!string) {
-
return;
-
}
-
va_list args;
-
va_start(args, string);
-
NSString *logString = [[NSString alloc] initWithFormat:string arguments:args];
-
NSLog(@"%@", logString);
-
va_end(args);
-
}
-
复制代码
使用方法:
-
[self log:@"123%@%@%@", @"4", @"5", @"6"];
-
复制代码
打印结果:
-
2018-08-07 17:50:23.797442+0800 WTMasonryDemo[11139:999893] 123456
-
复制代码
5.MASLayoutConstraint
这个类是 NSLayoutConstraint 的子类,它的作用是当我们使用上面提到的宏 MASAttachKeys
调试约束冲突时,通过在控制台打印出的内容,更容易区分那些约束是通过 Masonry 设置的。
该类只添加了一个属性:
-
@property (nonatomic, strong) id mas_key;
-
复制代码
这个属性是由于保存标识该约束对象的 key。
https://blog.csdn.net/weixin_34221073/article/details/88120938