iOS開發框架--MyLayout

机械心發表於2024-10-20

MyLayout 框架不僅支援 Objective-C,也可以在 Swift 中使用。透過 MyLayout,可以使用物件導向的方式來建立和管理檢視的佈局,簡化了 Auto Layout 中繁瑣的約束設定流程。在 Objective-C 中,MyLayout 提供了相同的佈局型別和屬性,使用方式稍有不同,主要是語法和呼叫方式上的差異。

先介紹一下如何使用吧,線性佈局和相對佈局是用的比較多的佈局方式。

1. 線性佈局(MyLinearLayout)

線性佈局是一種裡面的子檢視按新增的順序從上到下或者從左到右依次排列的單列(單行)佈局檢視,因此裡面的子檢視是透過新增的順序建立約束和依賴關係的。 子檢視從上到下依次排列的線性佈局檢視稱為垂直線性佈局檢視,而子檢視從左到右依次排列的線性佈局檢視則稱為水平線性佈局

建立一個垂直線性佈局的示例:

MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert];
rootLayout.frame = self.view.bounds;
rootLayout.topPos.equalTo(@0);
rootLayout.leftPos.equalTo(@0);
rootLayout.rightPos.equalTo(@0);
rootLayout.bottomPos.equalTo(@0);

// 新增子檢視
UIView *view1 = [UIView new];
view1.myHeight = 50;
view1.leftPos.equalTo(@10);
view1.rightPos.equalTo(@10);
[view1 setBackgroundColor:[UIColor redColor]];
[rootLayout addSubview:view1];

UIView *view2 = [UIView new];
view2.myHeight = 100;
view2.leftPos.equalTo(@10);
view2.rightPos.equalTo(@10);
[view2 setBackgroundColor:[UIColor blueColor]];
[rootLayout addSubview:view2];

[self.view addSubview:rootLayout];

在這個例子中,我們建立了一個垂直線性佈局容器 rootLayout,並在其中新增了兩個 UIView 子檢視。每個子檢視都有自己的高度和邊距設定。view1view2 分別設定了不同的高度,且左右邊距為 10。

2. 相對佈局(MyRelativeLayout)

相對佈局允許子檢視透過相對父檢視或者其他子檢視的位置來佈局。

相對佈局示例:

MyRelativeLayout *rootLayout = [MyRelativeLayout new];
rootLayout.frame = self.view.bounds;
rootLayout.topPos.equalTo(@0);
rootLayout.leftPos.equalTo(@0);
rootLayout.rightPos.equalTo(@0);
rootLayout.bottomPos.equalTo(@0);

UIView *view1 = [UIView new];
view1.mySize = CGSizeMake(100, 100);
view1.centerXPos.equalTo(rootLayout.centerXPos);  // 水平居中
view1.topPos.equalTo(@10);                        // 距離父檢視頂部 10
[view1 setBackgroundColor:[UIColor redColor]];
[rootLayout addSubview:view1];

UIView *view2 = [UIView new];
view2.mySize = CGSizeMake(100, 100);
view2.topPos.equalTo(view1.bottomPos).offset(10); // 位於 view1 底部,間隔 10
view2.centerXPos.equalTo(view1.centerXPos);       // 水平與 view1 對齊
[view2 setBackgroundColor:[UIColor blueColor]];
[rootLayout addSubview:view2];

[self.view addSubview:rootLayout];

在這個示例中,view1 在父檢視中水平居中,並且距離頂部有 10 的間隔。而 view2 則位於 view1 的下方,並保持水平對齊。透過設定 centerXPostopPos 等屬性,MyLayout 可以輕鬆實現相對佈局。

3. 佈局框架的類架構

這張圖展示了 MyLayout 佈局框架的類架構,幫助開發者理解其內部設計和結構。

  1. MyBaseLayout

    • 這是 MyLayout 框架的基礎類,所有具體的佈局類(如 MyLinearLayoutMyFrameLayout 等)都繼承自它。它負責處理佈局容器的基礎功能,如檢視的排列、佈局更新等。
  2. 佈局子類

    • MyBaseLayout 派生的不同佈局型別用於支援多種佈局方式:
      • MyLinearLayout:線性佈局,檢視依次排列,可以是垂直或水平。
      • MyFrameLayout:幀佈局,子檢視重疊在一起,根據設定的大小、位置顯示。
      • MyRelativeLayout:相對佈局,子檢視可以相對於其他檢視或容器進行佈局。
      • MyFlowLayout:流式佈局,子檢視按行或列排列,類似於文字換行的效果。
      • MyFloatLayout:浮動佈局,檢視可以根據容器空間自動排列。
      • MyPathLayout:路徑佈局,子檢視可以沿著路徑排列。
      • MyGridLayout:網格佈局,子檢視按網格排列。
      • MyTableLayout:表格佈局,子檢視按表格形式排列。
  3. MyViewSizeClass 和子類:

    • MyViewSizeClass 是用於定義檢視在不同尺寸類別下的表現,類似於 iOS 的 Size Class 概念。
    • MyLayoutViewSizeClass 是它的子類,用於處理 MyLayout 檢視的大小、邊距、位置等屬性。
    • 不同的佈局有各自的 ViewSizeClass,如 MyLinearLayoutViewSizeClassMyTableLayoutViewSizeClass 等,來定義在這些佈局中的尺寸規則。
  4. UIView 的擴充套件 (Category)

    • 透過對 UIView 進行擴充套件,MyLayout 框架為檢視新增了自定義佈局屬性,如 leftPostopPoswidthSizeheightSize 等。這些屬性與 MyLayoutPosMyLayoutSize 類相關聯,幫助開發者透過簡單的設定實現複雜的佈局需求。
  5. MyLayoutPos 和 MyLayoutSize

    • MyLayoutPos:MyLayoutPos類是用來描述一個檢視所在的位置的類。UIView中擴充套件出了leftPos,topPos,bottomPos,rightPos,centerXPos,centerYPos這六個變數來實現檢視的定位操作。您可以用這些變數的equalTo方法來設定檢視之間的邊距和間距。 equalTo 方法可以設定NSNumber, MyLayoutPos, NSArray<MyLayoutPos*>這幾種值,分別用於不同的場景。同時系統提供了6個簡單的變數myLeft, myTop, myBottom, myRight, myCenterX, mYCenterY來設定NSNumber型別的值,比如 A.leftPos.equalTo(@10); 等價於 A.myLeft = 10;.

    • MyLayoutSize:MyLayoutSize類是用來描述一個檢視的尺寸的類。UIView中擴充套件出了widthSize,heightSize這兩個變數來實現檢視的寬度和高度尺寸的設定。您可以用其中的equalTo方法來設定檢視的寬度和高度。equalTo方法可以設定NSNumber, MyLayoutSize, NSArray<MyLayoutSize*>這幾種值,分別用於不同的場景。同時系統提供了2個簡單的變數myWidth,myHeight來設定NSNumber型別的值,比如A.widthSize.equalTo(@10); 等價於A.myWidth = 10;.

  6. MyWeight

    • MyWeight 是一個與佈局權重相關的概念,用於控制檢視在容器中佔據的相對空間。

透過這個類架構圖,可以看到 MyLayout 框架是如何透過繼承和擴充套件的方式,將多種佈局模式整合到一個框架中,從而簡化複雜佈局的實現。

4. 底層原理

MyLayout 的底層原理主要是透過對每個檢視的佈局屬性(如 myLeftMarginmyWidthmyHeight 等)進行計算,並在佈局容器中根據這些屬性重新調整每個子檢視的位置和大小。這個過程與 Auto Layout 系統相似,但 MyLayout 不依賴 iOS 自帶的 Auto Layout 約束機制,而是透過手動佈局來最佳化效能和簡化實現。

1. 檢視樹遍歷與佈局計算

MyLayout 的核心機制是遍歷檢視樹,逐個計算每個檢視的位置和大小。這個過程在佈局檢視的 layoutSubviews 方法中觸發。當父佈局容器需要重新佈局時,會呼叫 layoutSubviews,在這個方法中,MyLayout 遍歷所有子檢視,並根據子檢視的佈局屬性(如邊距、寬高、自適應等)進行計算和定位。

每個檢視的佈局屬性都會影響到其最終的 frame,MyLayout 會根據這些屬性和佈局容器的尺寸來動態調整子檢視的位置和大小。例如:

  • myLeftMarginmyRightMargin 決定檢視在父檢視中的左右間距。
  • myWidth 決定檢視的寬度,可以是固定值、百分比或根據內容自適應。
  • weight 屬性用於動態分配剩餘空間,類似於 flexboxflex 屬性。
2. 佈局屬性的自定義與擴充套件

MyLayout 基於 UIView 的擴充套件,將自定義的佈局屬性直接掛載在每個子檢視上。透過為 UIView 擴充套件自定義屬性(例如 myLeftMarginmyHeight),MyLayout 實現了佈局屬性的可訪問性。然後,框架透過在佈局檢視的 layoutSubviews 方法中訪問這些自定義屬性,完成佈局的計算和調整。

這些自定義屬性的設定值可以是固定數值,也可以是相對父檢視或兄弟檢視的動態值,這使得 MyLayout 在佈局時非常靈活。例如:

  • 當檢視的寬度是相對父檢視的寬度時,可以設定 myWidth.equalTo(self.view.myWidth),表示檢視的寬度等於父檢視寬度。
3. 自適應與動態調整

MyLayout 支援子檢視的自適應佈局,透過計算檢視的固有內容大小和父檢視的剩餘空間,動態調整子檢視的尺寸和位置。與 Auto Layout 類似,當某個檢視的內容發生變化時(例如文字檢視內容變長),MyLayout 可以自動調整該檢視的大小,使其適應新的內容。

此外,MyLayout 還支援動態調整佈局。當父檢視的尺寸改變時(例如旋轉螢幕或視窗大小調整),MyLayout 會重新計算所有子檢視的佈局,確保它們始終適應當前的父檢視大小。

4. 避免 Auto Layout 的效能開銷

MyLayout 的一個主要優勢是避免了 Auto Layout 系統帶來的效能開銷。Auto Layout 透過約束系統來管理佈局,內部需要解決一系列的線性方程,這可能在複雜佈局場景下導致效能瓶頸。而 MyLayout 直接操作檢視的 frame 屬性,跳過了約束的解析過程,從而提高了佈局效率,特別是在需要頻繁動態調整佈局的場景中表現更佳。

5. 佈局型別的實現

MyLayout 提供了多種佈局型別(線性佈局、相對佈局、表格佈局等),這些佈局型別的實現原理是根據佈局容器的不同型別,採用不同的演算法來計運算元檢視的排列方式。例如:

  • 線性佈局:透過遍歷子檢視,按照垂直或水平方向依次排列,並根據 myLeftMarginmyTopMargin 等屬性調整每個檢視的位置。
  • 相對佈局:根據子檢視的相對定位屬性(例如 centerXPos.equalTo()),在佈局時計算相對關係,調整檢視的位置。
  • 表格佈局:按照行列方式排列子檢視,類似於表格的佈局邏輯。
6. 效能最佳化

MyLayout 的效能最佳化體現在以下幾個方面:

  • 避免不必要的重繪:在子檢視的佈局屬性發生變化時,MyLayout 會觸釋出局重新整理,但它會避免無關子檢視的重繪和佈局調整,減少效能開銷。
  • 輕量級的佈局計算:由於不依賴 Auto Layout 的約束解析,MyLayout 的佈局計算只涉及簡單的幾何運算,避免了複雜的約束求解過程,從而提升佈局效率。
  • 支援快取機制:在某些複雜場景下,MyLayout 還可以透過快取佈局結果,進一步減少重複計算的開銷。

4. 使用

podfile中加入,然後執行,命令:pod install

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'

pod 'MyLayout'

框架作者還給出了一個y演示demo:

相關文章