代碼實(shí)現(xiàn)CollectionView的headerView的懸停
// 新建一個(gè)繼承UICollectionViewFlowLayout的類,重寫layoutAttributesForElementsInRect這個(gè)方法,初始化collectionView的時(shí)候,用此類進(jìn)行初始化即可。
- (instancetype)init{ self = [superinit]; if (self) { } returnself;}// 這方法里面的偏量是取決你的collectionView是橫向滑動(dòng)還是縱向滑動(dòng),我這是采取的橫向滑動(dòng),如果你要縱向華東,需要把偏移指標(biāo)取反,left-top y-x等。- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ //UICollectionViewLayoutAttributes:我稱它為collectionView中的item(包括cell和header、footer這些)的《結(jié)構(gòu)信息》 //截取到父類所返回的數(shù)組(里面放的是當(dāng)前屏幕所能展示的item的結(jié)構(gòu)信息),并轉(zhuǎn)化成不可變數(shù)組 NSMutableArray *superArray = [[superlayoutAttributesForElementsInRect:rect]mutableCopy]; //創(chuàng)建存索引的數(shù)組,無符號(hào)(正整數(shù)),無序(不能通過下標(biāo)取值),不可重復(fù)(重復(fù)的話會(huì)自動(dòng)過濾) NSMutableIndexSet *noneHeaderSections = [NSMutableIndexSetindexSet]; //遍歷superArray,得到一個(gè)當(dāng)前屏幕中所有的section數(shù)組 for (UICollectionViewLayoutAttributes *attributes in superArray) { //如果當(dāng)前的元素分類是一個(gè)cell,將cell所在的分區(qū)section加入數(shù)組,重復(fù)的話會(huì)自動(dòng)過濾 if (attributes.rePResentedElementCategory == UICollectionElementCategoryCell) { [noneHeaderSections addIndex:attributes.indexPath.section]; } } //遍歷superArray,將當(dāng)前屏幕中擁有的header的section從數(shù)組中移除,得到一個(gè)當(dāng)前屏幕中沒有header的section數(shù)組 //正常情況下,隨著手指往上移,header脫離屏幕會(huì)被系統(tǒng)回收而cell尚在,也會(huì)觸發(fā)該方法 for (UICollectionViewLayoutAttributes *attributes in superArray) { //如果當(dāng)前的元素是一個(gè)header,將header所在的section從數(shù)組中移除 if ([attributes.representedElementKindisEqualToString:UICollectionElementKindSectionHeader]) { [noneHeaderSections removeIndex:attributes.indexPath.section]; } } //遍歷當(dāng)前屏幕中沒有header的section數(shù)組 [noneHeaderSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){ //取到當(dāng)前section中第一個(gè)item的indexPath NSIndexPath *indexPath = [NSIndexPathindexPathForItem:0inSection:idx]; //獲取當(dāng)前section在正常情況下已經(jīng)離開屏幕的header結(jié)構(gòu)信息 UICollectionViewLayoutAttributes *attributes = [selflayoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeaderatIndexPath:indexPath]; //如果當(dāng)前分區(qū)確實(shí)有因?yàn)殡x開屏幕而被系統(tǒng)回收的header if (attributes) { //將該header結(jié)構(gòu)信息重新加入到superArray中去 [superArray addObject:attributes]; } }]; //遍歷superArray,改變header結(jié)構(gòu)信息中的參數(shù),使它可以在當(dāng)前section還沒完全離開屏幕的時(shí)候一直顯示 for (UICollectionViewLayoutAttributes *attributes in superArray) { //如果當(dāng)前item是header if ([attributes.representedElementKindisEqualToString:UICollectionElementKindSectionHeader]) { //得到當(dāng)前header所在分區(qū)的cell的數(shù)量 NSInteger numberOfItemsInSection = [self.collectionViewnumberOfItemsInSection:attributes.indexPath.section]; //得到第一個(gè)item的indexPath NSIndexPath *firstItemIndexPath = [NSIndexPathindexPathForItem:0inSection:attributes.indexPath.section]; //得到最后一個(gè)item的indexPath NSIndexPath *lastItemIndexPath = [NSIndexPathindexPathForItem:MAX(0, numberOfItemsInSection-1)inSection:attributes.indexPath.section]; //得到第一個(gè)item和最后一個(gè)item的結(jié)構(gòu)信息 UICollectionViewLayoutAttributes *firstItemAttributes, *lastItemAttributes; if (numberOfItemsInSection>0) { //cell有值,則獲取第一個(gè)cell和最后一個(gè)cell的結(jié)構(gòu)信息 firstItemAttributes = [selflayoutAttributesForItemAtIndexPath:firstItemIndexPath]; lastItemAttributes = [selflayoutAttributesForItemAtIndexPath:lastItemIndexPath]; }else { //cell沒值,就新建一個(gè)UICollectionViewLayoutAttributes firstItemAttributes = [UICollectionViewLayoutAttributesnew]; //然后模擬出在當(dāng)前分區(qū)中的唯一一個(gè)cell,cell在header的下面,高度為0,還與header隔著可能存在的sectionInset的top CGFloat x =CGRectGetMaxX(attributes.frame)+self.sectionInset.left; firstItemAttributes.frame =CGRectMake(0, x, 0,0); //因?yàn)橹挥幸粋€(gè)cell,所以最后一個(gè)cell等于第一個(gè)cell lastItemAttributes = firstItemAttributes; } //獲取當(dāng)前header的frame CGRect rect = attributes.frame; //當(dāng)前的滑動(dòng)距離 + 因?yàn)閷?dǎo)航欄產(chǎn)生的偏移量,默認(rèn)為64(如果app需求不同,需自己設(shè)置) CGFloat offset =self.collectionView.contentOffset.x; //第一個(gè)cell的y值 - 當(dāng)前header的高度 - 可能存在的sectionInset的top CGFloat headerX = firstItemAttributes.frame.origin.x - rect.size.width - self.sectionInset.left; //哪個(gè)大取哪個(gè),保證header懸停 //針對(duì)當(dāng)前header基本上都是offset更加大,針對(duì)下一個(gè)header則會(huì)是headerY大,各自處理 CGFloat maxX =MAX(offset,headerX); //最后一個(gè)cell的y值 + 最后一個(gè)cell的高度 + 可能存在的sectionInset的bottom - 當(dāng)前header的高度 //當(dāng)當(dāng)前section的footer或者下一個(gè)section的header接觸到當(dāng)前header的底部,計(jì)算出的headerMissingY即為有效值 CGFloat headerMissingX =CGRectGetMaxX(lastItemAttributes.frame) + self.sectionInset.right - rect.size.width; //給rect的y賦新值,因?yàn)樵谧詈笙У呐R界點(diǎn)要跟誰消失,所以取小 rect.origin.x = MIN(maxX,headerMissingX); //給header的結(jié)構(gòu)信息的frame重新賦值 attributes.frame = rect; //如果按照正常情況下,header離開屏幕被系統(tǒng)回收,而header的層次關(guān)系又與cell相等,如果不去理會(huì),會(huì)出現(xiàn)cell在header上面的情況 //通過打印可以知道cell的層次關(guān)系z(mì)Index數(shù)值為0,我們可以將header的zIndex設(shè)置成1,如果不放心,也可以將它設(shè)置成非常大,這里隨便填了個(gè)7 attributes.zIndex =7; } } //轉(zhuǎn)換回不可變數(shù)組,并返回 return [superArraycopy]; }//return YES;表示一旦滑動(dòng)就實(shí)時(shí)調(diào)用上面這個(gè)layoutAttributesForElementsInRect:方法- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound{ returnYES;}新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注