UICollectionView的sectionHeader悬浮效果
collectionView沒(méi)有類似于tableView的Plain效果(即sectionHeader懸浮),不過(guò)可以通過(guò)自定義flowLayout實(shí)現(xiàn),因此它比tableView擁有更強(qiáng)大的擴(kuò)展性,這點(diǎn)是毋庸置疑的。
在項(xiàng)目中相關(guān)列表頁(yè)的實(shí)現(xiàn),我更傾向于使用UIColletionView,這個(gè)因人而異。
下面的代碼是我參考XLPlainFlowLayout轉(zhuǎn)換成Swift來(lái)的(就是copy+學(xué)習(xí)),在此感謝源代碼的分享。
####MTPlainFlowLayout.swift
import UIKitclass MTPlainFlowLayout: UICollectionViewFlowLayout {// 設(shè)置停留位置,默認(rèn)為64(沒(méi)有導(dǎo)航欄同樣設(shè)置有效)var naviHeight: CGFloat = 64override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {// UICollectionViewLayoutAttributes:稱它為collectionView中的item(包括cell和header、footer這些)的《結(jié)構(gòu)信息》var attributesArray: [UICollectionViewLayoutAttributes] = []if let superAttributesArray = super.layoutAttributesForElementsInRect(rect) {attributesArray = superAttributesArray}// 創(chuàng)建存索引的數(shù)組,無(wú)符號(hào)(正整數(shù)),無(wú)序(不能通過(guò)下標(biāo)取值),不可重復(fù)(重復(fù)的話會(huì)自動(dòng)過(guò)濾)let noneHeaderSections = NSMutableIndexSet()// 遍歷superArray,得到一個(gè)當(dāng)前屏幕中所有的section數(shù)組for attributes in attributesArray {// 如果當(dāng)前的元素分類是一個(gè)cell,將cell所在的分區(qū)section加入數(shù)組,重復(fù)的話會(huì)自動(dòng)過(guò)濾if attributes.representedElementCategory == .Cell {noneHeaderSections.addIndex(attributes.indexPath.section)}}// 遍歷superArray,將當(dāng)前屏幕中擁有的header的section從數(shù)組中移除,得到一個(gè)當(dāng)前屏幕中沒(méi)有header的section數(shù)組// 正常情況下,隨著手指往上移,header脫離屏幕會(huì)被系統(tǒng)回收而cell尚在,也會(huì)觸發(fā)該方法for attributes in attributesArray {if let kind = attributes.representedElementKind {//如果當(dāng)前的元素是一個(gè)header,將header所在的section從數(shù)組中移除if kind == UICollectionElementKindSectionHeader {noneHeaderSections.removeIndex(attributes.indexPath.section)}}}// 遍歷當(dāng)前屏幕中沒(méi)有header的section數(shù)組noneHeaderSections.enumerateIndexesUsingBlock { (index, stop) in// 取到當(dāng)前section中第一個(gè)item的indexPathlet indexPath = NSIndexPath(forRow: 0, inSection: index)// 獲取當(dāng)前section在正常情況下已經(jīng)離開(kāi)屏幕的header結(jié)構(gòu)信息if let attributes = self.layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: indexPath) {// 如果當(dāng)前分區(qū)確實(shí)有因?yàn)殡x開(kāi)屏幕而被系統(tǒng)回收的header,將該header結(jié)構(gòu)信息重新加入到superArray中去attributesArray.append(attributes)}}// 遍歷superArray,改變header結(jié)構(gòu)信息中的參數(shù),使它可以在當(dāng)前section還沒(méi)完全離開(kāi)屏幕的時(shí)候一直顯示for attributes in attributesArray {if attributes.representedElementKind == UICollectionElementKindSectionHeader {let section = attributes.indexPath.sectionlet firstItemIndexPath = NSIndexPath(forRow: 0, inSection: section)var numberOfItemsInSection = 0// 得到當(dāng)前header所在分區(qū)的cell的數(shù)量if let number = self.collectionView?.numberOfItemsInSection(section) {numberOfItemsInSection = number}// 得到最后一個(gè)item的indexPathlet lastItemIndexPath = NSIndexPath(forRow: max(0, numberOfItemsInSection-1), inSection: section)// 得到第一個(gè)item和最后一個(gè)item的結(jié)構(gòu)信息let firstItemAttributes: UICollectionViewLayoutAttributes!let lastItemAttributes: UICollectionViewLayoutAttributes!if numberOfItemsInSection > 0 {// cell有值,則獲取第一個(gè)cell和最后一個(gè)cell的結(jié)構(gòu)信息firstItemAttributes = self.layoutAttributesForItemAtIndexPath(firstItemIndexPath)lastItemAttributes = self.layoutAttributesForItemAtIndexPath(lastItemIndexPath)} else {// cell沒(méi)值,就新建一個(gè)UICollectionViewLayoutAttributesfirstItemAttributes = UICollectionViewLayoutAttributes()// 然后模擬出在當(dāng)前分區(qū)中的唯一一個(gè)cell,cell在header的下面,高度為0,還與header隔著可能存在的sectionInset的toplet itemY = attributes.frame.maxY + self.sectionInset.topfirstItemAttributes.frame = CGRect(x: 0, y: itemY, width: 0, height: 0)// 因?yàn)橹挥幸粋€(gè)cell,所以最后一個(gè)cell等于第一個(gè)celllastItemAttributes = firstItemAttributes}// 獲取當(dāng)前header的framevar rect = attributes.frame// 當(dāng)前的滑動(dòng)距離 + 因?yàn)閷?dǎo)航欄產(chǎn)生的偏移量,默認(rèn)為64(如果app需求不同,需自己設(shè)置)var offset_Y: CGFloat = 0if let y = self.collectionView?.contentOffset.y {offset_Y = y}offset_Y = offset_Y + naviHeight// 第一個(gè)cell的y值 - 當(dāng)前header的高度 - 可能存在的sectionInset的toplet headerY = firstItemAttributes.frame.origin.y - rect.size.height - self.sectionInset.top// 哪個(gè)大取哪個(gè),保證header懸停// 針對(duì)當(dāng)前header基本上都是offset更加大,針對(duì)下一個(gè)header則會(huì)是headerY大,各自處理let maxY = max(offset_Y, headerY)// 最后一個(gè)cell的y值 + 最后一個(gè)cell的高度 + 可能存在的sectionInset的bottom - 當(dāng)前header的高度// 當(dāng)當(dāng)前section的footer或者下一個(gè)section的header接觸到當(dāng)前header的底部,計(jì)算出的headerMissingY即為有效值let headerMissingY = lastItemAttributes.frame.maxY + self.sectionInset.bottom - rect.size.height// 給rect的y賦新值,因?yàn)樵谧詈笙У呐R界點(diǎn)要跟誰(shuí)消失,所以取小rect.origin.y = min(maxY, headerMissingY)// 給header的結(jié)構(gòu)信息的frame重新賦值attributes.frame = rect// 如果按照正常情況下,header離開(kāi)屏幕被系統(tǒng)回收,而header的層次關(guān)系又與cell相等,如果不去理會(huì),會(huì)出現(xiàn)cell在header上面的情況// 通過(guò)打印可以知道cell的層次關(guān)系z(mì)Index數(shù)值為0,我們可以將header的zIndex設(shè)置成1,如果不放心,也可以將它設(shè)置成非常大attributes.zIndex = 1024}}return attributesArray}override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {return true}} 復(fù)制代碼注意: 1.上述自定義flowLayout里存在一個(gè)bug,代碼里使用的self.sectionInset是初始化UICollectionViewFlowLayout設(shè)置的sectionInset,而不會(huì)隨著代理方法:func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets返回的sectionInset去更新; 2.如果初始化UICollectionViewFlowLayout并且只有一組section的時(shí)候設(shè)置sectionInset,是沒(méi)有上述問(wèn)題的; 3.如果有多組section,同時(shí)需要?jiǎng)討B(tài)設(shè)置每組的sectionInset的話,那么就會(huì)sectionHeader與預(yù)期效果就會(huì)有一定的偏差,這個(gè)偏差就是因?yàn)樯鲜?的bug所造成的。 4.OC版本也有同樣上述問(wèn)題
···
解決辦法: 1.如果只有一組section的話可以選擇在初始化flowLayout的時(shí)候設(shè)置sectionInset; 2.多組section的話就不建議使用上述1的方式,而是選擇 UICollectionViewDelegateFlowLayout里的代理方法func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets去動(dòng)態(tài)設(shè)置當(dāng)前section的sectionInset,這樣上面的bug就可以避免了; 3.以上兩個(gè)方法大家因情況而選擇,如果大家有更好的解決方案或者自定義flowLayout請(qǐng)留言或在本文底部評(píng)論; 4.OC版同樣可使用上面的解決方案。
####如何使用 上面貼出來(lái)的代碼就是完整的Swift文件,直接使用 MTPlainFlowLayout創(chuàng)建UICollectionViewFlowLayout對(duì)象使用即可。
圖片示例:
轉(zhuǎn)載于:https://juejin.im/post/5a3321976fb9a045204c419f
總結(jié)
以上是生活随笔為你收集整理的UICollectionView的sectionHeader悬浮效果的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: 数据库备份DBS提供异常任务自助修复
- 下一篇: Android 应用内微信 H5 支付
