【iOS】UICollectionView區(qū)頭停留

UITableView系統(tǒng)提供了兩種常用樣式(UITableViewStylePlainUITableViewStyleGrouped),UITableViewStylePlain可以讓區(qū)頭停留在頂部,但是UICollectionView沒有直接提供這樣的樣式苍糠,如果需要我們只能重寫UICollectionViewFlowLayout,直接上代碼:

//
//  UICollectionPlainFlowLayout.h
//  FlowLayoutDemo
//
//  Created by IDBENY on 2017/7/19.
//  Copyright ? 2017年 www.idbeny.com All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ShoppingCollectionPlainFlowLayout : UICollectionViewFlowLayout

@property (nonatomic, assign) CGFloat navHeight;//默認為64.0, default is 64.0

@end
//
//  UICollectionPlainFlowLayout.m
//  FlowLayoutDemo
//
//  Created by IDBENY on 2017/7/19.
//  Copyright ? 2017年 www.idbeny.com All rights reserved.
//

#import "ShoppingCollectionPlainFlowLayout.h"

@implementation ShoppingCollectionPlainFlowLayout

- (instancetype)init {
    self = [super init];
    if (self) {
        _navHeight = 64.0;
    }
    return self;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    //UICollectionViewLayoutAttributes:我稱它為collectionView中的item(包括cell和header辰企、footer這些)的《結構信息》
    //截取到父類所返回的數(shù)組(里面放的是當前屏幕所能展示的item的結構信息)宠能,并轉化成不可變數(shù)組
    NSMutableArray *superArray = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
    
    //創(chuàng)建存索引的數(shù)組夸楣,無符號(正整數(shù))椰苟,無序(不能通過下標取值)锅知,不可重復(重復的話會自動過濾)
    NSMutableIndexSet *noneHeaderSections = [NSMutableIndexSet indexSet];
    //遍歷superArray氯材,得到一個當前屏幕中所有的section數(shù)組
    for (UICollectionViewLayoutAttributes *attributes in superArray) {
        //如果當前的元素分類是一個cell站绪,將cell所在的分區(qū)section加入數(shù)組牡昆,重復的話會自動過濾
        if (attributes.representedElementCategory == UICollectionElementCategoryCell) {
            [noneHeaderSections addIndex:attributes.indexPath.section];
        }
    }
    
    //遍歷superArray爱致,將當前屏幕中擁有的header的section從數(shù)組中移除愤估,得到一個當前屏幕中沒有header的section數(shù)組
    //正常情況下畅哑,隨著手指往上移旭绒,header脫離屏幕會被系統(tǒng)回收而cell尚在仇轻,也會觸發(fā)該方法
    for (UICollectionViewLayoutAttributes *attributes in superArray) {
        //如果當前的元素是一個header京痢,將header所在的section從數(shù)組中移除
        if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
            [noneHeaderSections removeIndex:attributes.indexPath.section];
        }
    }
    
    //遍歷當前屏幕中沒有header的section數(shù)組
    [noneHeaderSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
        
        //取到當前section中第一個item的indexPath
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
        //獲取當前section在正常情況下已經離開屏幕的header結構信息
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
        
        //如果當前分區(qū)確實有因為離開屏幕而被系統(tǒng)回收的header
        if (attributes) {
            //將該header結構信息重新加入到superArray中去
            [superArray addObject:attributes];
        }
    }];
    
    //遍歷superArray,改變header結構信息中的參數(shù)篷店,使它可以在當前section還沒完全離開屏幕的時候一直顯示
    for (UICollectionViewLayoutAttributes *attributes in superArray) {
        
        //如果當前item是header
        if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
            //得到當前header所在分區(qū)的cell的數(shù)量
            NSInteger numberOfItemsInSection = [self.collectionView numberOfItemsInSection:attributes.indexPath.section];
            //得到第一個item的indexPath
            NSIndexPath *firstItemIndexPath = [NSIndexPath indexPathForItem:0 inSection:attributes.indexPath.section];
            //得到最后一個item的indexPath
            NSIndexPath *lastItemIndexPath = [NSIndexPath indexPathForItem:MAX(0, numberOfItemsInSection-1) inSection:attributes.indexPath.section];
            //得到第一個item和最后一個item的結構信息
            UICollectionViewLayoutAttributes *firstItemAttributes, *lastItemAttributes;
            if (numberOfItemsInSection>0) {
                //cell有值祭椰,則獲取第一個cell和最后一個cell的結構信息
                firstItemAttributes = [self layoutAttributesForItemAtIndexPath:firstItemIndexPath];
                lastItemAttributes = [self layoutAttributesForItemAtIndexPath:lastItemIndexPath];
            } else {
                //cell沒值,就新建一個UICollectionViewLayoutAttributes
                firstItemAttributes = [UICollectionViewLayoutAttributes new];
                //然后模擬出在當前分區(qū)中的唯一一個cell,cell在header的下面疲陕,高度為0方淤,還與header隔著可能存在的sectionInset的top
                CGFloat y = CGRectGetMaxY(attributes.frame)+self.sectionInset.top;
                firstItemAttributes.frame = CGRectMake(0, y, 0, 0);
                //因為只有一個cell,所以最后一個cell等于第一個cell
                lastItemAttributes = firstItemAttributes;
            }
            
            //獲取當前header的frame
            CGRect rect = attributes.frame;
            
            //當前的滑動距離 + 因為導航欄產生的偏移量蹄殃,默認為64(如果app需求不同携茂,需自己設置)
            CGFloat offset = self.collectionView.contentOffset.y + _navHeight;
            //第一個cell的y值 - 當前header的高度 - 可能存在的sectionInset的top
            CGFloat headerY = firstItemAttributes.frame.origin.y - rect.size.height - self.sectionInset.top;
            
            //哪個大取哪個,保證header懸停
            //針對當前header基本上都是offset更加大诅岩,針對下一個header則會是headerY大讳苦,各自處理
            CGFloat maxY = MAX(offset,headerY);
            
            //最后一個cell的y值 + 最后一個cell的高度 + 可能存在的sectionInset的bottom - 當前header的高度
            //當當前section的footer或者下一個section的header接觸到當前header的底部带膜,計算出的headerMissingY即為有效值
            CGFloat headerMissingY = CGRectGetMaxY(lastItemAttributes.frame) + self.sectionInset.bottom - rect.size.height;
            
            //給rect的y賦新值,因為在最后消失的臨界點要跟誰消失鸳谜,所以取小
            rect.origin.y = MIN(maxY,headerMissingY);
            //給header的結構信息的frame重新賦值
            attributes.frame = rect;
            
            //如果按照正常情況下,header離開屏幕被系統(tǒng)回收膝藕,而header的層次關系又與cell相等,如果不去理會咐扭,會出現(xiàn)cell在header上面的情況
            //通過打印可以知道cell的層次關系zIndex數(shù)值為0芭挽,我們可以將header的zIndex設置成1,如果不放心草描,也可以將它設置成非常大览绿,這里隨便填了個7
            attributes.zIndex = 7;
        }
    }
    
    //轉換回不可變數(shù)組,并返回
    return [superArray copy];
}

//return YES;表示一旦滑動就實時調用上面這個layoutAttributesForElementsInRect:方法
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound {
    return YES;
}

@end
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末穗慕,一起剝皮案震驚了整個濱河市饿敲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逛绵,老刑警劉巖怀各,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異术浪,居然都是意外死亡瓢对,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門胰苏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硕蛹,“玉大人,你說我怎么就攤上這事硕并》ㄑ妫” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵倔毙,是天一觀的道長埃仪。 經常有香客問我,道長陕赃,這世上最難降的妖魔是什么卵蛉? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮么库,結果婚禮上傻丝,老公的妹妹穿的比我還像新娘。我一直安慰自己诉儒,他們只是感情好桑滩,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般运准。 火紅的嫁衣襯著肌膚如雪幌氮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天胁澳,我揣著相機與錄音该互,去河邊找鬼。 笑死韭畸,一個胖子當著我的面吹牛宇智,可吹牛的內容都是我干的。 我是一名探鬼主播胰丁,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼随橘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锦庸?” 一聲冷哼從身側響起机蔗,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎甘萧,沒想到半個月后萝嘁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡扬卷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年牙言,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怪得。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡咱枉,死狀恐怖,靈堂內的尸體忽然破棺而出徒恋,到底是詐尸還是另有隱情蚕断,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布因谎,位于F島的核電站基括,受9級特大地震影響颜懊,放射性物質發(fā)生泄漏财岔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一河爹、第九天 我趴在偏房一處隱蔽的房頂上張望匠璧。 院中可真熱鬧,春花似錦咸这、人聲如沸夷恍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酿雪。三九已至遏暴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間指黎,已是汗流浹背朋凉。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留醋安,地道東北人杂彭。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像吓揪,于是被迫代替她去往敵國和親亲怠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內容