UITableView、UICollectionView列表控件的快速搭建

前因:

直接用 UITableViewDataSource、UICollectionViewDataSource、UITableViewDelegate、UICollectionViewDelegate 數據源代理方法 去創建一個簡單的 listView (以下統稱 UITableView、UICollectionView 為 listView),步驟實在太麻煩而且沒必要,浪費時間。

目錄:

1、單一數據創建原則;

2、列表數據(cell數據)創建原則;

3、列表組頭組尾數據創建規則;

4、寫法上的注意點;

5、寫法上的技巧;

6、基礎總結;

7、在 cell、headerFooterView、reusableView?中接收傳遞的數據;?

8、listView 代理數據源設置;

9、自適應高度的使用;

10、點擊事件綁定;

11、最后說兩句;


單一數據創建規則:

1、一個基于 NSObject 基類的對象代表一個單一數據;

2、一個數據對象 就 代表 一個列表中的一個 cell;

3、通過 .bind(@"類名"),指定創建 cell 的類名;

4、通過 .bindHeight(cell 高度),綁定?UITableViewCell 的高度;也可綁定?UICollectionViewCell 的高度,這樣的話?UICollectionViewCell 的寬度則等于?UICollectionView 的寬度;

5、通過?.bindSize(item的size),綁定 UICollectionViewCell?的 itemSize; 若也使用?.bindHeight,優先級為?.bindSize >?.bindHeight;

6、通過 .bindSpacing(mt_collectionViewSpacingMake(最小行距、最小間距、組間間距)),指定?UICollectionView 組的 行距,間距,間隔;

以上 提及的用法 使用任一 NSObject 對象,若為 數組對象,除會綁定本身外,還會順帶 綁定 數組元素;

7、若只想綁定 數組對象 而不順帶綁定數組元素,可使用 .arrBind(@"類名") 綁定 數組元素 cell類名;類似的還有 .arrBindSize(item的size),為僅對 數組對象本身綁定 size;

8、針對數組的用法 還有:.bindRowsHeight(@[高度])、.bindItemsSize(@[尺寸])、.bindItemsSpacing(@[mt_collectionViewSpacingMake(最小行距、最小間距、組間間距)]),用于對部分數組元素進行綁定;經實踐證實 不常用,了解即可。

以上 提及的為 一個數據 綁定 cell 的創建原則;可參考本人的用法,如下:

object.bind(@"className").bindHeight(100); ?//?UITableViewCell 或?UICollectionViewCell;

object.bind(@"className").bindSize(CGSizeMake(100, 100)); ?//UICollectionViewCell;

//如果不需要傳入數據,可將 cell 的類名字符串綁定對應尺寸后直接傳入,常用于開發時快速查看 cell樣式,如下:

@"className".bindHeight(100);

@"className".bindSize(CGSizeMake(100, 100)); ?


列表數據(cell數據)創建規則:

1、由 多個 單一 數據 構成的 數組對象,即代表 一個 完整列表 的數據;

2、一個 數組 對象 代表 listView 的 一個組,組內的單一數據 代表 列表的 一個 cell;

3、一個 完整的 列表數據,即為 一個 二維數組;即 最外層數組 內每一個元素 均為數組,代表 列表的 一個組數據;而 內層數組 內每一個元素均為 一個單一數據,代表列表中的該組中的一個 cell;

以上 提及的為 列表數據 的創建原則;可參考本人的用法,如下:

1、一個完成的列表數據,其標準形式如下:

@[

? ? @[

? ? ? ? object1.bind(@"className").bindHeight(100),

? ? ? ? object2.bind(@"className").bindHeight(100),

? ? ? ? object3.bind(@"className").bindHeight(100)

? ? ],

? ? @[

? ? ? ? object4.bind(@"className").bindHeight(100),

? ? ? ? object5.bind(@"className").bindHeight(100),

? ? ? ? object6.bind(@"className").bindHeight(100)

? ? ],

? ? @[

? ? ? ? object7.bind(@"className").bindHeight(100),

? ? ? ? object8.bind(@"className").bindHeight(100),

? ? ? ? object9.bind(@"className").bindHeight(100)

? ? ]

];

//其含義為:這個列表有 3個組,每一組有 3 個類名為 className 的cell,且高度為100

2、 可以將 綁定 添加至 數組上,這樣就不用每一個數組元素寫相同的綁定,如下:

@[

? ? @[object1, object2, object3].bind(@"className").bindHeight(100),

? ? @[object4, object5, object6].bind(@"className").bindHeight(100),

? ? @[object7, object8, object9].bind(@"className").bindHeight(100),

];

//這個寫法的含義 與 第1點描述的是一樣的

3、針對 列表中 只有一個 組的情況,可省略外層數組,只提供內層數組即可,如:

@[

? ? object1,

? ? object2,

? ? object3

].bind(@"className").bindHeight(100);

//其含義為:這個列表有 1個組,這個組有 3 個類名為 className 的cell,且高度為100

實踐證明,這種寫法極其便利,而且出現頻率極高,能滿足絕大部分傻瓜式滾動列表。

以上僅為 列表 cell 的數據組織結構,對于列表中 存在的 組頭組尾(UITableViewHeaderFooterView、UICollectionReusableView),其 數據組織 需要與 cell的數據組織 分開處理,便于理解 與 維護。


列表組頭組尾數據創建規則:

1、由 多個 單一 數據 構成的 數組對象,即代表 一個 完整列表 的組頭/組尾數據;

2、一個 數組 對象 代表 listView 的 一個組頭/組尾數據,組內的單一數據 代表 列表的 某一個 組頭/組尾;

3、規定 一個二維數組代表 一個listView 的所有組頭組尾數據,該二維數組 只包含兩個 子數組,超出部分將不作處理;其中,第一個 子數組 代表 組頭,第二個 子數組 代表組尾;

4、組頭組尾數據并不是 提供多少 就會創建多少,實際數量依賴于 cell 數據。即 有多少組 cell數據,才會相應地 去查找是否有提供的 組頭組尾數據;

以上 提及的為 列表組頭組尾數據?的創建原則;可參考本人的用法,只是數據的組織形式與cell數據的組織形式略有不同,綁定規則通用,即數組綁定會順帶綁定數組元素,如下:

1、一個 完整的 組頭組尾數據,其標準形式如下:

@[

? ? @[

? ? ? ? object1.bind(@"headerView").bindHeight(100),

? ? ? ? object2.bind(@"headerView").bindHeight(100),

? ? ? ? object3.bind(@"headerView").bindHeight(100),

? ? ],

? ? @[

? ? ? ? object4.bind(@"footerView").bindHeight(100),

? ? ? ? object5.bind(@"footerView").bindHeight(100),

? ? ? ? object6.bind(@"footerView").bindHeight(100),

? ? ]

];

//其含義為:有 3 個 組頭數據,創建出來的是 類名為?headerView 高度為 100 的組頭View;有 3 個 組尾數據,創建出來的是 類名為?footerView 高度為 100 的組尾View;

注意:實際是否生成 對應 該組頭組尾數據的View,還是需要有 cell數據的結構決定。

簡便寫法也是支持的,如:

@[

? ? @[object1, object2, object3].bind(@"headerView").bindHeight(100),

? ? @[object4, object5, object6].bind(@"footerView").bindHeight(100)

];

//含義同上

2、對于只需提供組頭數據的情況,可省略外層數組,只提供內層數組。如:

@[object1, object2, object3].bind(@"headerView").bindHeight(100);


寫法上的注意點:

1、如果是 以標準的 二維數組 組織數據,請不要將綁定直接添加至 最外層數組,因為 數組 綁定 時只會傳遞 一層 數組元素,而不會向下 傳遞至 數組元素的數組元素,如以下寫法 是錯誤的:

@[

? ? @[

? ? ? ? object1,

? ? ? ? object2,

? ? ? ? object3

? ? ]

].bind(@"className").bindHeight(100);

//錯誤寫法,這樣綁定只會將綁定傳遞至 內層數組,而想要綁定的?object1、object2、object3 實質上并沒有綁定。

正確的寫法為:

@[

? ? @[

? ? ? ? object1,

? ? ? ? object2,

? ? ? ? object3

? ? ].bind(@"className").bindHeight(100)

];

// 若存在多組,也是直接綁定內層數組,就能連帶 單一數據 的綁定;單組的情況下 可以直接以 一維數組 代替 二維數組;


2、如果存在 需要 一個 數據 運用到 多個 cell 的情況,請使用?mt_reuse(object) 對數據進行包裝,如以下寫法是錯誤的:

? ? @[

? ? ? ? object.bind(@"className1"),

? ? ? ? object.bind(@"className2"),

? ? ].bindHeight(100);

// 這樣綁定的話,后面的 className2 會把 前面的 className1 覆蓋掉,最后生成的為 2 個 className2 的 cell

正確的寫法為:

? ? @[

? ? ? ? mt_reuse(object).bind(@"className1"),

? ? ? ? mt_reuse(object).bind(@"className2"),

? ? ].bindHeight(100);

// 使用?mt_reuse() 包裝數據后,實質上是將綁定添加至這個 包裝之后的 對象 上,由于是不同的 包裝對象,所以綁定 能不受影響,傳遞數據 時 會將這個 重用對象 里的 object 取出 傳遞至對應的 cell


3、原則上 .bindSize 只會對 UICollectionViewCell 起作用,而 .bindHeight 則對UICollectionViewCell 和 UITableViewCell 都起作用。如以下寫法是不起作用的:

?????object.bind(@"UITableViewCell").bindSize(CGSizeMake(100, 100));

// 對一個?UITableViewCell 綁定了 size 是無效的;因為?UITableViewCell 不需要寬度

正確寫法如下:

?object.bind(@"UICollectionViewCell").bindSize(CGSizeMake(100, 100));

//或者

?object.bind(@"UITableViewCell").bindHeight(100);

object.bind(@"UICollectionViewCell").bindHeight(100);

//?UITableViewCell 只對 高度綁定 生效,UICollectionViewCell 則兩者都可,優先選擇 bindSize,其次再會 選擇 bindHeight;如果選擇的是 bindHeight, 則它的寬度 等于 父控件?UICollectionView 的寬度


寫法上的技巧:

1、綁定添加在數組上時,仍然可以為 單數據添加綁定,若如此做,數組上添加的綁定對于該單一數據將不起作用;這樣做的目的是能夠 根據實際情況 自行定制數據。

以下以單組數據舉例說明,多組寫法只是增加單組數量,在此不闡述。

示例1:

@[

? ? object1,

? ? object2.bind(@"className2")

].bind(@"className1").bindHeight(100);

// 為數組的 所有數據 綁定 100 的高度, 類名為 className1 的cell;其中, object2 的 cell 類名為 className2。

//或者

@[

? ? object1,

? ? object2..bindHeight(200)

].bind(@"className").bindHeight(100);

// 為數組的 所有數據綁定類名為 className 的cell,高度為 100;其中, object2 的高度為200。

2、對于變量表示的數組對象,可通過引用數組元素的方式自行定制。如下:

NSArray*array=@[object1, object2];

array.bind(@"className1").bindHeight(100);

array[1].bind(@"className2");

//或者

array.bind(@"className").bindHeight(100);

array[1].bindHeight(200);

這個寫法含義同上。

3、對于不需要傳入數據對象的情況,可以直接傳類名字符串,綁定尺寸。如下:

@"className".bindHeight(100);

@"className".bindSize(CGSizeMake(100,100));

//這個寫法 可省略?.bind(@"className")

4、對于只需要進行占位,而不需要傳入實質數據的情況,第 3 點已經可以滿足,但是對于不同尺寸的情況,前面的寫法注意已經提過,是會出現對于同一對象后面的綁定會覆蓋前面的情況,所以使用?mt_reuse(object) 進行表面的數據包裝,如果需要包裝的數據為空,可使用?mt_empty() 代替;即?mt_empty() ==?mt_reuse(nil)。這樣做是為了帶來寫法上的便利性。針對 第3點 的情況,對于不需要傳入實質數據,而又想生成不同大小的 cell,可以這樣寫:

mt_empty().bind(@"className").bindHeight(100);

mt_empty().bind(@"className").bindHeight(200);

mt_empty().bind(@"className").bindHeight(300);

//或者

mt_reuse(@"className").bindHeight(100);?

mt_reuse(@"className").bindHeight(200);

mt_reuse(@"className").bindHeight(300);


mt_empty().bind(@"className").bindSize(CGSizeMake(100,100));

mt_empty().bind(@"className").bindSize(CGSizeMake(200,200));

mt_empty().bind(@"className").bindSize(CGSizeMake(300,300));

//或者

mt_reuse(@"className").bindSize(CGSizeMake(100,100));

mt_reuse(@"className").bindSize(CGSizeMake(200,200));

mt_reuse(@"className").bindSize(CGSizeMake(300,300));

也可缺省綁定?className,代表生成一個默認的空cell。如下:

mt_empty().bindHeight(100);

mt_empty().bindHeight(200);

mt_empty().bindHeight(300);


mt_empty().bindSize(CGSizeMake(100,100))

mt_empty().bindSize(CGSizeMake(200,200))

mt_empty().bindSize(CGSizeMake(300,300))

5、對于 單一數據 要生成多個?相同列表 cell 的情況,可使用 .bindCount(n) 生成包含 n 個 包裝該 單一數據的數組,用于快速生成多數據;以下舉例說明 生成一個包含多個 使用?同一數據?樣式相同cell 列表在 使用?.bindCount(n) 與不使用的區別:

// 不使用

@[

? ? mt_reuse(object),

? ? mt_reuse(object),

? ? mt_reuse(object)

].bind(@"className").bindHeight(100);

//使用

object.bindCount(3).bind(@"className").bindHeight(100);

這個用法比較常用的場景為在開發時造假數據調試用;也可通過該方式造出數組對象,在根據要點2 提及的,自行定制數據。

基礎用法總結:

1、 .bind() 用于綁定類名,如果 單一數據 為字符串,則該字符串同時 包含類名綁定的含義,即:

@"className".bindHeight(100);

mt_empty().bind(@"className").bindHeight(100);

mt_reuse(@"className").bindHeight(100);

等價。只是寫法上不一樣,如果存在多個 cell 需要使用相同 的數據,要使用 ?mt_reuse() 包裝。

2、綁定尺寸 常用有兩種,bindHeight()bindSize();其中 UITableViewCell 只會 接收 bindHeight();

3、mt_reuse() 可用于對多個 cell 傳遞同一個數據的情況,避免 綁定被覆蓋;

4、mt_empty() 為?mt_reuse() 包裝的數據為空的情況,即?mt_empty() ==?mt_reuse(nil);多用于做占位cell使用;

5、bindCount(n) 可用于生成 一個包含 n 個包裝了相同數據 數組元素的數組對象;多用于造假數據;

6、bindSpacing() 用于綁定在組頭組尾數組上,代表 collectionView 幾個關鍵組屬性(最小行間距、最小列間距,組間距),用在 列表數據上是不起作用的。

7、二維數組 代表 一個列表數據,里面每一個一維數組代表列表的一個組,組內每一個元素代表列表中 這個組的每一個cell。如果 列表只有一個組,可以簡寫成一位數組的寫法,數組的所有數組元素則代表列表的所有cell。

8、二維數組 代表一個 組頭組尾數據,第一個子一維數組里存放組頭數據,第二個子一位數組存放組尾數據。如果只提供一個一維數組,默認代表一個組頭數據。

總之,用法都很靈活,組織思路簡單,多敲代碼試驗一下就能快速上手了!之后所有關于列表的交互或者樣式設置基本都是圍繞這一套機制實現。


在 cell、headerFooterView、reusableView?中接收傳遞的數據:

在編寫 這些列表單元格控件時,請繼承使用?MTDelegateCollectionViewCell、MTDelegateTableViewCell、MTDelegateCollectionReusableView 和?MTDelegateHeaderFooterView,這幾個基類并沒有做特殊操作,只是添加了一些 用于辨別 索引的子屬性,都是直接繼承 對應的 UIKit 控件,這樣做能避免直接對原生框架的 UIKit 控件作修改,避免加重原生框架負擔,同時便于框架內部維護。

以上 4個控件實現了一個很重要的協議:MTExchangeDataProtocol,協議如下:

@protocol MTExchangeDataProtocol

/**當接收到數據*/

-(void)whenGetResponseObject:(NSObject*)object;

/**接收數據的類型*/

-(Class)classOfResponseObject;

@optional

以下以 UICollectionViewCell 的自定義,解說如何從以上的便捷寫法中,獲取到這個 單一數據。

1、繼承?MTDelegateCollectionViewCell,新建類 DemoCell。如下:

@interface DemoCell : MTDelegateCollectionViewCell ?@end

2、由于基類已經實現了?MTExchangeDataProtocol 協議,這里只需重寫兩個協議方法即可。如下:

@implementation DemoCell

// 數據 最后會通過?whenGetResponseObject: 方法傳遞進來

-(void)whenGetResponseObject:(NSDictionary *)object

{

? ? NSLog(@"%@", object);

}

// 這個方法會判斷 數據是否為想要的類型,這里需要的數據類型為?NSDictionary,所以

whenGetResponseObject 方法接收到數據時可以放心使用, 保證了數據類型的準確性

-(Class)classOfResponseObject

{

? ? return [NSDictionary class];

}

@end

3、最后 組織后數據傳入即可,如下:

@{}.bind(@"DemoCell").bindHeight(100);

這樣就能把這個空字典 傳遞進?DemoCell 了。

這里只是舉例,實際上傳什么數據,按實際情況而定,默認基類?classOfResponseObject 沒有限制類型,可傳遞任意基類為NSObject的對象。

實際使用中,可以構造模型數據的字典對象,機制內部默認會將字典轉換為?classOfResponseObject 對應的模型對象。如 cell 需要一個 AModel 如下:

@interface AModel

@property (nonatomic, strong) NSString* a;

@property (nonatomic, strong) NSString* b;

@end

則我們在構造數據時,可以給定一個字典對象,如下:

@{

@"a" : @"a",?

@"b" : @"b"

}.bind(@"className")



listView 代理數據源設置:

先來 看看 listView 快速創建數據源的幾個方法:

1、添加代理數據源:

[self addTarget:delegate EmptyData:emptyData DataList: dataList SectionList:sectionList];

delegate:為一個遵循?UITableViewDelegate、UICollectionViewDelegateFlowLayout、MTDelegateProtocol 的代理對象;該代理對象 也會傳遞至 cell 的 mt_delegate 屬性中,用于處理 cell 傳遞過來的交互操作,通常是一個 ViewController;

MTDelegateProtocol 協議 如下:

@protocol MTDelegateProtocol <NSObject>

@optional

-(void)doSomeThingForMe:(id)obj withOrder:(NSString*)order;

-(id)getSomeThingForMe:(id)obj withOrder:(NSString*)order;

-(void)doSomeThingForMe:(id)obj withOrder:(NSString*)order withItem:(id)item;

-(id)getSomeThingForMe:(id)obj withOrder:(NSString*)order withItem:(id)item;

@end

代理對象通過實現這些可選方法 實現 cell 與 外界的數據交互,order 通常作為一個事件辨識,用于區分 交互事件,具體用途按自己情況而定,這個協議的主要目的就是用來做 交互的。

對于單純的 cell 點擊事件,完全可以在代理里實現?tableView:didSelectRowAtIndexPath: 或?

collectionView:didSelectItemAtIndexPath: 進行處理;同理,UIScrollView的代理方法也寫在代理中即可,這套機制內部會將這些代理方法帶出來到我們傳遞的這個代理對象中進行處理,只是將數據源方法包裝起來在內部實現,省卻我們重復編寫數據源代理方法的麻煩。

dataList:即是上面我提到的列表數據的構造,就是扔到這里使用的。

sectionList:同理,即是上面提到的組頭組尾數據構造,在這里做為參數傳遞。

emptyData:當上面的 dataList 單一數據數量為 0 時,會調用這個 單一數據創建空數據頁面,原則上可以不傳尺寸,內部會自動生成同 listView 同等寬高的一個cell。

上面所說的這個?addTarget:EmptyData:DataList:SectionList: 方法做了這幾個操作:

1、給定列表數據;

2、給定組頭組尾數據;

3、給定一個空數據;

4、為 listView 添加一個代理對象,用于作數據傳遞;

在實際使用情況中,用的比較多的通常是這個方法的一個便利方法:

- (void)addTarget:(id)target;

這個方法的作用同上,只是傳遞的?dataList、sectionList、emptyData 為空,而轉而通過listView 的刷新方法去進行傳遞。

2、listView 刷新數據源 方法如下:

- (void)reloadDataWithDataList:(NSArray*)dataList;

- (void)reloadDataWithDataList:(NSArray*)dataList EmptyData:(NSObject*)emptyData;

- (void)reloadDataWithDataList:(NSArray*)dataList SectionList:(NSArray*)sectionList;

- (void)reloadDataWithDataList:(NSArray*)dataList SectionList:(NSArray*)sectionList EmptyData:(NSObject*)emptyData;

以上刷新方法的參數含義同上,只是略有不同,缺省不傳的參數,缺省參數默認傳 nil;這些便利方法最終內部都會去調 最后一個方法:

- (void)reloadDataWithDataList:(NSArray*)dataList SectionList:(NSArray*)sectionList EmptyData:(NSObject*)emptyData;

刷新方法 與?addTarget 方法不同的地方在于:

1、交互操作的處理 通過在 addTarget 方法中指定 代理對象,而后由這個代理對象處理這些交互事件即可,所以沒必要放在 頻繁調用的?reloadData ?方法中;

2、刷新方法 默認回去調用 listView 的原生方法 reloadData,用于刷新列表,這個方法是作為刷新存在的。

兩個方法相同的地方在于:都留有傳遞 構造數據 的參數 用于創建 列表cell。

以下提供一個實際使用過程中,listView 搭建的常用套路:

1、首先你得有一個listView;

2、先綁定 target,如下:

[listView?addTarget: target];?

// 先不用急著提供數據源,因為列表 總會涉及到數據刷新,可考慮直接將數據源放到刷新方法去調用

3、提供數據源,刷新列表,如下:

[listView?reloadDataWithDataList: dataList SectionList: sectionList EmptyData: emptyData];

可根據項目的實際情況,調用對應的便利方法即可。


自適應高度的使用:

對于固定尺寸的 cell,用 bindHeight、bindSize 的確已經很方便了,但是對于一些不確定高度的情況,用起來總不那么順手,因為要先計算出高度,再傳入高度,而要計算高度,就需要知道這個 cell 內子控件怎么部署,每一個子控件的間隔、位置和高度,這樣無疑是增大工作量,只為計算一個高度,確實不劃算。于是,就有了自適應高度這樣一個東西。

先來看幾個新知識。

1、在基于這一套機制下的單元格 cell 中,遵循名為?MTInitProtocol 的協議,這個協議中有一個可選方法,如下:

/**反向計算父控件高度*/

-(CGSize)layoutSubviewsForWidth:(CGFloat)contentWidth Height:(CGFloat)contentHeight;

其中,contentWidth 和?contentHeight 為 listView 的 寬和高,這個方法是在數據源調用 計算尺寸的代理方法內部被調用,用于 計算 cell 的實時高度,返回值 CGSize 即為 cell 的尺寸。注意,對于 UITableViewCell,實際上 width 是無作用的,因為UIKit 并沒有提供 對于的 寬度設置代理方法。

通過這種方式,我們在編寫 cell 的時候,就可以在這個方法中 順帶將 cell 在實際展示時,根據子控件的情況而 展示的尺寸 求出來,而不再需要在外部再計算尺寸傳入,非常方便。同時,這個方法是可以用來布局子控件的,因為你的子控件布局完成后,基本上 這個 cell 整體寬高就出來了。

當然,使用自適應高度并不是自發的,需要在構造數據中去開啟:

2、在構造你的單一數據時,使用 .automaticDimension() 即代表啟用自適應高度。你不再需要使用 .bindHeight、.bindSize 去規定 cell 的尺寸,即使你使用了也不會起作用,從優先級來看為:.automaticDimension() >?.bindSize >?.bindHeight,使用規則同上說的列表數據規則。綁定了 .automaticDimension() 后,機制內部將不會去檢查?.bindHeight 或 .bindSize,而會直接去調用 cell 的?layoutSubviewsForWidth:Height: 方法,獲取我們已經在cell 內部計算出來的 尺寸。

示例如下:

//不使用自適應高度

object.bind(@"className").bindHeight(100);?

object.bind(@"className").bindSize(CGSizeMake(100, 100));?


//使用自適應高度

object.bind(@"className").automaticDimension();?

// cell內部

-(CGSize)layoutSubviewsForWidth:(CGFloat)contentWidth Height:(CGFloat)contentHeight

{

????return CGSizeMake(100, 100);

}


3、先來看一個block:

typedef void(^MTAutomaticDimensionSize)(CGSize size);

通過在構造數據時綁定 .automaticDimensionSize(MTAutomaticDimensionSize),可以在獲取到計算高度后,回調出去作處理。如:

object.bind(@"className").automaticDimension().automaticDimensionSize(^(CGSize size){

//..........

});

根據需要決定是否需要回調,實際開發過程中用處比較小。

自適應高度的目的,有三個好處:

1、解決了 不確定高度 帶來的繁瑣計算問題;

2、將 一個 cell 的樣式設置 和 尺寸設置 統一至 cell 內部去實現,而不再需要分到去外部使用代理方法去計算,一來麻煩,二來沒必要,尺寸本來就屬于cell 的部分,不需要拿出去讓外部決定,cell 自己清楚自己內部有什么子控件,這些子控件又是什么內容,完全可以自己確定高度;

3、布局子控件,以往 手動布局 都是 通過原生提供的?layoutSubviews?方法來布局子控件,現在,你可以放心地在這個計算尺寸的方法里布局子控件,繼而得出 cell 的尺寸,一舉兩得。然后再在?layoutSubviews 中,調用?layoutSubviewsForWidth:Height:?方法,完成子控件布局。如下:

-(void)layoutSubviews

{

? ? [super layoutSubviews];

? ? [self layoutSubviewsForWidth:self.width Height:self.height];

}

//雖然機制內部 會調用?layoutSubviewsForWidth: Height: 獲得尺寸,但實際上 布局子控件最后還是需要走?layoutSubviews 方法,所以需要在這里再調用一次,將 cell 的寬高傳入。

注意,layoutSubviewsForWidth: Height: 中請不要直接引用 使用 cell 的寬高,因為你正在計算 cell 的寬高 是無法獲取它的尺寸的,本質上只是將寫在?layoutSubviews 的布局邏輯 轉移到這個方法上,使得這個方法既能布局子控件,也能計算尺寸。

如果使用 本人封裝的 樣式設置框架,上面示例在?layoutSubviews 中調用計算高度這一步 是不需要的,在框架基類中已經實現,直接所有自定義cell 重寫?layoutSubviewsForWidth: Height: 即可。關與 樣式設置 與 這個列表快速搭建框架聯用,將在另一章中介紹,本章不作闡述。

總結來說,自適應高度的使用步驟為:

1、先在 自定義的 cell 中重寫?layoutSubviewsForWidth:Height:?方法,布局子控件的同時順帶計算高度;示例如下:

-(CGSize)layoutSubviewsForWidth:(CGFloat)contentWidthHeight:(CGFloat)contentHeight

{? ?

????self.label.x = 15;

????self.label.y = 20;

? ? [self.label sizeToFit];

? ? return? CGSizeMake(contentWidth, label.maxY +100);

}

2、在構造數據時,使用?.automaticDimension() 綁定單一數據,開啟自適應高度:

object.bind(@"className").automaticDimension();?

實際上,不使用自適應高度,也可以重寫?layoutSubviewsForWidth:Height: 方法,可以將子控件布局寫在里面,在?layoutSubviews 中調用即可,這樣無論使用?自適應高度 與否,完全取決于單一數據如何綁定,是使用?.automaticDimension() 還是使用.bindHeight、.bindSize。


點擊事件綁定:

除了通過?MTDelegateProtocol 代理模式進行數據交互外,還能通過構造數據時綁定 點擊事件回調的方式進行交互,這種方式使用更便捷。

先來看一個 block:

typedef void(^MTClick)(id _Nullable object);

可以在構造數據時,使用 .bindClick(MTClick) 綁定點擊回調:

data.bind(@"className").bindClick(^(id object) {

});

.bindClick 會為 數據 通過運行時屬性 mt_click 將回調保存下來,當觸發了didSelectRowAtIndexPath 或者?didSelectItemAtIndexPath,會將 indexPath 作為參數調起回調。

當然,如果 在代理中實現了?didSelectRowAtIndexPath 或?didSelectItemAtIndexPath 方法,也會觸發方法調用。

對于 cell 內子控件(比如按鈕) 觸發的點擊事件,需要自行調起 傳入數據的?mt_click(object) 來調起回調,可以通過 為傳入的?object ?使用 .bindOrder(NSString)?綁定字符串標識區分是何種事件。如在 cell 內由于觸發了按鈕點擊,調起回調如下:

if(self.mt_click)?self.mt_click(object.bindOrder(@"ClickEvent"));

要獲取綁定的標識,通過屬性 .mt_order?獲取。如:

data.bind(@"className").bindClick(^(id object) {

if([object.mt_order isEqualToString:@"ClickEvent"]) {//.........}

});

關于 .bindClick 的深度用法,在本人的 樣式設置架構 里得到充分利用,上面介紹的綁定標識步驟在框架中已有實現,不需要我們再去手動.bindOrder,只需去寫業務回調即可。詳情請閱讀我的 樣式設置架構文章,里面將這種列表搭建方式運用的淋漓盡致。


至此,關于 列表的快速搭建用法已介紹完畢,具體使用,還是需要多實踐才能體會到 這套架構的好處。

最后說兩句:

1、可查看我的庫源碼,里面除了這個框架,還有各種我在開發中經歷過的,最后封裝成庫的使用功能,之后我也會針對性的為這些功能寫文章,講解如何使用。

2、因為內容有點多,精力有限,實在不可能每樣東西都能兼顧, 深入理解底層原理能幫助你快速上手如何使用。

3、cocoaPod 暫時不會更新,原因 我還在想十字滾動的多重滾動情況怎么寫,我們常遇到的情況通常是一層,即上面是頁卡可以點擊加左右滑,也可一個列表上下滑的功能??赡艽嬖谶@個功能嵌套的情況,所以我打算完成這個功能再更新 cocoaPod。這套機制已經很穩定了,不經常改動,已穩定經歷 3 個 app的開發,可放心使用。

源碼地址:?https://github.com/alrjqow/MagicThought.git,

理解這套機制,可參考這些庫模塊內容:

?MTDelegateMode?關鍵文件:?

1、MTDataSource.m,這個文件主要講 數據源的實現都放在這里;

2、UIView+Delegate,通過這個分類文件,暴露調用方法;

Dependency?關鍵文件:?

NSObject+ReuseIdentifier,通過這個分類文件,實現單一數據的綁定功能;



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

推薦閱讀更多精彩內容

  • $HTML, HTTP,web綜合問題 1、前端需要注意哪些SEO 2、 的title和alt有什么區別 3、HT...
    Hebborn_hb閱讀 4,637評論 0 20
  • 概要 64學時 3.5學分 章節安排 電子商務網站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,257評論 0 3
  • (1)前端基礎面試題 一、HTML 面試題 1、html 語義化? 閱讀代碼時能根據標簽理解你的用意,便于閱讀 方...
    Coder__T閱讀 576評論 0 5
  • 久違的晴天,家長會。 家長大會開好到教室時,離放學已經沒多少時間了。班主任說已經安排了三個家長分享經驗。 放學鈴聲...
    飄雪兒5閱讀 7,539評論 16 22
  • 今天感恩節哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉變要...
    迷月閃星情閱讀 10,590評論 0 11