IOS封裝自定義布局的方法
一、概述
1、對(duì)于經(jīng)常使用的控件或類,通常將其分裝為一個(gè)單獨(dú)的類來供外界使用,以此達(dá)到事半功倍的效果
2、由于分裝的類不依賴于其他的類,所以若要使用該類,可直接將該類拖進(jìn)項(xiàng)目文件即可
3、在進(jìn)行分裝的時(shí)候,通常需要用到代理設(shè)計(jì)模式
二、代理設(shè)計(jì)模式
1、代理設(shè)計(jì)模式的組成
客戶類(通常作為代理):通常委托這是角色來完成業(yè)務(wù)邏輯
真實(shí)角色:將客戶類的業(yè)務(wù)邏輯轉(zhuǎn)化為方法列表,即代理協(xié)議
代理協(xié)議:
- 定義了需要實(shí)現(xiàn)的業(yè)務(wù)邏輯
- 定義了一組方法列表,包括必須實(shí)現(xiàn)的方法或選擇實(shí)現(xiàn)的方法
- 代理協(xié)議是代理對(duì)象所要遵循一組規(guī)則
代理角色
- 若要作為代理,需要遵守代理協(xié)議,并且實(shí)現(xiàn)必須實(shí)現(xiàn)的代理方法
- 代理角色可以通過調(diào)用代理協(xié)議中的方法完成業(yè)務(wù)邏輯,也可以附加自己的操作
文字描述通常是抽象的,一下通過圖示來闡述代理設(shè)計(jì)模式
三、自定義布局類的封裝
1、業(yè)務(wù)邏輯
如圖
2、布局每個(gè)cell的業(yè)務(wù)邏輯
由于設(shè)置每個(gè)cell的布局屬性的業(yè)務(wù)邏輯較復(fù)雜,特附上如下思維導(dǎo)圖
3、封裝思路封裝需要根據(jù)客戶類業(yè)務(wù)邏輯需求來提供接口
1)、通過代理協(xié)議的可選實(shí)現(xiàn)的方法獲取的屬性值的屬性,需要設(shè)置默認(rèn)值
2)、未提供默認(rèn)值的且必須使用的屬性,需要通過必須實(shí)現(xiàn)的方法來獲得
3)、自定義布局提供的接口可選
- 列數(shù)
- 列之間的間距
- 行之間的間距
- 內(nèi)邊距
4)、自定義布局提供的接口必選
每個(gè)元素的高度,寬度可以通過列數(shù)和列間距計(jì)算得到
四、封裝步驟
設(shè)置代理協(xié)議,提供接口
//聲明LYPWaterFlowLayout為一個(gè)類 @class LYPWaterFlowLayout; @protocol LYPWaterFlowLayoutDelegate <NSObject> //必須實(shí)現(xiàn)的方法 @required /**獲取瀑布流每個(gè)元素的高度*/ - (CGFloat)waterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout heightForItemAtIndex:(NSInteger)index itemWith:(CGFloat)itemWith; //可選實(shí)現(xiàn)的方法 @optional /**獲取瀑布流的列數(shù)*/ - (NSInteger)columnCountInWaterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout; /**獲取瀑布流列間距*/ - (CGFloat)columnMarginInWaterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout; /**獲取瀑布流的行間距*/ - (CGFloat)rowMarginInWaterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout; /**獲取瀑布流的內(nèi)邊距*/ - (UIEdgeInsets)edgeInsetsInWaterFlowLayout:(LYPWaterFlowLayout *)waterFlowLayout; @end
設(shè)置代理屬性
@interface LYPWaterFlowLayout : UICollectionViewLayout /**代理*/ @property (nonatomic, weak) id<LYPWaterFlowLayoutDelegate> delegate; @end
設(shè)置通過可選代理方法獲取屬性值的屬性的默認(rèn)值
/**默認(rèn)的列數(shù)*/ static const NSInteger LYPDefaultColumnCount = 3; /**默認(rèn)每一列之間的間距*/ static const CGFloat LYPDefaultColumMargin = 10; /**默認(rèn)每一行之間的間距*/ static const CGFloat LYPDefaultRowMargin = 10; /**默認(rèn)邊緣間距*/ static const UIEdgeInsets LYPDefaultEdgeInsets = {10, 10, 10, 10};
設(shè)置通過可選代理方法獲取屬性值的屬性的訪問方式若代理提供屬性值,則忽略默認(rèn)值
- (NSInteger)columnCount { //判斷代理是否實(shí)現(xiàn)了獲取列數(shù)的可選方法 if ([self.delegate respondsToSelector:@selector(columnCountInWaterFlowLayout:)]) { //實(shí)現(xiàn),返回通過代理設(shè)置的列數(shù) return [self.delegate columnCountInWaterFlowLayout:self]; } else { //為實(shí)現(xiàn),返回默認(rèn)的列數(shù) return LYPDefaultColumnCount; } }
注:其他屬性值的獲取與上述方法幾乎完全相同,不再贅述
設(shè)置布局
1)、設(shè)置需要的成員屬性
/**所有cell的布局屬性*/ @property (nonatomic, strong) NSMutableArray *attrsArray; /**所有列的當(dāng)前高度*/ @property (nonatomic, strong) NSMutableArray *columnHeights;
2)、通過懶加載的方式初始化成員屬性
/**--attrsArray--懶加載*/ - (NSMutableArray *)attrsArray { if (_attrsArray == nil) { _attrsArray = [NSMutableArray array]; } return _attrsArray; } /**--columnHeights--懶加載*/ - (NSMutableArray *)columnHeights { if (_columnHeights == nil) { _columnHeights = [NSMutableArray array]; } return _columnHeights; }
3)、初始化布局
- (void)prepareLayout { [super prepareLayout]; /**清除之前跟布局相關(guān)的所有屬性,重新設(shè)置新的布局*/ //清除之前計(jì)算的所有列的高度 [self.columnHeights removeAllObjects]; //設(shè)置所有列的初始高度 for (NSInteger i = 0; i<self.columnCount; i++) { self.columnHeights[i] = @(self.edgeInsets.top); } //清除之前所有的布局屬性 [self.attrsArray removeAllObjects]; /**開始創(chuàng)建每一個(gè)cell對(duì)應(yīng)的布局屬性*/ NSInteger count = [self.collectionView numberOfItemsInSection:0]; for (NSInteger i = 0; i<count; i++) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; //獲取indexPath位置cell對(duì)應(yīng)的布局屬性 UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath]; //將indexPath位置的cell的布局屬性添加到所有cell的布局屬性數(shù)組中 [self.attrsArray addObject:attrs]; } }
4)、返回包含所有cell的布局屬性的數(shù)組
- (nullable NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { return self.attrsArray; } 設(shè)置每一個(gè)cell的布局屬性 - (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(nonnull NSIndexPath *)indexPath { //獲取indexPath位置的布局屬性 UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; /**設(shè)置cell布局屬性的frame*/ /***確定cell的尺寸***/ //獲取collectionView的寬度 CGFloat collectionViewWidth = self.collectionView.frame.size.width; //cell寬度 CGFloat width = ((collectionViewWidth - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columMargin)) / self.columnCount; //cell高度 CGFloat height = [self.delegate waterFlowLayout:self heightForItemAtIndex:indexPath.item itemWith:width]; /***設(shè)置cell的位置***/ NSInteger destColumn = 0; CGFloat minColumnHeight = [self.columnHeights[0] doubleValue]; for (NSInteger i = 1; i<self.columnCount; i++) { CGFloat columnHeight = [self.columnHeights[i] doubleValue]; if (minColumnHeight > columnHeight) { minColumnHeight = columnHeight; destColumn = i; } } //計(jì)算cell的位置 CGFloat x = self.edgeInsets.left + destColumn * (width + self.columMargin); CGFloat y = minColumnHeight; //判斷是不是第一行 if (y != self.edgeInsets.top) { //若不是第一行,需要加上行間距 y += self.rowMargin; } /**給cell的布局屬性的frame賦值*/ attrs.frame = CGRectMake(x, y, width, height); //更新最短那列的高度 self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame)); /**返回indexPath位置的cell的布局屬性*/ return attrs; }
5)、設(shè)置collectionView內(nèi)容的尺寸
- (CGSize)collectionViewContentSize { //獲取最高的那一列的高度 CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue]; for (NSInteger i = 1; i<self.columnCount; i++) { CGFloat columnHeight = [self.columnHeights[i] doubleValue]; if (maxColumnHeight < columnHeight) { maxColumnHeight = columnHeight; } } //返回collectionView的contentSize,高度為最高的高度加上一個(gè)行間距 return CGSizeMake(0, maxColumnHeight + self.rowMargin); }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
- 詳解iOS中UIView的layoutSubviews子視圖布局方法使用
- iOS App開發(fā)中擴(kuò)展RCLabel組件進(jìn)行基于HTML的文本布局
- iOS組件封裝與自動(dòng)布局自定義表情鍵盤
- 詳解iOS應(yīng)用使用Storyboard布局時(shí)的IBOutlet與IBAction
- iOS App開發(fā)中Masonry布局框架的基本用法解析
- 深入解析iOS應(yīng)用開發(fā)中九宮格視圖布局的相關(guān)計(jì)算方法
- iOS模仿電子書首頁(yè)實(shí)現(xiàn)書架布局樣式
- IOS實(shí)現(xiàn)自定義布局瀑布流
- iOS開發(fā)之手動(dòng)布局子視圖
相關(guān)文章
iOS使用CoreMotion實(shí)現(xiàn)搖一搖功能
這篇文章主要為大家詳細(xì)介紹了iOS使用CoreMotion實(shí)現(xiàn)搖一搖功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06ios實(shí)現(xiàn)搜索關(guān)鍵字高亮效果
這篇文章主要介紹了ios實(shí)現(xiàn)搜索關(guān)鍵字高亮效果的方法以及實(shí)例代碼分享,有需要的朋友參考學(xué)習(xí)下。2018-02-02iOS App項(xiàng)目中引入SQLite數(shù)據(jù)庫(kù)的教程
SQLite是一個(gè)極輕的嵌入式數(shù)據(jù)庫(kù),在應(yīng)用程序中捆綁使用可以更方便地幫助操控關(guān)系型數(shù)據(jù),這里我們就來看一下iOS App項(xiàng)目中引入SQLite數(shù)據(jù)庫(kù)的教程2016-06-06iOS10添加本地推送(Local Notification)實(shí)例
這篇文章主要為大家詳細(xì)介紹了iOS10添加本地推送(Local Notification)實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09ios 實(shí)現(xiàn)倒計(jì)時(shí)的兩種方式
這篇文章主要介紹了ios實(shí)現(xiàn)倒計(jì)時(shí)的兩種方式,第一種方式使用NSTimer來實(shí)現(xiàn),第二種方式使用GCD來實(shí)現(xiàn)。具體內(nèi)容詳情大家參考下本文2017-01-01iOS在頁(yè)面銷毀時(shí)如何優(yōu)雅的cancel網(wǎng)絡(luò)請(qǐng)求詳解
這篇文章主要給大家介紹了關(guān)于iOS在頁(yè)面銷毀時(shí)如何優(yōu)雅的cancel網(wǎng)絡(luò)請(qǐng)求的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05iOS實(shí)現(xiàn)動(dòng)態(tài)自適應(yīng)標(biāo)簽
這篇文章主要為大家詳細(xì)介紹了iOS動(dòng)態(tài)自適應(yīng)標(biāo)簽的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03iOS實(shí)現(xiàn)步驟進(jìn)度條功能實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于iOS實(shí)現(xiàn)步驟進(jìn)度條功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11