iOS之UICollectionView自定義布局

UICollectionView基礎(chǔ)

  • UICollectionViewFlowLayout:視圖布局對(duì)象(流水布局:一行排滿,自動(dòng)排到下行)吗讶,繼承自UICollectionViewLayout篓像。UICollectionViewLayout內(nèi)有一個(gè)collectionView屬性动遭,所有的視圖布局對(duì)象都繼承自UICollectionViewLayout善茎。
  • 若我們要自定義布局對(duì)象明垢,我們一般繼承UICollectionViewFlowLayout蚣常,然后重寫里面的一些方法就可以了。
  • 需要實(shí)現(xiàn)三個(gè)協(xié)議痊银;UICollectionViewDataSource(數(shù)據(jù)源)抵蚊、UICollectionViewDelegateFlowLayout(視圖布局),自定義布局需要實(shí)現(xiàn)UICollectionViewDataSource、UICollectionViewDelegate兩個(gè)協(xié)議即可贞绳。

一谷醉、自定義線性布局

  • 首先要繼承與流水布局UICollectionViewFlowLayout
#import <UIKit/UIKit.h>

@interface LineCollectionViewLayout : UICollectionViewFlowLayout

@end
  • 重寫相應(yīng)的方法
#import "LineCollectionViewLayout.h"

@implementation LineCollectionViewLayout

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

/**
 * 用來做布局的初始化操作(不建議在init方法中進(jìn)行布局的初始化操作)
 - 注意:一定要調(diào)用[super prepareLayout]
 */
- (void)prepareLayout{
    [super prepareLayout];
    // 水平滾動(dòng)
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    // 設(shè)置內(nèi)邊距
    CGFloat inset = (self.collectionView.frame.size.width - self.itemSize.width) * 0.5;
    self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
}

/**
 * 這個(gè)方法的返回值是一個(gè)數(shù)組(數(shù)組里面存放著rect范圍內(nèi)所有元素的布局屬性)
 * 這個(gè)數(shù)組中存放的都是UICollectionViewLayoutAttributes對(duì)象
 * 這個(gè)方法的返回值決定了rect范圍內(nèi)所有元素的排布(frame)*/
/**
 UICollectionViewLayoutAttributes *attrs;
 1.一個(gè)cell對(duì)應(yīng)一個(gè)UICollectionViewLayoutAttributes對(duì)象
 2.UICollectionViewLayoutAttributes對(duì)象決定了cell的frame
 */
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    // 獲得super已經(jīng)計(jì)算好的布局屬性
    NSArray *array = [super layoutAttributesForElementsInRect:rect];
    // 計(jì)算collectionView最中心點(diǎn)的x值
    CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;
    // 在原有布局屬性的基礎(chǔ)上,進(jìn)行微調(diào)
    for (UICollectionViewLayoutAttributes *attrs in array) {
        // cell的中心點(diǎn)x 和 collectionView最中心點(diǎn)的x值 的間距
        CGFloat delta = ABS(attrs.center.x - centerX);
        
        // 根據(jù)間距值 計(jì)算 cell的縮放比例
        CGFloat scale = 1 - delta / self.collectionView.frame.size.width;
        
        // 設(shè)置縮放比例
        attrs.transform = CGAffineTransformMakeScale(scale, scale);
    }
    
    return array;
}

/**
 * 當(dāng)collectionView的顯示范圍發(fā)生改變的時(shí)候冈闭,是否需要重新刷新布局
 * 一旦重新刷新布局俱尼,就會(huì)重新調(diào)用下面的方法:
 1.prepareLayout
 2.layoutAttributesForElementsInRect:方法
 */
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}

/**
 * 這個(gè)方法的返回值,就決定了collectionView停止?jié)L動(dòng)時(shí)的偏移量
 * proposedContentOffset:原本情況下萎攒,collectionView停止?jié)L動(dòng)時(shí)最終的偏移量
 * velocity:滾動(dòng)速率遇八,通過這個(gè)參數(shù)可以了解滾動(dòng)的方向
 */
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{
    // 計(jì)算出最終顯示的矩形框
    CGRect rect;
    rect.origin.y = 0;
    rect.origin.x = proposedContentOffset.x;
    rect.size = self.collectionView.frame.size;
    
    // 獲得super已經(jīng)計(jì)算好的布局屬性
    NSArray *array = [super layoutAttributesForElementsInRect:rect];
    
    // 計(jì)算collectionView最中心點(diǎn)的x值
    CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;
    
    // 存放最小的間距值
    CGFloat minDelta = MAXFLOAT;
    for (UICollectionViewLayoutAttributes *attrs in array) {
        if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {
            minDelta = attrs.center.x - centerX;
        }
    }
    // 修改原有的偏移量
    proposedContentOffset.x += minDelta;
    return proposedContentOffset;
}

@end

自定義環(huán)形布局

  • 同樣要繼承與流水布局UICollectionViewFlowLayout
#import <UIKit/UIKit.h>

@interface CircleCollectionViewLayout : UICollectionViewFlowLayout

@end
  • 重寫相應(yīng)的方法
#import "CircleCollectionViewLayout.h"

@interface CircleCollectionViewLayout()
/** 布局屬性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end

@implementation CircleCollectionViewLayout

/** 懶加載 */
- (NSMutableArray *)attrsArray
{
    if (!_attrsArray) {
        _attrsArray = [NSMutableArray array];
    }
    return _attrsArray;
}

- (void)prepareLayout
{
    [super prepareLayout];
    
    [self.attrsArray removeAllObjects];
    
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i < count; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
        [self.attrsArray addObject:attrs];
    }
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return self.attrsArray;
}

/**
 * 這個(gè)方法需要返回indexPath位置對(duì)應(yīng)cell的布局屬性
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    CGFloat radius = 70;
    // 圓心的位置
    CGFloat oX = self.collectionView.frame.size.width * 0.5;
    CGFloat oY = self.collectionView.frame.size.height * 0.5;
    
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    attrs.size = CGSizeMake(50, 50);
    if (count == 1) {
        attrs.center = CGPointMake(oX, oY);
    } else {
        CGFloat angle = (2 * M_PI / count) * indexPath.item;
        CGFloat centerX = oX + radius * sin(angle);
        CGFloat centerY = oY + radius * cos(angle);
        attrs.center = CGPointMake(centerX, centerY);
    }
    
    return attrs;
}

@end

對(duì)自定義布局的使用

 // 創(chuàng)建布局
    CircleCollectionViewLayout *layout = [[CircleCollectionViewLayout alloc] init];
    
    // 創(chuàng)建CollectionView
    CGFloat collectionW = self.view.frame.size.width;
    CGFloat collectionH = 200;
    CGRect frame = CGRectMake(0, 150, collectionW, collectionH);
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    [self.view addSubview:collectionView];
    self.collectionView = collectionView;
    
    // 注冊(cè)
    [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([PhotoCell class]) bundle:nil] forCellWithReuseIdentifier:photoId];
  • ****增加 touchesBegan:方法,通過點(diǎn)擊讓兩種布局相互轉(zhuǎn)換****
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if ([self.collectionView.collectionViewLayout isKindOfClass:[LineCollectionViewLayout class]]) {
        [self.collectionView setCollectionViewLayout:[[CircleCollectionViewLayout alloc] init] animated:YES];
    } else {
        LineCollectionViewLayout *layout = [[LineCollectionViewLayout alloc] init];
        layout.itemSize = CGSizeMake(100, 100);
        [self.collectionView setCollectionViewLayout:layout animated:YES];
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耍休,一起剝皮案震驚了整個(gè)濱河市刃永,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌羹应,老刑警劉巖揽碘,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件次屠,死亡現(xiàn)場(chǎng)離奇詭異园匹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)劫灶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門裸违,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人本昏,你說我怎么就攤上這事供汛。” “怎么了涌穆?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵怔昨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我宿稀,道長(zhǎng)趁舀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任祝沸,我火速辦了婚禮矮烹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘罩锐。我一直安慰自己奉狈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布涩惑。 她就那樣靜靜地躺著仁期,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上跛蛋,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天碰纬,我揣著相機(jī)與錄音,去河邊找鬼问芬。 笑死悦析,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的此衅。 我是一名探鬼主播强戴,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼挡鞍!你這毒婦竟也來了骑歹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤墨微,失蹤者是張志新(化名)和其女友劉穎道媚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翘县,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡最域,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锈麸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镀脂。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖忘伞,靈堂內(nèi)的尸體忽然破棺而出薄翅,到底是詐尸還是另有隱情,我是刑警寧澤氓奈,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布翘魄,位于F島的核電站,受9級(jí)特大地震影響舀奶,放射性物質(zhì)發(fā)生泄漏暑竟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一伪节、第九天 我趴在偏房一處隱蔽的房頂上張望光羞。 院中可真熱鬧,春花似錦怀大、人聲如沸纱兑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)潜慎。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铐炫,已是汗流浹背垒手。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留倒信,地道東北人科贬。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鳖悠,于是被迫代替她去往敵國(guó)和親榜掌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容