【OC】【一秒就會】【collectionView 頭部吸住功能】

貢獻做者 -【XJDomain】
博客XJ:  https://my.oschina.net/shengbingli/blog
GitHubhttps://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/XJQRCodeToolDemo
app

// 強大的swift3.0版本側滑全功能、第三方庫都知足不了個人要求
https://github.com/lishengbing/swift3.0-XJDragerDemo
spa

// 強大的oc版本抽屜效果、第三方庫都知足不了個人要求
https://github.com/lishengbing/XJDragViewController
.net

相關文章
相關標籤/搜索