IOS開(kāi)發(fā)系列——Masonry手寫(xiě)Autolayout專(zhuān)題【整理】

Masonry手寫(xiě)Autolayout專(zhuān)題

Masonry介紹與使用實(shí)踐:快速上手Autolayout

http://www.cocoachina.com/ios/20141219/10702.html

1Masonry開(kāi)發(fā)技巧

1.1簡(jiǎn)單使用技巧

1.1.1[基礎(chǔ)]居中顯示一個(gè)view

- (void)viewDidLoad

{

[superviewDidLoad];

// Do any additional setup after loading the view.

WS(ws);

UIView *sv = [UIViewnew];

[sv showPlaceHolder];

sv.backgroundColor = [UIColor blackColor];

[self.view addSubview:sv];

[sv mas_makeConstraints:^(MASConstraintMaker *make) {

make.center.equalTo(ws.view);

make.size.mas_equalTo(CGSizeMake(300, 300));

}];

}

代碼效果

使用我之間寫(xiě)的MMPlaceHolder可以看到superview已經(jīng)按照我們預(yù)期居中并且設(shè)置成了適當(dāng)?shù)拇笮?/p>

那么先看看這幾行代碼

//從此以后基本可以?huà)仐塁GRectMake了

UIView *sv = [UIViewnew];

//在做autoLayout之前一定要先將view添加到superview上否則會(huì)報(bào)錯(cuò)

[self.view addSubview:sv];

//mas_makeConstraints就是Masonry的autolayout添加函數(shù)將所需的約束添加到block中行了

[sv mas_makeConstraints:^(MASConstraintMaker *make) {

//將sv居中(很容易理解吧?)

make.center.equalTo(ws.view);

//將size設(shè)置成(300,300)

make.size.mas_equalTo(CGSizeMake(300, 300));

}];

這里有兩個(gè)問(wèn)題要分解一下

首先在Masonry中能夠添加autolayout約束有三個(gè)函數(shù)

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;

- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;

- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;

/*

mas_makeConstraints只負(fù)責(zé)新增約束Autolayout不能同時(shí)存在兩條針對(duì)于同一對(duì)象的約束否則會(huì)報(bào)錯(cuò)

mas_updateConstraints針對(duì)上面的情況會(huì)更新在block中出現(xiàn)的約束不會(huì)導(dǎo)致出現(xiàn)兩個(gè)相同約束的情況

mas_remakeConstraints則會(huì)清除之前的所有約束僅保留最新的約束

三種函數(shù)善加利用就可以應(yīng)對(duì)各種情況了

*/

其次equalTo和mas_equalTo的區(qū)別在哪里呢?其實(shí)mas_equalTo是一個(gè)MACRO

#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只是對(duì)其參數(shù)進(jìn)行了一個(gè)BOX操作(裝箱) MASBoxValue的定義具體可以看看源代碼太長(zhǎng)就不貼出來(lái)了

所支持的類(lèi)型除了NSNumber支持的那些數(shù)值類(lèi)型之外就只支持CGPoint CGSize UIEdgeInsets

介紹完這幾個(gè)問(wèn)題我們就繼續(xù)往下了PS:剛才定義的sv會(huì)成為我們接下來(lái)所有sample的superView

1.1.2[初級(jí)]讓一個(gè)view略小于其superView(邊距為10)

UIView *sv1 = [UIViewnew];

[sv1 showPlaceHolder];

sv1.backgroundColor = [UIColor redColor];

[sv addSubview:sv1];

[sv1 mas_makeConstraints:^(MASConstraintMaker *make) {

make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));

/*等價(jià)于

make.top.equalTo(sv).with.offset(10);

make.left.equalTo(sv).with.offset(10);

make.bottom.equalTo(sv).with.offset(-10);

make.right.equalTo(sv).with.offset(-10);

*/

/*也等價(jià)于

make.top.left.bottom.and.right.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));

*/

}];

代碼效果

可以看到edges其實(shí)就是top,left,bottom,right的一個(gè)簡(jiǎn)化分開(kāi)寫(xiě)也可以一句話(huà)更省事

那么為什么bottom和right里的offset是負(fù)數(shù)呢?因?yàn)檫@里計(jì)算的是絕對(duì)的數(shù)值計(jì)算的bottom需要小魚(yú)sv的底部高度所以要-10同理用于right

這里有意思的地方是and和with其實(shí)這兩個(gè)函數(shù)什么事情都沒(méi)做

- (MASConstraint *)with{

returnself;

}

- (MASConstraint *)and {

returnself;

}

但是用在這種鏈?zhǔn)秸Z(yǔ)法中就非常的巧妙和易懂不得不佩服作者的心思(雖然我現(xiàn)在基本都會(huì)省略)

1.1.3[初級(jí)]讓兩個(gè)高度為150的view垂直居中且等寬且等間隔排列間隔為10(自動(dòng)計(jì)算其寬度)

int padding1 = 10;

[sv2 mas_makeConstraints:^(MASConstraintMaker *make) {

make.centerY.mas_equalTo(sv.mas_centerY);

make.left.equalTo(sv.mas_left).with.offset(padding1);

make.right.equalTo(sv3.mas_left).with.offset(-padding1);

make.height.mas_equalTo(@150);

make.width.equalTo(sv3);

}];

[sv3 mas_makeConstraints:^(MASConstraintMaker *make) {

make.centerY.mas_equalTo(sv.mas_centerY);

make.left.equalTo(sv2.mas_right).with.offset(padding1);

make.right.equalTo(sv.mas_right).with.offset(-padding1);

make.height.mas_equalTo(@150);

make.width.equalTo(sv2);

}];

代碼效果

這里我們?cè)趦蓚€(gè)子view之間互相設(shè)置的約束可以看到他們的寬度在約束下自動(dòng)的被計(jì)算出來(lái)了

1.1.4[中級(jí)]在UIScrollView順序排列一些view并自動(dòng)計(jì)算contentSize

UIScrollView *scrollView = [UIScrollViewnew];

scrollView.backgroundColor = [UIColor whiteColor];

[sv addSubview:scrollView];

[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {

make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(5,5,5,5));

}];

UIView *container = [UIViewnew];

[scrollView addSubview:container];

[container mas_makeConstraints:^(MASConstraintMaker *make) {

make.edges.equalTo(scrollView);

make.width.equalTo(scrollView);

}];

int count = 10;

UIView *lastView = nil;

for( int i = 1 ; i <= count ; ++i )

{

UIView *subv = [UIViewnew];

[container addSubview:subv];

subv.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )

saturation:( arc4random() % 128 / 256.0 ) + 0.5

brightness:( arc4random() % 128 / 256.0 ) + 0.5

alpha:1];

[subv mas_makeConstraints:^(MASConstraintMaker *make) {

make.left.and.right.equalTo(container);

make.height.mas_equalTo(@(20*i));

if( lastView )

{

make.top.mas_equalTo(lastView.mas_bottom);

}

else

{

make.top.mas_equalTo(container.mas_top);

}

}];

lastView = subv;

}

[container mas_makeConstraints:^(MASConstraintMaker *make) {

make.bottom.equalTo(lastView.mas_bottom);

}];

頭部效果

尾部效果

從scrollView的scrollIndicator可以看出scrollView的內(nèi)部已如我們所想排列好了

這里的關(guān)鍵就在于container這個(gè)view起到了一個(gè)中間層的作用能夠自動(dòng)的計(jì)算uiscrollView的contentSize

1.1.5[高級(jí)]橫向或者縱向等間隙的排列一組view

很遺憾autoLayout并沒(méi)有直接提供等間隙排列的方法(Masonry的官方demo中也沒(méi)有對(duì)應(yīng)的案例)但是參考案例3我們可以通過(guò)一個(gè)小技巧來(lái)實(shí)現(xiàn)這個(gè)目的為此我寫(xiě)了一個(gè)Category

@implementation UIView(Masonry_LJC)

- (void) distributeSpacingHorizontallyWith:(NSArray*)views

{

NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count+1];

for( int i = 0 ; i < views.count+1 ; ++i )

{

UIView *v = [UIViewnew];

[spaces addObject:v];

[self addSubview:v];

[v mas_makeConstraints:^(MASConstraintMaker *make) {

make.width.equalTo(v.mas_height);

}];

}

UIView *v0 = spaces[0];

__weak __typeof(&*self)ws = self;

[v0 mas_makeConstraints:^(MASConstraintMaker *make) {

make.left.equalTo(ws.mas_left);

make.centerY.equalTo(((UIView*)views[0]).mas_centerY);

}];

UIView *lastSpace = v0;

for( int i = 0 ; i < views.count; ++i )

{

UIView *obj = views[i];

UIView *space = spaces[i+1];

[obj mas_makeConstraints:^(MASConstraintMaker *make) {

make.left.equalTo(lastSpace.mas_right);

}];

[space mas_makeConstraints:^(MASConstraintMaker *make) {

make.left.equalTo(obj.mas_right);

make.centerY.equalTo(obj.mas_centerY);

make.width.equalTo(v0);

}];

lastSpace = space;

}

[lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {

make.right.equalTo(ws.mas_right);

}];

}

- (void) distributeSpacingVerticallyWith:(NSArray*)views

{

NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count+1];

for( int i = 0 ; i < views.count+1 ; ++i )

{

UIView *v = [UIViewnew];

[spaces addObject:v];

[self addSubview:v];

[v mas_makeConstraints:^(MASConstraintMaker *make) {

make.width.equalTo(v.mas_height);

}];

}

UIView *v0 = spaces[0];

__weak __typeof(&*self)ws = self;

[v0 mas_makeConstraints:^(MASConstraintMaker *make) {

make.top.equalTo(ws.mas_top);

make.centerX.equalTo(((UIView*)views[0]).mas_centerX);

}];

UIView *lastSpace = v0;

for( int i = 0 ; i < views.count; ++i )

{

UIView *obj = views[i];

UIView *space = spaces[i+1];

[obj mas_makeConstraints:^(MASConstraintMaker *make) {

make.top.equalTo(lastSpace.mas_bottom);

}];

[space mas_makeConstraints:^(MASConstraintMaker *make) {

make.top.equalTo(obj.mas_bottom);

make.centerX.equalTo(obj.mas_centerX);

make.height.equalTo(v0);

}];

lastSpace = space;

}

[lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {

make.bottom.equalTo(ws.mas_bottom);

}];

}

@end

簡(jiǎn)單的來(lái)測(cè)試一下

UIView *sv11 = [UIViewnew];

UIView *sv12 = [UIViewnew];

UIView *sv13 = [UIViewnew];

UIView *sv21 = [UIViewnew];

UIView *sv31 = [UIViewnew];

sv11.backgroundColor = [UIColor redColor];

sv12.backgroundColor = [UIColor redColor];

sv13.backgroundColor = [UIColor redColor];

sv21.backgroundColor = [UIColor redColor];

sv31.backgroundColor = [UIColor redColor];

[sv addSubview:sv11];

[sv addSubview:sv12];

[sv addSubview:sv13];

[sv addSubview:sv21];

[sv addSubview:sv31];

//給予不同的大小測(cè)試效果

[sv11 mas_makeConstraints:^(MASConstraintMaker *make) {

make.centerY.equalTo(@[sv12,sv13]);

make.centerX.equalTo(@[sv21,sv31]);

make.size.mas_equalTo(CGSizeMake(40, 40));

}];

[sv12 mas_makeConstraints:^(MASConstraintMaker *make) {

make.size.mas_equalTo(CGSizeMake(70, 20));

}];

[sv13 mas_makeConstraints:^(MASConstraintMaker *make) {

make.size.mas_equalTo(CGSizeMake(50, 50));

}];

[sv21 mas_makeConstraints:^(MASConstraintMaker *make) {

make.size.mas_equalTo(CGSizeMake(50, 20));

}];

[sv31 mas_makeConstraints:^(MASConstraintMaker *make) {

make.size.mas_equalTo(CGSizeMake(40, 60));

}];

[sv distributeSpacingHorizontallyWith:@[sv11,sv12,sv13]];

[sv distributeSpacingVerticallyWith:@[sv11,sv21,sv31]];

[sv showPlaceHolderWithAllSubviews];

[sv hidePlaceHolder];

代碼效果

perfect!簡(jiǎn)潔明了的達(dá)到了我們所要的效果

1.2高級(jí)開(kāi)發(fā)技巧

1.2.1更新layout

1.2.1.1mas_updateConstraints

使用Masonry創(chuàng)建constraint來(lái)定義布局的方式有三種:mas_makeConstraints,mas_updateConstraints勒虾,mas_remakeConstraints。

有時(shí)你需要更新constraint(例如,動(dòng)畫(huà)和調(diào)試)而不是創(chuàng)建固定constraint掏导,可以使用mas_updateConstraints方法奠骄。

1.2.1.2mas_remakeConstraints

mas_remakeConstraints與mas_updateConstraints比較相似藕甩,都是更新constraint。不過(guò)锦爵,mas_remakeConstraints是刪除之前constraint,然后再添加新的constraint(適用于移動(dòng)動(dòng)畫(huà))奥裸;而mas_updateConstraints只是更新constraint的值险掀。

1.2.1.3代碼示例

[_loginButtonmas_remakeConstraints:^(MASConstraintMaker*make) {

make.width.equalTo(weakSelf.loginNameTextField.mas_width);

make.height.equalTo(weakSelf.loginNameTextField.mas_height);

make.centerX.mas_equalTo(weakSelf.loginContainerView.mas_centerX);

if(_loginVerifyImageCodeTextField) {

make.top.equalTo(weakSelf.loginVerifyImageCodeTextField.mas_bottom).with.offset(20);

}

}];

[_loginDescLabelmas_updateConstraints:^(MASConstraintMaker*make) {

make.top.equalTo(weakSelf.loginButton.mas_bottom).with.offset(5);

}];

1.3常見(jiàn)問(wèn)題

1.3.1在TableCell單元格中無(wú)法使用Masonry

在TableCell中增加子控件時(shí),無(wú)法正常使用Masonry來(lái)進(jìn)行布局湾宙,可能是Cell重用引起的樟氢。

1.3.2使用了Masonry布局的控件最好手動(dòng)釋放內(nèi)存,若有APP自動(dòng)釋放內(nèi)存侠鳄,會(huì)有延遲埠啃,導(dǎo)致頁(yè)面布局失敗

//需要手動(dòng)釋放mas_makeConstraints的內(nèi)存,若有APP自動(dòng)釋放內(nèi)存伟恶,會(huì)有延遲碴开,導(dǎo)致頁(yè)面布局失敗

_loginNameTextField=nil;

_loginVerifyCodeGetButton=nil;

_loginVerifyCodeTextField=nil;

1.3.3盡量直接用equalTo,而不用mas_equalTo

mas_equalTo需要等控件自身布局完成了之后才能調(diào)用博秫,而equalTo不需要潦牛。因此進(jìn)行子視圖布局時(shí),用前者容易掛機(jī)挡育。

1.3.4使用dispatch_get_main_queue保證布局與后續(xù)處理的同步

雖然mas_updateConstraints的block回調(diào)是順序執(zhí)行的巴碗,但是布局真正起作用還是放在了main_queue的下一個(gè)runloop中執(zhí)行的,所以如果要在布局完成后順序執(zhí)行某些處理(例如開(kāi)始進(jìn)行自定義繪制或者異步繪制)静盅,最好手動(dòng)放到main_queue的下一個(gè)runloop中執(zhí)行良价,確保布局完成后才開(kāi)始執(zhí)行代碼寝殴。

如下是示例代碼:

-(void)draw

{

[selfsetLayout];

//因?yàn)長(zhǎng)ayout庫(kù)是異步的,為了保證執(zhí)行順序上的同步明垢,需要作此處理

dispatch_async(dispatch_get_main_queue(), ^{

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

CGRectdrawFrame =CGRectMake(0,0,SCREEN_BOUNDS.size.width,_headerHeight);

UIGraphicsBeginImageContextWithOptions(drawFrame.size,YES,0);

CGContextRefcontext =UIGraphicsGetCurrentContext();

if(!context) {

return;

}

[[UIColorwhiteColor]set];

CGContextFillRect(context,drawFrame);

[_separationLineViewdrawRect:CGRectMake(0,0,

drawFrame.size.width,_lineHeight)];

[_iconButtondrawRect:_iconButton.frame];

[_nameButtondrawRect:_nameButton.frame];

[_postDateLabeldrawRect:_postDateLabel.frame];

UIImage*tempImage =UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

dispatch_async(dispatch_get_main_queue(),^{

//_postContentTextImageView.frame= _postContentTextView.frame;//textViewRect;//

[_iconButtonsetHidden:NO];

[_nameButtonsetHidden:NO];

[_postDateLabelsetHidden:NO];

_bgImageView.image=nil;

_bgImageView.image=tempImage;

});

});

});

}

2參考鏈接

實(shí)時(shí)顯示iOS編寫(xiě)UI代碼效果

http://blog.csdn.net/zhang_red/article/details/45503683

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蚣常,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子痊银,更是在濱河造成了極大的恐慌抵蚊,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件溯革,死亡現(xiàn)場(chǎng)離奇詭異贞绳,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)致稀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)冈闭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人抖单,你說(shuō)我怎么就攤上這事萎攒。” “怎么了矛绘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵耍休,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我货矮,道長(zhǎng)羊精,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任囚玫,我火速辦了婚禮喧锦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘劫灶。我一直安慰自己裸违,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布本昏。 她就那樣靜靜地躺著供汛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涌穆。 梳的紋絲不亂的頭發(fā)上怔昨,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音宿稀,去河邊找鬼趁舀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛祝沸,可吹牛的內(nèi)容都是我干的矮烹。 我是一名探鬼主播越庇,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼奉狈!你這毒婦竟也來(lái)了卤唉?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仁期,失蹤者是張志新(化名)和其女友劉穎桑驱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體跛蛋,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熬的,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赊级。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片押框。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖此衅,靈堂內(nèi)的尸體忽然破棺而出强戴,到底是詐尸還是另有隱情,我是刑警寧澤挡鞍,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站预烙,受9級(jí)特大地震影響墨微,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扁掸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一翘县、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谴分,春花似錦锈麸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至沙兰,卻和暖如春氓奈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鼎天。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工舀奶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斋射。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓育勺,卻偏偏與公主長(zhǎng)得像但荤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子涧至,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容