- NSLayoutConstraint
- VFL
- Masonry
在開發(fā)中我們經(jīng)常會將XIB囤耳、StoryBoard配合AutoLayout 來完成界面的適配,但是很多時候如果我們需要封裝一個庫卻不想在庫中使用XIB或者StoryBoard,此時我們就需要通過純代碼的方式來對屏幕進行適配了,這也是今天我想講的主要內(nèi)容,即 如何用純代碼的方式來進行屏幕適配
NSLayoutConstraint
我們先說一下使用 NSLayoutConstraint
的具體步驟:
- 利用
NSLayoutConstraint
類創(chuàng)建具體的約束對象,注意一個NSLayoutConstraint
實例代表一條約束寓娩。 - 添加約束對象到相應的 view 上,蘋果為我們提供了兩個方法:
- (void)addConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
- (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints NS_AVAILABLE_IOS(6_0);
下面我們來看一個具體的例子:
用NSLayoutConstraint
實現(xiàn)一個寬度高度為100,在父控件中垂直水平居中的效果
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
//要先禁止 autoresizing 功能
redView.translatesAutoresizingMaskIntoConstraints = NO;
/**
*參數(shù)一:要約束的控件
*參數(shù)二:約束的類型(做怎樣的約束)
*參數(shù)三:與參照控件之間的關系
*參數(shù)四:參照的控件
*參數(shù)五:約束的類型(做怎樣的約束)
*參數(shù)六:乘數(shù)
*參數(shù)七:常量
*/
// 添加寬度約束:父控件的一半
NSLayoutConstraint *consW = [NSLayoutConstraint constraintWithItem:redView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0.0
];
// 添加高度約束:父控件的一半
NSLayoutConstraint *consH = [NSLayoutConstraint constraintWithItem:redView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0.0
];
// 水平居中
NSLayoutConstraint *consX = [NSLayoutConstraint constraintWithItem:redView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0
];
// 垂直居中
NSLayoutConstraint *consY = [NSLayoutConstraint constraintWithItem:redView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0
];
[self.view addConstraints:@[consW,consH,consX,consY]];
效果圖如下:
注意事項
- 要先禁止 autoresizing 功能
redView.translatesAutoresizingMaskIntoConstraints = NO;
- 添加約束之前辫诅,一定要保證相關控件都已經(jīng)添加到對應的父控件上了
- 因為iOS中原點在左上角所以使用
offset
時注意right和bottom用負數(shù)
添加約束的規(guī)則
在創(chuàng)建約束之后催式,需要將其添加到作用的view上,在添加時要注意目標view需要遵循以下規(guī)則:
-
對于兩個同層級view之間的約束關系临庇,添加到它們的父view上image
-
對于兩個不同層級view之間的約束關系,添加到他們最近的共同父view上image
-
對于有層次關系的兩個view之間的約束關系麸塞,添加到層次較高的父view上 image
VFL(Visual Format Language)
通過上面寬高為100秃臣,在父控件中垂直水平居中的簡單效果可以看到復雜程度,蘋果公司為了簡化這種操作,設計了一種 可視化格式語言
即:VFL
VFL
的思想其實很簡單,它將約束分成了兩塊即水平方向(H:)和垂直方向(V:)
其實蘋果官方文檔對 這一塊 描述的特別詳細,這里我就直接貼圖了:
我們來看看官方為我們提供的API
/**
* 參數(shù)一:format VFL格式構成的字符串,用以表達想要添加的約束
* 參數(shù)二:options 對齊方式哪工,是一個枚舉值
* 參數(shù)三:metrics 一般傳入以間距為KEY的字典奥此,如: @{ @"margin":@20},KEY要與format參數(shù)里所填寫的“margin”相同
* 參數(shù)三:views 傳入約束中提到的View雁比,也是要傳入字典稚虎,但是KEY一定要和format參數(shù)里所填寫的View名字相同
*
* @return 返回約束的數(shù)組
*/
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)options
metrics:(NSDictionary *)metrics
views:(NSDictionary *)views;
注意:
因為參數(shù)三和參數(shù)四需要傳入字典,字典的KEY要和Format中的名字相同,如果我們自己去寫 第一比較麻煩,第二不小心還容易寫錯,所以蘋果為我們提供了一個強大的宏
: NSDictionaryOfVariableBindings
/* This macro is a helper for making view dictionaries
for +constraintsWithVisualFormat:options:metrics:views:
NSDictionaryOfVariableBindings(v1, v2, v3) is equivalent to
[NSDictionary dictionaryWithObjectsAndKeys:v1, @"v1", v2, @"v2", v3, @"v3", nil];
*/
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
為了更清晰的展示VFL的用法 下面我用一個例子來展示一下:
??例子:用VFL實現(xiàn)下面的效果:View寬高相等,邊距20
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
// 不要將AutoresizingMask轉為Autolayout的約束
blueView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:blueView];
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
// 不要將AutoresizingMask轉為Autolayout的約束
redView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:redView];
// 間距
NSNumber *margin = @20;
// 添加水平方向的約束
NSString *hFormat = @"H:|-margin-[blueView]-margin-[redView(==blueView)]-margin-|";
//把要添加約束的View轉成字典
NSDictionary *views = NSDictionaryOfVariableBindings(blueView, redView);
NSDictionary *mertrics = NSDictionaryOfVariableBindings(margin);
//options: 這里設置底部和頂部對齊
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:hFormat
options:NSLayoutFormatAlignAllTop
| NSLayoutFormatAlignAllBottom
metrics:mertrics
views:views];
[self.view addConstraints:constraints];
// 添加豎直方向的間距
NSNumber *height = @40;
NSString *vFormat = @"V:[blueView(height)]-margin-|";
NSDictionary *mertrics2 = NSDictionaryOfVariableBindings(margin, height);
NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:vFormat
options:kNilOptions
metrics:mertrics2
views:views];
[self.view addConstraints:constraints2];
VFL也是有缺陷的,這話是蘋果自己說的 有圖有真相
所以 這也就引出了下面我想講的東西
Masonry
Masonry
其實關于Masonry
的用法 其github上的主頁已經(jīng)描述的非常清楚直白了,這里我們直接用Masonry
來實現(xiàn)最開始的小例子
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.equalTo(self.view).multipliedBy(0.5);
make.center.equalTo(self.view);//make centerX and centerY = self.view
}];
是不是Soeasy.....代碼量簡單了很多?
比如我們想實現(xiàn)一個控件 距離其父控件上下左右都為10的效果:
NSInteger padding = 10;
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
是不是爽爆了....
更多更詳細的信息可以去其官方文檔上查看