自定義UICollectionView布局實現(xiàn)首頁功能菜單制作

市面上大多數(shù)的APP首頁面都會有功能菜單,并且通常可以滑動分頁


APP首頁功能菜單

UICollectionView原生Flow Layout的問題

要制作這樣的功能菜單,首選的解決方案自然是UICollectionView。然而,使用UICollectionView原生的Flow Layout了讨,會有兩個問題:

  1. UICollectionView設(shè)置成水平方向滾動時,菜單項會變成上下排布,即:
    [1][3][5] [7]
    [2][4][6]
    而我們所期望的是(假設(shè)每頁顯示6個功能項):
    [1][2][3] [7]
    [4][5][6]
  2. 剩余的item如果不滿一頁前计,則不會單獨占用一頁胞谭。比如有7個item,每頁顯示6個男杈,那么水平滾動UICollectionView丈屹,最終邊界只會固定在第7個item,而不是把第7個item當(dāng)作第2頁的第1個item來顯示

可以看下圖中的效果伶棒,原生的Flow Layout如圖上半部分顯示旺垒,而我們期望的是圖示下半部顯示的效果:


原生和自定義的UICollectionView布局

常見的解決方法

對此,有些人會尋找第三方控件實現(xiàn)肤无,而有些人會轉(zhuǎn)而用UIScrollView自行實現(xiàn)先蒋。
使用第三方控件需要熟悉其用法,有時靈活性可能不能滿足要求(例如想自定義item外觀)宛渐,且有的實現(xiàn)是基于UIScrollView的鞭达,沒有像UICollectionView那樣有復(fù)用cell,這在效率上來說皇忿,不是很理想(但通常菜單項不會很多,也不會太要緊)坦仍。
用UIScrollView自行實現(xiàn)鳍烁,需自行計算item的排布,會比較煩瑣繁扎,另外和前面提到的一樣幔荒,效率會有影響。

自定義UICollectionView布局

其實我們可以自定義UICollectionView布局來解決這個問題梳玫,自定義布局也是最為簡單高效的方案爹梁。

這里只簡要說一下實現(xiàn)過程及部分示例代碼,不再贅述自定義布局相關(guān)的基礎(chǔ)知識(對這方面不是很清楚的同學(xué)提澎,請自行查閱相關(guān)文章)姚垃,具體的實現(xiàn)代碼也會在最后給出。

在實現(xiàn)之前盼忌,先要確定一下要實現(xiàn)的功能积糯。自定義布局可以做的很靈活,但靈活就意味著考慮的事情越多谦纱,代碼也會越復(fù)雜看成。秉承KISS原則,就只實現(xiàn)足夠使用的功能跨嘉。
我們假定川慌,每個item的大小是固定不變的,這有助于layout計算排列item的位置。

實現(xiàn)

首先梦重,先創(chuàng)建自定義布局類:

@interface KSTCollectionViewPageHorizontalLayout : UICollectionViewLayout

@property (nonatomic, assign) CGFloat lineSpacing;
@property (nonatomic, assign) CGFloat interitemSpacing;
@property (nonatomic, assign) CGSize itemSize;

@end

prepareLayout方法中做相應(yīng)的計算:

- (void)prepareLayout {
    // 根據(jù)設(shè)定的itemSize和collectionView的frame來確定有多少行兑燥、多少列
    // 計算可以放多少列
    NSInteger baseColumnCount = self.collectionView.frame.size.width / (self.itemSize.width + self.interitemSpacing);
    // 計算可以放多少行
    CGFloat contentHeight = self.collectionView.frame.size.height - self.sectionInsetTop;
    NSInteger baseLineCount = contentHeight / (self.itemSize.height + self.lineSpacing);
}

確定好行數(shù)、列數(shù)后忍饰,我們就可以在layoutAttributesForItemAtIndexPath中對item進(jìn)行相應(yīng)的排布:

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    // 根據(jù)行數(shù)贪嫂、列數(shù),計算每頁各個item的位置

    NSInteger pageItemCount = (self.calculatedLineCount * self.calculatedColumnCount);
    NSInteger currentPage = indexPath.row / pageItemCount;
    NSInteger currentLine = (indexPath.row - currentPage * pageItemCount) / self.calculatedColumnCount;
    NSInteger currentColumn = indexPath.row % self.calculatedColumnCount;
    layoutAttributes.frame = CGRectMake(currentPage * self.collectionView.frame.size.width + currentColumn * (self.itemSize.width + self.interitemSpacing), self.sectionInsetTop + currentLine * (self.itemSize.height + self.lineSpacing), self.itemSize.width, self.itemSize.height);

    return layoutAttributes;
}

最后艾蓝,因為我們希望剩余的item如果不夠一頁力崇,也能單獨占用一頁顯示,就需要重寫collectionViewContentSize方法返回適當(dāng)?shù)?code>contentSize:

- (CGSize)collectionViewContentSize {
    // 計算item所占的頁數(shù)赢织,如7個item亮靴,每頁6個,則應(yīng)為2頁
    // 根據(jù)頁數(shù)計算出適當(dāng)?shù)腸ontentSize

    NSInteger sectionItemCount = [self.collectionView numberOfItemsInSection:0];
    NSInteger pageItemCount = (self.calculatedLineCount * self.calculatedColumnCount);
    NSInteger pageCount = (sectionItemCount + pageItemCount - 1) / pageItemCount;
    return CGSizeMake(pageCount * self.collectionView.frame.size.width, 0);
}

完成自定義布局后于置,給CollectionView指定布局即可:

KSTCollectionViewPageHorizontalLayout *pageHorizontalLayout = [[KSTCollectionViewPageHorizontalLayout alloc] init];
pageHorizontalLayout.itemSize = CGSizeMake(SCREEN_WIDTH / 5, 83 * (SCREEN_WIDTH / 375.0));
self.menuCollectionView.pagingEnabled = YES;
self.menuCollectionView.collectionViewLayout = pageHorizontalLayout;

細(xì)節(jié)

  • 是否需要item間距及每行的間距
  • 對于每一行item茧吊,是左對齊進(jìn)行排布,還是分散對齊進(jìn)行排布
  • 是否需要像原生Flow Layout的sectionInset功能
  • ……

類似這些細(xì)節(jié)問題可以自行考量八毯,畢竟關(guān)注的細(xì)節(jié)越多搓侄,實現(xiàn)起來也就越復(fù)雜,還是建議視項目需要進(jìn)行取舍话速。

自定義布局的優(yōu)點

  • 學(xué)習(xí)成本低讶踪,只需要更換UICollectionView的布局即可,不需要像使用第三方控件那樣泊交,去熟悉第三方控件的屬性乳讥、API
  • 靈活性好,要顯示什么樣的item廓俭,都可以自定義cell實現(xiàn)
  • 效率高云石,最終還是使用UICollectionView,cell有良好的復(fù)用
  • 復(fù)用性好研乒,布局可以用于任意的UICollectionView

參考代碼

具體實現(xiàn)代碼請參考:KSTCollectionViewPageHorizontalLayout

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末汹忠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子雹熬,更是在濱河造成了極大的恐慌错维,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件橄唬,死亡現(xiàn)場離奇詭異赋焕,居然都是意外死亡,警方通過查閱死者的電腦和手機仰楚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門隆判,熙熙樓的掌柜王于貴愁眉苦臉地迎上來犬庇,“玉大人,你說我怎么就攤上這事侨嘀〕敉欤” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵咬腕,是天一觀的道長欢峰。 經(jīng)常有香客問我,道長涨共,這世上最難降的妖魔是什么纽帖? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮举反,結(jié)果婚禮上懊直,老公的妹妹穿的比我還像新娘。我一直安慰自己火鼻,他們只是感情好室囊,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著魁索,像睡著了一般融撞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粗蔚,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天尝偎,我揣著相機與錄音,去河邊找鬼支鸡。 笑死,一個胖子當(dāng)著我的面吹牛趁窃,可吹牛的內(nèi)容都是我干的牧挣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼醒陆,長吁一口氣:“原來是場噩夢啊……” “哼瀑构!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起刨摩,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤寺晌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后澡刹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呻征,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年罢浇,在試婚紗的時候發(fā)現(xiàn)自己被綠了陆赋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沐祷。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖攒岛,靈堂內(nèi)的尸體忽然破棺而出赖临,到底是詐尸還是另有隱情,我是刑警寧澤灾锯,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布兢榨,位于F島的核電站,受9級特大地震影響顺饮,放射性物質(zhì)發(fā)生泄漏吵聪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一领突、第九天 我趴在偏房一處隱蔽的房頂上張望暖璧。 院中可真熱鬧,春花似錦君旦、人聲如沸澎办。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽局蚀。三九已至,卻和暖如春恕稠,著一層夾襖步出監(jiān)牢的瞬間琅绅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工鹅巍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留千扶,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓骆捧,卻偏偏與公主長得像澎羞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子敛苇,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

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