UICollectionView框架总结
一、UIcollectionView介紹
1.1、簡(jiǎn)介
首先看蘋果官方文檔 UICollectionView Class Reference 的介紹:
The UICollectionView class manages an ordered collection of data items and presents them using customizable layouts. Collection views provide the same general function as table views except that a collection view is able to support more than just single-column layouts. Collection views support customizable layouts that can be used to implement multi-column grids, tiled layouts, circular layouts, and many more. You can even change the layout of a collection view dynamically if you want.
在WWDC2012中的Introducing Collection Views,蘋果首次介紹了UICollectionView,類似UITableView的用法使人很容易接受,但強(qiáng)大的自定義布局,又使其相較于UITableView有了選擇它的更多理由,UITableView中的表格只支持單排列表,沒(méi)有辦法支持網(wǎng)格列表模式。
一句話總結(jié)就是:UICollectionView與UITableView相似,卻提供了可自定義多列網(wǎng)格(Grild)的功能。
下面通過(guò)一張圖片來(lái)了解UICollectionView的構(gòu)成:
text1.2、UICollectionView用來(lái)做什么
UICollectionView 類用于管理有序的數(shù)據(jù)單元,并且可以定制化的顯示他們。UICollectionView 除了提供和tableview相同的一般的方法外,還支持一列以上的布局。UICollectionView 支持的定制化布局能夠?qū)崿F(xiàn)網(wǎng)格或是瓷磚的布局效果。你甚至可以動(dòng)態(tài)的改變Collectionview的布局。
二、UICollectionView與UITableView比較
2.1、UICollectionViewDataSource
UICollectionViewDelegate-負(fù)責(zé)提供展示的數(shù)據(jù),實(shí)現(xiàn)下面兩個(gè)必須的委托方法,其實(shí)與UITableView并無(wú)二意
- numberOfItemsInsection: 某個(gè)Section里面有多少item
- cellForItemAtIndexPath: 對(duì)于某個(gè)位置應(yīng)該顯示什么樣的cell,里面會(huì)涉及到cell的復(fù)用,可參考UITableView
2.2、UICollectionViewDelegate
UICollectionViewDelegate-負(fù)責(zé)用戶的交互、cell的外形,委托方法和UITableView相似,可以選擇性實(shí)現(xiàn)以下委托方法。
- collectionView:shouleHighlightItemAtIndexPath:是否支持高亮?
- collectionView:didHighlightItemAtIndexPath:如果支持高亮,那么高亮;
- collectionView:shouldSelectItemAtIndexPath:詢問(wèn)是否可以被選中?
- collectionView:didUnhighlightItemAtIndexPath:如果支持高亮,那么現(xiàn)在取消高亮;
- collectionView:didSelectItemAtIndexPath:如果支持可以被選中,那么選中 cell;
2.3、UICollectionViewLayout
在布局上,與UITableView直接使用系統(tǒng)提供的樣式,UICollectionView使用的是UICollectionViewLayout來(lái)自定義布局樣式。
- UICollectionViewLayout是一個(gè)抽象基類,需要繼承它來(lái)為CollectionView生成Layout信息。Layout對(duì)象的作用是決定Cells,Supplementary Views和Decoration Views在CollectionView中的布局位置。系統(tǒng)也提供了UICollectionViewFlowLayout-l流水式布局效果
- UICollectionView的顯示效果幾乎全部由UICollectionViewLayout負(fù)責(zé),而真正存儲(chǔ)著每一個(gè)cell的位置、大小等屬性的是UICollectionViewLayoutAttributes。每一個(gè)cell對(duì)應(yīng)著一個(gè)屬于自己的UICollectionViewLayoutAttributes,而UICollectionViewLayout正是利用UICollectionViewLayoutAttributes里存在的信息對(duì)每一個(gè)cell進(jìn)行布局。
三、UICollectionView層次結(jié)構(gòu)
UICollectionView類負(fù)責(zé)管理數(shù)據(jù)的有序集合以及以自定義布局的模式來(lái)呈現(xiàn)這些數(shù)據(jù),它提供了一些常用的表格功能,此外還增加了許多單欄布局。UICollectionView支持可以用于實(shí)現(xiàn)多列網(wǎng)格、平鋪的布局、圓形的布局和更多的自定義布局,甚至可以動(dòng)態(tài)的改變它的布局。
3.1、UICollectionView基本使用
我們要使用UICollectionView得實(shí)現(xiàn)UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout這三個(gè)協(xié)議。UICollectionViewDataSource是它的數(shù)據(jù)源,UICollectionDelegate是它的呈現(xiàn)樣式,UICollectionViewDelegateFlowLayout是它的布局樣式。
3.1.1 初始化
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
UICollectionView *colView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
3.1.2 注冊(cè)UICollectionView使用的cell類型
[colView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"myCell"];
3.1.3 設(shè)置代理
colView.delegate = self;
colView.dataSource = self;
3.1.4 實(shí)現(xiàn)協(xié)議UICollectionViewDataSource
//配置UICollectionView的每個(gè)section的item數(shù) - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {return [[self loadData] count]; } //配置section數(shù) - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } //呈現(xiàn)數(shù)據(jù) - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellID = @"myCell"; UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath]; cell.backgroundColor = [UIColor redColor]; return cell; }3.1.5 UICollectionViewDelegateFlowLayout
//配置每個(gè)item的size - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return CGSizeMake(100, 60); } //配置item的邊距 - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(20, 20, 0, 20); } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {return CGSizeMake(200, 60); }3.1.6 UICollectionViewDelegate
復(fù)制代碼 //點(diǎn)擊item時(shí)觸發(fā) - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {NSLog(@"您點(diǎn)擊了item:%@", [[self loadData] objectAtIndex:indexPath.row]); [collectionView cellForItemAtIndexPath:indexPath].backgroundColor = [UIColor redColor]; } //當(dāng)前item是否可以點(diǎn)擊 - (BOOL) collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(nonnull NSIndexPath *)indexPath { if (indexPath.row % 2) { return YES; } return NO; } //cell點(diǎn)擊時(shí)是否高亮,點(diǎn)擊cell時(shí)的樣式和點(diǎn)擊后cell的樣式 - (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath {return YES; } - (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; cell.backgroundColor = [UIColor redColor]; } - (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; cell.backgroundColor = [UIColor grayColor]; }3.2、自定義UICollectionViewFlowLayout
使用UICollectionViewFlowLayout之前,我們來(lái)了解它內(nèi)部常用的屬性
//同一組當(dāng)中,垂直方向:行與行之間的間距;水平方向:列與列之間的間距 @property (nonatomic) CGFloat minimumLineSpacing; //垂直方向:同一行中的cell之間的間距;水平方向:同一列中,cell與cell之間的間距 @property (nonatomic) CGFloat minimumInteritemSpacing; //每個(gè)cell統(tǒng)一尺寸 @property (nonatomic) CGSize itemSize; //滑動(dòng)反向,默認(rèn)滑動(dòng)方向是垂直方向滑動(dòng) @property (nonatomic) UICollectionViewScrollDirection scrollDirection; //每一組頭視圖的尺寸。如果是垂直方向滑動(dòng),則只有高起作用;如果是水平方向滑動(dòng),則只有寬起作用。 @property (nonatomic) CGSize headerReferenceSize; //每一組尾部視圖的尺寸。如果是垂直方向滑動(dòng),則只有高起作用;如果是水平方向滑動(dòng),則只有寬起作用。 @property (nonatomic) CGSize footerReferenceSize; //每一組的內(nèi)容縮進(jìn) @property (nonatomic) UIEdgeInsets sectionInset;3.2.1 NYWaterflowLayout(瀑布流樣式)
NYWaterflowLayout繼承與UICollectionViewLayout 重寫部分方法
核心代碼
- (void)prepareLayout {[super prepareLayout];// 清楚以前計(jì)算的所有高度[self.columnHeights removeAllObjects];for (NSInteger i = 0; i < self.columnCount; i++) { [self.columnHeights addObject:@(self.edgeInsets.top)]; } // 清楚之前所有的布局屬性 [self.attrsArray removeAllObjects]; // 開(kāi)始創(chuàng)建每一個(gè)cell對(duì)應(yīng)的布局屬性 NSInteger count = [self.collectionView numberOfItemsInSection:0]; for (NSInteger i = 0; i < count; i++) { //創(chuàng)建位置 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; [self.attrsArray addObject:attributes]; } } // 決定cell的排布 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {return self.attrsArray; } - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {// 創(chuàng)建布局屬性UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; // collectionView的寬度 CGFloat collectionViewWidth = self.collectionView.frame.size.width; // 設(shè)置布局屬性的frame CGFloat width = (collectionViewWidth - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columnMargin) / self.columnCount; CGFloat h = [self.delegate waterflowLayout:self heightForItemAtIndex:indexPath.item itemWidth:width]; // CGFloat h = 50 + arc4random_uniform(100); // 找出高度最短的那一列 __block NSInteger destColumn = 0; __block CGFloat minColumnHeight = [self.columnHeights[0] doubleValue]; [self.columnHeights enumerateObjectsUsingBlock:^(NSNumber *columnHeightNumber, NSUInteger idx, BOOL * _Nonnull stop) { CGFloat columnHeight = columnHeightNumber.doubleValue; if (minColumnHeight > columnHeight) { minColumnHeight = columnHeight; destColumn = idx; } }]; // 獲取最短列的xy CGFloat x = self.edgeInsets.left + destColumn * (width + self.columnMargin); CGFloat y = minColumnHeight; if (y != self.edgeInsets.top) { y += self.rowMargin; } attributes.frame = CGRectMake(x, y, width, h); //更新最短那列的高度 self.columnHeights[destColumn] = @(CGRectGetMaxY(attributes.frame)); return attributes; } // 設(shè)置CollectionViewContentSize - (CGSize)collectionViewContentSize {__block CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue]; [self.columnHeights enumerateObjectsUsingBlock:^(NSNumber *columnHeightNumber, NSUInteger idx, BOOL * _Nonnull stop) { CGFloat columnHeight = columnHeightNumber.doubleValue; if (maxColumnHeight < columnHeight) { maxColumnHeight = columnHeight; } }]; return CGSizeMake(0, maxColumnHeight + self.edgeInsets.bottom); }NYWaterflowLayoutDelegate
@required - (CGFloat)waterflowLayout:(NYWaterflowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth;@optional // 瀑布流布局的列數(shù) - (NSInteger)columnCountInWaterflowLayout:(NYWaterflowLayout *)waterflowLayout; // 列間距 - (CGFloat)columnMarginInWaterflowLayout:(NYWaterflowLayout *)wateflowLayout; // 行間距 - (CGFloat)rowMarginInWaterflowLayout:(NYWaterflowLayout *)waterflowLayout; // 邊沿間距 - (UIEdgeInsets)edgeInsetsInWaterflowLayout:(NYWaterflowLayout *)waterflowLayout;效果圖
3.2.2 NYCircularLayout(環(huán)形布局)
核心代碼
- (void)prepareLayout {[super prepareLayout];// 獲取item的個(gè)數(shù)_itemCount = (int)[self.collectionView numberOfItemsInSection:0]; _attributeArray = [[NSMutableArray alloc] init]; // 先設(shè)定大圓的半徑取長(zhǎng)和寬最短的 CGFloat radius = MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height)/2; // 計(jì)算圓心的位置 CGPoint center = CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.size.height/2); // 設(shè)置每個(gè)item的大小為50*50 則半徑為25 for (int i = 0; i < _itemCount; i++) { UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]; // 設(shè)置item的大小 attributes.size = CGSizeMake(50, 50); // 計(jì)算每個(gè)item中心的坐標(biāo) 算出的x,y值還要減去item自身的半徑大小 float x = center.x + cosf(2*M_PI/_itemCount * i)*(radius - 25); float y = center.y + sinf(2*M_PI/_itemCount * i)*(radius - 25); attributes.center = CGPointMake(x, y); [_attributeArray addObject:attributes]; } } // 設(shè)定區(qū)域大小 - (CGSize)collectionViewContentSize {return self.collectionView.frame.size; } - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {return _attributeArray; } - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {// 創(chuàng)建布局屬性UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; return attributes; }效果圖
3.2.3 CarouselViewLayout(旋轉(zhuǎn)布局)
核心代碼
- (void)prepareLayout {[super prepareLayout];if (self.visibleCount < 1) { self.visibleCount = 3; } if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { _viewHeight = CGRectGetHeight(self.collectionView.frame); _itemHeight = self.itemSize.height; self.collectionView.contentInset = UIEdgeInsetsMake((_viewHeight - _itemHeight) / 2, 0, (_viewHeight - _itemHeight) / 2, 0); } else { _viewHeight = CGRectGetWidth(self.collectionView.frame); _itemHeight = self.itemSize.width; self.collectionView.contentInset = UIEdgeInsetsMake(0, (_viewHeight - _itemHeight) / 2, 0, (_viewHeight - _itemHeight) / 2); } } - (CGSize)collectionViewContentSize {NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { return CGSizeMake(CGRectGetWidth(self.collectionView.frame), cellCount * _itemHeight); } return CGSizeMake(cellCount * _itemHeight, CGRectGetHeight(self.collectionView.frame)); } - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {NSInteger cellCount = [self.collectionView numberOfItemsInSection:0]; CGFloat centerY = (self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.contentOffset.y : self.collectionView.contentOffset.x) + _viewHeight / 2; NSInteger index = centerY / _itemHeight; NSLog(@"第幾個(gè)-----%ld",(long)index); NSInteger count = (self.visibleCount - 1) / 2; // 取第一個(gè)和當(dāng)前位置減1的最大值 NSInteger minIndex = MAX(0, (index - count)); // 取最后一個(gè)和當(dāng)前位置加1的最小值 NSInteger maxIndex = MIN((cellCount - 1), (index + count)); NSMutableArray *array = [NSMutableArray array]; //更新可視范圍內(nèi)的布局 for (NSInteger i = minIndex; i <= maxIndex; i++) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; [array addObject:attributes]; } return array; } - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];attributes.size = self.itemSize; CGFloat cY = (self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.contentOffset.y : self.collectionView.contentOffset.x) + _viewHeight / 2; CGFloat attributesY = _itemHeight * indexPath.row + _itemHeight / 2; attributes.zIndex = -ABS(attributesY - cY); CGFloat delta = cY - attributesY; CGFloat ratio = - delta / (_itemHeight * 2); CGFloat scale = 1 - ABS(delta) / (_itemHeight * 6.0) * cos(ratio * M_PI_4); // attributes.transform = CGAffineTransformMakeScale(scale, scale); CGFloat centerY = attributesY; switch (self.carouselAnim) { case HJCarouselAnimRotary: attributes.transform = CGAffineTransformRotate(attributes.transform, - ratio * M_PI_4); centerY += sin(ratio * M_PI_2) * _itemHeight / 2; break; case HJCarouselAnimCarousel: centerY = cY + sin(ratio * M_PI_2) * _itemHeight * INTERSPACEPARAM; break; case HJCarouselAnimCarousel1: centerY = cY + sin(ratio * M_PI_2) * _itemHeight * INTERSPACEPARAM; if (delta > 0 && delta <= _itemHeight / 2) { attributes.transform = CGAffineTransformIdentity; CGRect rect = attributes.frame; if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { rect.origin.x = CGRectGetWidth(self.collectionView.frame) / 2 - _itemSize.width * scale / 2; rect.origin.y = centerY - _itemHeight * scale / 2; rect.size.width = _itemSize.width * scale; CGFloat param = delta / (_itemHeight / 2); rect.size.height = _itemHeight * scale * (1 - param) + sin(0.25 * M_PI_2) * _itemHeight * INTERSPACEPARAM * 2 * param; } else { rect.origin.x = centerY - _itemHeight * scale / 2; rect.origin.y = CGRectGetHeight(self.collectionView.frame) / 2 - _itemSize.height * scale / 2; rect.size.height = _itemSize.height * scale; CGFloat param = delta / (_itemHeight / 2); rect.size.width = _itemHeight * scale * (1 - param) + sin(0.25 * M_PI_2) * _itemHeight * INTERSPACEPARAM * 2 * param; } attributes.frame = rect; return attributes; } break; case HJCarouselAnimCoverFlow: { CATransform3D transform = CATransform3DIdentity; transform.m34 = -1.0/400.0f; transform = CATransform3DRotate(transform, ratio * M_PI_4, 1, 0, 0); attributes.transform3D = transform; } break; default: break; } if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { attributes.center = CGPointMake(CGRectGetWidth(self.collectionView.frame) / 2, centerY); } else { attributes.center = CGPointMake(centerY, CGRectGetHeight(self.collectionView.frame) / 2); } return attributes; } //當(dāng)滑動(dòng)停止時(shí)使得item的位置位于中心 - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {CGFloat index = roundf(((self.scrollDirection == UICollectionViewScrollDirectionVertical ? proposedContentOffset.y : proposedContentOffset.x) + _viewHeight / 2 - _itemHeight / 2) / _itemHeight); if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { proposedContentOffset.y = _itemHeight * index + _itemHeight / 2 - _viewHeight / 2; } else { proposedContentOffset.x = _itemHeight * index + _itemHeight / 2 - _viewHeight / 2; } return proposedContentOffset; }效果圖
3.2.4 banner輪播器
3.2.4.1 UIScrollView實(shí)現(xiàn)
核心代碼
//初始化ImageView - (void)initLayoutSubview {[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(self);}];CGSize size = self.scrollView.bounds.size; _preImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)]; [_scrollView addSubview:_preImageView]; _currentImageView = [[UIImageView alloc] initWithFrame:CGRectMake(size.width, 0, size.width, size.height)]; [_scrollView addSubview:_currentImageView]; _nextImageView = [[UIImageView alloc] initWithFrame:CGRectMake(size.width * 2, 0, size.width, size.height)]; [_scrollView addSubview:_nextImageView]; self.scrollView.contentSize = CGSizeMake(3 * self.scrollView.bounds.size.width, 0); WS(weakSelf); [self.pageControl mas_makeConstraints:^(MASConstraintMaker *make) { make.bottom.equalTo(weakSelf.mas_bottom).offset(-10); make.width.equalTo(weakSelf.mas_width); make.height.equalTo(@(60)); }]; self.currentPage = 0; self.pageControl.numberOfPages = self.bannersArray.count; self.pageControl.currentPage = _currentPage; [self startTimer]; } //準(zhǔn)備加載頁(yè) - (void)pageLoad {_preImageView.image = nil;_currentImageView.image = nil;_nextImageView.image = nil; _pageControl.currentPage = _currentPage; - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {[self scrollViewDidEnd];[self startTimer]; }- (void)scrollViewDidEnd { int index = _scrollView.contentOffset.x / self.window.bounds.size.width; if (index == 0) { _currentPage = _currentPage == 0 ? self.bannersArray.count - 1 : _currentPage - 1; } else if (index == 2) { _currentPage = _currentPage + 1 == self.bannersArray.count ? 0 : _currentPage + 1; } [self pageLoad]; }SDCycleScrollViewhttp://www.oschina.net/p/sdcyclescrollview
效果圖
附錄
作者:方向_4d0d
鏈接:https://www.jianshu.com/p/7ffa012f34a4
來(lái)源:簡(jiǎn)書(shū)
簡(jiǎn)書(shū)著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。
總結(jié)
以上是生活随笔為你收集整理的UICollectionView框架总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 输入密码查看flag(详解)——Bugk
- 下一篇: Flutter - Internatio