程式碼適配Masonry使用的詳細介紹
Masonry簡介
Masonry
是一個輕量級的佈局框架,它擁有自己的描述語法(採用更優雅的鏈式語法封裝)來自動佈局,具有很好可讀性且同時支援iOS和Max OS X
等。
總之,對於側重寫程式碼的coder,請你慢慢忘記Frame
,喜歡Masonry
吧
使用前的準備
若是你對於自動佈局很熟練的話,再接觸這個第三方
Masonry
很容易上手的,對UI介面顯示的控制元件的約束本質都是相同的,現在呢,我一般都是喜歡在控制器裡匯入#import "Masonry.h"
之前再新增兩個巨集,來提高App的開發效率。
//1. 對於約束引數可以省去"mas_"
#define MAS_SHORTHAND
//2. 對於預設的約束引數自動裝箱
#define MAS_SHORTHAND_GLOBALS
即:需要我們匯入的框架與巨集如下
//define this constant if you want to use Masonry without the 'mas_' prefix
#define MAS_SHORTHAND
//define this constant if you want to enable auto-boxing for default syntax
#define MAS_SHORTHAND_GLOBALS
#import "Masonry.h" //巨集必須新增在標頭檔案前面
新增約束前提
:被約束的必須有父控制元件,其中約束項都必須是UIView或子類的例項
。
約束的屬性
在此我就列舉幾個可能不太熟悉的吧
@property (nonatomic, strong, readonly) MASConstraint *leading; //首部
@property (nonatomic, strong, readonly) MASConstraint *trailing; //尾部
@property (nonatomic, strong, readonly) MASConstraint *baseline; //文字基線
約束的三種方法
/**
//這個方法只會新增新的約束
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
} ];
// 這個方法會將以前的所有約束刪掉,新增新的約束
[blueView mas_remakeConstraints:^(MASConstraintMaker *make) {
}];
// 這個方法將會覆蓋以前的某些特定的約束
[blueView mas_updateConstraints:^(MASConstraintMaker *make) {
}];
*/
`
常見約束的各種型別
/**
1.尺寸:width、height、size
2.邊界:left、leading、right、trailing、top、bottom
3.中心點:center、centerX、centerY
4.邊界:edges
5.偏移量:offset、insets、sizeOffset、centerOffset
6.priority()約束優先順序(0~1000),multipler乘因數, dividedBy除因數
*/
Masonry約束易忽略的技術點
使用
Masonry
不需要設定控制元件的translatesAutoresizingMaskIntoConstraints
屬性為NO
;
防止block
中的迴圈引用,使用弱引用(這是錯誤觀點),在這裡block
是區域性的引用,block
內部引用self
不會造成迴圈引用的__weak typeof (self) weakSelf = self
;(沒必要的寫法)
Masonry約束控制元件出現衝突的問題
當約束衝突發生的時候,我們可以設定view的key來定位是哪個view
redView.mas_key = @"redView";
greenView.mas_key = @"greenView";
blueView.mas_key = @"blueView";
若是覺得這樣一個個設定比較繁瑣,怎麼辦呢,Masonry則提供了批量設定的巨集MASAttachKeys
MASAttachKeys(redView,greenView,blueView); //一句程式碼即可全部設定
約束iconView
距離各個邊距為30
[iconView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(30, 30, 30, 30));
}];
equalTo 和 mas_equalTo的區別
#define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...) greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...) lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_offset(...) valueOffset(MASBoxValue((__VA_ARGS__)))
得出結論:mas_equalTo
只是對其引數進行了一個BOX
(裝箱) 操作,目前支援的型別:數值型別(NSNumber)、 點(CGPoint)、大小(CGSize)、邊距(UIEdgeInsets)
,而equalTo:
這個方法不會對引數進行包裝。
//常見約束的寫法 這裡太簡單了 ,就不備註了
[iconView makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.view).offset(-30);
make.top.equalTo(self.view).offset(30);
make.height.width.equalTo(100); //== make.size.equalTo(100);
//make.size.mas_equalTo(self.view).multipliedBy(0.5);
//make.top.greaterThanOrEqualTo(self.view).offset(padding);
}];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(10); //with 增強可讀性
make.left.equalTo(greenView.mas_right).and.offset(10); //and 增強可讀性
make.bottom.equalTo(blueView.mas_top).offset(-10);
make.right.equalTo(superview.mas_right).offset(-10);
make.width.equalTo(greenView.mas_width);
make.height.equalTo(@[greenView, blueView]); //約束引數相同可以通過陣列
}];
更新約束的問題
例如:控制器有個按鈕,若是點選按鈕,則按鈕本身的大小、位置會隨機改變
- 監聽按鈕點選
[self.btn addTarget:self action:@selector(didClickBtn:) forControlEvents:UIControlEventTouchUpInside];
-
處理事件
(void) didClickBtn:(UIButton *)button { self.btnSize = CGSizeMake(self.btnSize.width * 1.3, self.btnSize.height * 1.3); //設定一個屬性(btnSize)儲存其大小的變化 //1.告知需要更新約束,但不會立刻開始,系統然後呼叫updateConstraints [self setNeedsUpdateConstraints]; //2.告知立刻更新約束,系統立即呼叫updateConstraints [self updateConstraintsIfNeeded]; //3.這裡動畫當然可以取消,具體看專案的需求 //系統block內引用不會導致迴圈引用,block結束就會釋放引用物件 [UIView animateWithDuration:0.4 animations:^{ [self layoutIfNeeded]; //告知頁面立刻重新整理,系統立即呼叫updateConstraints }]; }
-
蘋果官方建議:新增/更新約束在這個方法
(updateConstraints)
內
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
//更新約束
[self.btn updateConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
make.width.equalTo(@(self.buttonSize.width)).priorityLow();
make.height.equalTo(@(self.buttonSize.height)).priorityLow();
make.width.lessThanOrEqualTo(self);
make.height.lessThanOrEqualTo(self);
}];
//according to apple super should be called at end of method
//最後必須呼叫父類的更新約束
[super updateConstraints];
}
- 設定
requiresConstraintBasedLayout
為YES
+ (BOOL)requiresConstraintBasedLayout{
return YES ; //重寫這個方法 若檢視基於自動佈局的
}
重置約束的問題
對於控制元件的重新約束,則之前的約束都是無效的,步驟都更新約束一樣的,只是在updateConstraints
方法內的約束方法改為了remakeConstraints
,直接貼程式碼吧(仍以按鈕為例,其他原理都是相同的)
//首先監聽按鈕
[self.btn addTarget:self action:@selector(didClickBtn:) forControlEvents:UIControlEventTouchUpInside];
//處理事件
- (void) didClickBtn :(UIButton *)button{
(...) //觸發條件
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
/**
* 動畫展示變化 - 這句程式碼可有可無,參考專案具體的需求
* [UIView animateWithDuration:0.4 animations:^{
* [self layoutIfNeeded];
* }];
*/
}
//重置約束
- (void)updateConstraints {
[self.btn remakeConstraints:^(MASConstraintMaker *make) {
.....
}];
[super updateConstraints];
}
+ (BOOL)requiresConstraintBasedLayout{
return YES ; //重寫這個方法 若檢視基於自動佈局的
}
多個(2個以上)控制元件的等間隔排序顯示
首先介紹2個函式
/**
* axisType 軸線方向
* fixedSpacing 間隔大小
* fixedItemLength 每個控制元件的固定長度/寬度
* leadSpacing 頭部間隔
* tailSpacing 尾部間隔
*
*/
//1. 等間隔排列 - 多個控制元件間隔固定,控制元件長度/寬度變化
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;
//2. 等間隔排列 - 多個固定大小固定,間隔空隙變化
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedItemLength:(CGFloat)fixedItemLength
leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;
//首先新增5個檢視
NSMutableArray *array = [NSMutableArray new];
for (int i = 0; i < 5; i ++) {
UIView *view = [UIView new];
view.backgroundColor = [UIColor greenColor];
[self addSubview:view];
[array addObject:view]; //儲存新增的控制元件
}
//水平方向控制元件間隔固定等間隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:15 leadSpacing:10 tailSpacing:10];
[array makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(50);
make.height.equalTo(70);
}];
//水平方向寬度固定等間隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:70 leadSpacing:10 tailSpacing:10];
[array makeConstraints:^(MASConstraintMaker *make) { //陣列額你不必須都是view
make.top.equalTo(50);
make.height.equalTo(70);
}];
水平方向等間隔.png
水平方向控制元件寬度固定等間隔.png
多行label的約束問題
對於UILabel
文字內容多的問題,個人覺得Masonry
約束設定的非常簡單優雅,在此非常感謝Masonry
的作者@Robert Payne
//建立label
self.label = [UILabel new];
self.label.numberOfLines = 0;
self.label.lineBreakMode = NSLineBreakByTruncatingTail;
self.label.text = @"有的人,沒事時喜歡在朋友圈裡到處點贊,東評論一句西評論一句,比誰都有存在感。等你有事找他了,他就立刻變得很忙,讓你再也找不著。真正的朋友,平常很少聯絡。可一旦你遇上了難處,他會立刻回覆你的訊息,第一時間站出來幫你。所謂的存在感,不是你有沒有出現,而是你的出現有沒有價值。存在感,不是刷出來的,也不是說出來的。有存在感,未必是要個性鋒芒畢露、甚至鋒利扎人。翩翩君子,溫潤如玉,真正有存在感的人,反而不會刻意去強調他的存在感。他的出現,永遠都恰到好處。我所欣賞的存在感,不是長袖善舞巧言令色,而是對他人的真心關照;不是鋒芒畢露計較勝負,而是讓人相處得舒服;不是時時刻刻聒噪不休,而是關鍵時刻能挺身而出。別總急著出風頭,希望你能有恰到好處的存在感。";
[self addSubview: self.label];
[self.label makeConstraints:^(MASConstraintMaker *make) {
make.left.top.equalTo(10);
make.right.equalTo(-10);
}];
//新增約束
- (void)layoutSubviews {
//1. 執行 [super layoutSubviews];
[super layoutSubviews];
//2. 設定preferredMaxLayoutWidth: 多行label約束的完美解決
self.label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
//3. 設定preferredLayoutWidth後,需要再次執行 [super layoutSubviews];
//其實在實際中這步不寫,也不會出錯,官方解釋是說設定preferredLayoutWidth後需要重新計算並佈局介面,所以這步最好執行
[super layoutSubviews];
}
多行label約束.png
UIScrollView的問題
原理同自動佈局一樣 UIScrollView上新增UIView
UIView上新增需要顯示的控制元件 UIScrollView滾動高度取決於顯示控制元件的總高度
對子控制元件做好約束,可達到控制UIView的大小
//建立滾動檢視
UIScrollView *scrollView = [UIScrollView new];
self.scrollView = scrollView;
scrollView.backgroundColor = [UIColor grayColor];
[self addSubview:scrollView];
[self.scrollView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
[self setUpContentView]; //新增內容檢視
- (void)setUpContentView {
//約束UIScrollView上contentView
UIView *contentView = [UIView new];
[self.scrollView addSubview:contentView];
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.width.equalTo(self.scrollView); //此處必填 - 關鍵點
}];
//新增控制元件到contentView,約束原理與自動佈局相同
UIView *lastView;
CGFloat height = 30;
for (int i = 0; i <1 5; i ++) {
UIView *view = UIView.new;
view.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 256.0 green:arc4random() % 255 / 256.0 blue:arc4random() % 255 / 256.0 alpha:1.0];
[contentView addSubview:view];
[view makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(lastView ? lastView.bottom : @0);
make.left.equalTo(0);
make.width.equalTo(contentView.width);
make.height.equalTo(height);
}];
height += 30;
lastView = view;
}
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(lastView.bottom);
}];
}
scrollView的約束設定.png
Masonry
原始碼GitHub
地址下載:Masonry
終於寫完了,有什麼理解不對的直接說...今晚還有巴薩、尤文的歐冠,真心傷不起。。。
文/chongzone(簡書作者)
原文連結:http://www.jianshu.com/p/e3162f3c61fa
著作權歸作者所有,轉載請聯絡作者獲得授權,並標註“簡書作者”。