SDAutoLayout 和 Masonry

技術有限,很少寫文章。在iOS中,我們知道布局一直是頭疼的事,它不像Android提供XML,系統有一個解析器負責解析并布局,雖然故事板文件也是xml文件,由系統解析,但只有鬼才會編輯那個XML。在iOS中,我們通常對frame進行賦值或使用故事板和NSLayoutConstraint, 那個VLF或許也有的用,但太煩了。使用NSLayoutConstraint用代碼寫約束,在開發中我很少用,一般直接通過故事板拉約束就可以了。在此我們不討論特別復雜的布局,因為如果是特別復雜的布局,我們可以考慮結合h5來做,使用JS做交互上的東西。那是另外一套體系了,現在我們考慮使用效率更高的原生控件的布局方式。
在布局中,網上見到使用代碼也可以方便布局的第三方庫:Masonry, 后來又看到國內牛人寫了SDAutoLayout,在實際開發中,也可能會用到使用代碼布局的情況,因為SDAutoLayout的API相當精美,而且有大量的Demo可以參考,比如這里,于是我簡單看了一下SDAutoLayout的源代碼,并做一個小分析。

SDAutoLayout提供的API漂亮:
比如我要一個視圖:
上:20
下:20
寬:100
高:100

UIView *view = UIView.new;
view.sd_layout.leftSpaceToView(self.view, 20).bottomSpaceToView(self.view, 20).widthIs(100).heightIs(100)

基本原理分析:

view是沒有sd_layout屬性的,作者使用動態關聯,擴展UIView, 調用getter方法時sd_layout對象生成,此對象作為view對象的布局控制模塊, 每次生成一個sd_layout對象,都把它加為super viewautolayoutModelsArray中,sd_layout對象即SDAutoLyaout的實例,SDAutoLayout下設left, top, bottom, right等對象負責具體的布局... 用文字解釋不了,看個圖:

圖示1.png

圖示2.png

可見作者的面向對象思想是相當牛B的。

當我們調用 view.sd_layout.leftSpaceToView(self.view, 20) 時,sd_layout 對象的leftSpaceToView 是個懶加載的block, 我們這樣是調用這個block, 這樣在內部SDAutoLayout對frame進行賦值,所以,SDAutoLayout并不像其名字AutoLayout所示通過NSLayoutConstraint來對view進行布局的,它是本質上通過修改視圖的frame來處理的。

繼續:當調用[someView updateLayout]的時候,我們順著API找下去:

// UIView的Category中:
- (void)updateLayout
{
    [self.superview layoutSubviews];
}

也就是說,當對一個view updateLayout的時候,它會調其父視圖的layoutSubviews, 而當調用layoutSubviews時,SDAutoLayout使用方法交換,調用sd_layoutSubviews,我們可以看到交換方法的源代碼:

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        NSArray *selStringsArray = @[@"setText:"];
        
        [selStringsArray enumerateObjectsUsingBlock:^(NSString *selString, NSUInteger idx, BOOL *stop) {
            NSString *mySelString = [@"sd_" stringByAppendingString:selString];
            
            Method originalMethod = class_getInstanceMethod(self, NSSelectorFromString(selString));
            Method myMethod = class_getInstanceMethod(self, NSSelectorFromString(mySelString));
            method_exchangeImplementations(originalMethod, myMethod);
        }];
    });
}

調用sd_layoutSubviews

// UIView的Category中:
- (void)sd_layoutSubviews
{
    [self sd_layoutSubviews]; // 此處由于方法交換,調用[self layoutSubviews]   
    [self sd_layoutSubviewsHandle];
}

sd_layoutSubviewsHandle中,我們知道,看上面圖中視圖有一個autolayoutModelsArray,它記錄著其所有子視圖用SDAutoLayout布局的對象,并可以找到每一個子視圖所關聯著的SDAutoLayout對象,所它來布局。上面談到,在SDAutoLayout中有left, top, bottom, right等具體的對象,在這些對象中都有一個refView代表其所參照的視圖,我們可以把它想像成鏈表,鏈表的上一個節點動了,其被參照的視圖根據refView找到前一個節點,從而作刷新。
但是,想來想去,由于SDAutoLayout是基于對原始布局方式frame進行設置,這就可能隱藏一個弱點:如果被依賴的視圖沒有準備好,這時候someView需要刷新會不會亂?我們寫個程序測一下:

我們做兩個視圖:
View0 - 紅色
View1 - 綠色

圖示3.png

代碼如下:

- (void)test {
    UIView *superView = self.view;
    
    UIView *view0 = UIView.new;
    view0.backgroundColor = [UIColor redColor];
    [self.view addSubview:view0];
    
    UIView *view1 = UIView.new;
    view1.backgroundColor = [UIColor greenColor];
    [self.view addSubview:view1];
   
    // view0參照view1
    // 先寫view1的布局,再寫view0的布局
    view1.sd_layout.rightSpaceToView(superView, 20).topSpaceToView(superView, 100).widthIs(100).heightIs(100);
    view0.sd_layout.leftSpaceToView(superView, 20).topSpaceToView(superView, 100).heightIs(100).rightSpaceToView(view1, 100);
}

程序運行正常,我們得到了期望的結果,但是如果把view0的布局和view1的布局調換一下:

- (void)test {
    ...

    // view0參照view1
    // 先寫view0的布局,再寫view1的布局
    view0.sd_layout.leftSpaceToView(superView, 20).topSpaceToView(superView, 100).heightIs(100).rightSpaceToView(view1, 100);
    view1.sd_layout.rightSpaceToView(superView, 20).topSpaceToView(superView, 100).widthIs(100).heightIs(100);
}

運行錯亂,紅色視圖不見了。

這就是剛才所說的,位置調換,沒有得到及時刷新的原因,這無疑是SDAutoLayout不好的地方,如果我們非要先寫view1的布局,再寫view0的布局,就要手動加上刷新:


- (void)test {
    ...
    
    // view0參照view1
    // 先寫view1的布局,再寫view0的布局
    view0.sd_layout.leftSpaceToView(superView, 20).topSpaceToView(superView, 100).heightIs(100).rightSpaceToView(view1, 100);
    view1.sd_layout.rightSpaceToView(superView, 20).topSpaceToView(superView, 100).widthIs(100).heightIs(100);
   
    [view1 updateLayout]; // 加上這一句
}

為什么加上這一句就好了呢?參照上面我們的圖1中,當調用 [view1 updateLayout]; 的時候,實系上會調用其superView的layoutSubviews,而superView維護了一個autolayoutModelsArray,這個數組存儲了子視圖的布局模塊SDAutoLayout對象,然后再次刷新,這時候view1已準備就緒,當然就好了。

但是在Masonry中,我們調換位置寫都能得到正確的結果:

[view0 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.offset(20);
        make.top.offset(100);
        make.height.equalTo(@100);
        make.right.equalTo(view1.mas_left).with.offset(-100);
}];
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.offset(100);
        make.right.offset(-20);
        make.width.equalTo(@100);
        make.height.equalTo(@100);
 }];

Masonry的工作原理待后續,但它是基于NSLayoutConstraint的,NSLayoutConstraint說到最最底部就是一個數學公式,我們關心不了那么多,Masonry和SDAutoLayout最大的不同就是上面所說一個基于對frame的設置,一個是基于NSLayoutConstraint.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容