貢獻做者 -【XJDomain】
博客XJ: https://my.oschina.net/shengbingli/blog
GitHub: https://github.com/lishengbing/XJQRCodeToolDemogit
1:tableview有自動頭部和底部吸頂的功能,collectionView也是能夠作到的
2:將collectionView的 flowLayout 換成個人自定義類的對象便可github
使用範例:swift
//固定頭部layout SectionHeadercCollectionViewLayout *layout = [[SectionHeadercCollectionViewLayout alloc] init]; //不固定頭部 UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
// // SectionHeadercCollectionViewLayout.h // XDSectionCollectionView // // Created by 李勝兵 on 15/9/9. // Copyright (c) 2015年 李勝兵. All rights reserved. // #import <UIKit/UIKit.h> @interface SectionHeadercCollectionViewLayout : UICollectionViewFlowLayout @end
// // SectionHeadercCollectionViewLayout.m // XDSectionCollectionView // // Created by 李勝兵 on 15/9/9. // Copyright (c) 2015年 李勝兵. All rights reserved. // #import "SectionHeadercCollectionViewLayout.h" /** * SectionHeader能夠像tableView那樣懸浮在頂部 * 繼承於UICollectionViewFlowLayout,用法與collectionViewFlowLayout同樣 */ @implementation SectionHeadercCollectionViewLayout - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect { //UICollectionViewLayoutAttributes:我稱它爲collectionView中的item(包括cell和header、footer這些)的《結構信息》 //截取到父類所返回的數組(裏面放的是當前屏幕所能展現的item的結構信息),並轉化成不可變數組 NSMutableArray *superArray = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; //建立存索引的數組,無符號(正整數),無序(不能經過下標取值),不可重複(重複的話會自動過濾) NSMutableIndexSet *noneHeaderSections = [NSMutableIndexSet indexSet]; //遍歷superArray,獲得一個當前屏幕中全部的section數組 for (UICollectionViewLayoutAttributes *attributes in superArray) { //若是當前的元素分類是一個cell,將cell所在的分區section加入數組,重複的話會自動過濾 if (attributes.representedElementCategory == UICollectionElementCategoryCell) { [noneHeaderSections addIndex:attributes.indexPath.section]; } } //遍歷superArray,將當前屏幕中擁有的header的section從數組中移除,獲得一個當前屏幕中沒有header的section數組 //正常狀況下,隨着手指往上移,header脫離屏幕會被系統回收而cell尚在,也會觸發該方法 for (UICollectionViewLayoutAttributes *attributes in superArray) { //若是當前的元素是一個header,將header所在的section從數組中移除 if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { [noneHeaderSections removeIndex:attributes.indexPath.section]; } } //遍歷當前屏幕中沒有header的section數組 [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]; //若是當前分區確實有由於離開屏幕而被系統回收的header if (attributes) { //將該header結構信息從新加入到superArray中去 [superArray addObject:attributes]; } }]; //遍歷superArray,改變header結構信息中的參數,使它能夠在當前section還沒徹底離開屏幕的時候一直顯示 for (UICollectionViewLayoutAttributes *attributes in superArray) { //若是當前item是header if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { //獲得當前header所在分區的cell的數量 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]; //而後模擬出在當前分區中的惟一一個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; //第一個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離開屏幕被系統回收,而header的層次關係又與cell相等,若是不去理會,會出現cell在header上面的狀況 //經過打印能夠知道cell的層次關係zIndex數值爲0,咱們能夠將header的zIndex設置成1,若是不放心,也能夠將它設置成很是大,這裏隨便填了個7 attributes.zIndex = 7; } } //轉換回不可變數組,並返回 return [superArray copy]; } //return YES;表示一旦滑動就實時調用上面這個layoutAttributesForElementsInRect:方法 - (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound { return YES; } @end
若是你以爲對你有用,請你star,謝謝
若是你以爲之後對你仍是有點用,也請你star、謝謝數組
// 強大的二維碼一鍵搞定
https://github.com/lishengbing/XJQRCodeToolDemoapp
// 強大的swift3.0版本側滑全功能、第三方庫都知足不了個人要求
https://github.com/lishengbing/swift3.0-XJDragerDemospa
// 強大的oc版本抽屜效果、第三方庫都知足不了個人要求
https://github.com/lishengbing/XJDragViewController.net