iOS流布局UICollectionView詳解

iOS流布局UICollectionView系列一——初識與簡單使用UICollectionView

一矗蕊、簡介

????????UICollectionView是iOS6之后引入的一個新的UI控件陌宿,它和UITableView有著諸多的相似之處购桑,其中許多代理方法都十分類似似将。簡單來說屏积,UICollectionView是比UITbleView更加強(qiáng)大的一個UI控件,有如下幾個方面:

1宾舅、支持水平和垂直兩種方向的布局

2统阿、通過layout配置方式進(jìn)行布局

3彩倚、類似于TableView中的cell特性外,CollectionView中的Item大小和位置可以自由定義

4扶平、通過layout布局回調(diào)的代理方法帆离,可以動態(tài)的定制每個item的大小和collection的大體布局屬性

5、更加強(qiáng)大一點(diǎn)蜻直,完全自定義一套layout布局方案盯质,可以實(shí)現(xiàn)意想不到的效果

這篇博客,我們主要討論CollectionView使用原生layout的方法和相關(guān)屬性概而,其他特點(diǎn)和更強(qiáng)的制定化,會在后面的博客中介紹


二囱修、先來實(shí)現(xiàn)一個最簡單的九宮格類布局

????????在了解UICollectionView的更多屬性前赎瑰,我們先來使用其進(jìn)行一個最簡單的流布局試試看,在controller的viewDidLoad中添加如下代碼:

//創(chuàng)建一個layout布局類UICollectionViewFlowLayout*?layout?=?[[UICollectionViewFlowLayoutalloc]init];//設(shè)置布局方向?yàn)榇怪绷鞑季謑ayout.scrollDirection?=UICollectionViewScrollDirectionVertical;//設(shè)置每個item的大小為100*100layout.itemSize?=CGSizeMake(100,100);//創(chuàng)建collectionView?通過一個布局策略layout來創(chuàng)建UICollectionView*?collect?=?[[UICollectionViewalloc]initWithFrame:self.view.frame?collectionViewLayout:layout];//代理設(shè)置collect.delegate=self;????collect.dataSource=self;//注冊item類型?這里使用系統(tǒng)的類型[collect?registerClass:[UICollectionViewCellclass]?forCellWithReuseIdentifier:@"cellid"];???????[self.view?addSubview:collect];

這里有一點(diǎn)需要注意破镰,collectionView在完成代理回調(diào)前餐曼,必須注冊一個cell油挥,類似如下:

[collect?registerClass:[UICollectionViewCell?class]forCellWithReuseIdentifier:@"cellid"];

這和tableView有些類似凭戴,又有些不同狡相,因?yàn)閠ableView除了注冊cell的方法外粉寞,還可以通過臨時創(chuàng)建來做:

//tableView在從復(fù)用池中取cell的時候沽讹,有如下兩種方法//使用這種方式如果復(fù)用池中無近刘,是可以返回nil的娘荡,我們在臨時創(chuàng)建即可-?(nullable__kindofUITableViewCell*)dequeueReusableCellWithIdentifier:(NSString*)identifier;//6.0后使用如下的方法直接從注冊的cell類獲取創(chuàng)建钞护,如果沒有注冊?會崩潰-?(__kindofUITableViewCell*)dequeueReusableCellWithIdentifier:(NSString*)identifier?forIndexPath:(NSIndexPath*)indexPathNS_AVAILABLE_IOS(6_0);

我們可以分析:因?yàn)閁ICollectionView是iOS6.0之前的新類喉祭,因此這里統(tǒng)一了從復(fù)用池中獲取cell的方法养渴,沒有再提供可以返回nil的方式,并且在UICollectionView的回調(diào)代理中泛烙,只能使用從復(fù)用池中獲取cell的方式進(jìn)行cell的返回理卑,其他方式會崩潰,例如:

//這是正確的方法-(UICollectionViewCell*)collectionView:(UICollectionView*)collectionView?cellForItemAtIndexPath:(NSIndexPath*)indexPath{UICollectionViewCell*?cell??=?[collectionView?dequeueReusableCellWithReuseIdentifier:@"cellid"forIndexPath:indexPath];????cell.backgroundColor?=?[UIColorcolorWithRed:arc4random()%255/255.0green:arc4random()%255/255.0blue:arc4random()%255/255.0alpha:1];returncell;}//這樣做會崩潰-(UICollectionViewCell*)collectionView:(UICollectionView*)collectionView?cellForItemAtIndexPath:(NSIndexPath*)indexPath{//????UICollectionViewCell?*?cell??=?[collectionView?dequeueReusableCellWithReuseIdentifier:@"cellid"?forIndexPath:indexPath];//????cell.backgroundColor?=?[UIColor?colorWithRed:arc4random()%255/255.0?green:arc4random()%255/255.0?blue:arc4random()%255/255.0?alpha:1];UICollectionViewCell*?cell?=?[[UICollectionViewCellalloc]init];returncell;}

上面錯誤的方式會崩潰蔽氨,信息如下藐唠,讓我們使用從復(fù)用池中取cell的方式:

上面的設(shè)置完成后,我們來實(shí)現(xiàn)如下幾個代理方法:

這里與TableView的回調(diào)方式十分類似

//返回分區(qū)個數(shù)-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView?*)collectionView{return1;}//返回每個分區(qū)的item個數(shù)-(NSInteger)collectionView:(UICollectionView?*)collectionViewnumberOfItemsInSection:(NSInteger)section{return10;}//返回每個item-(UICollectionViewCell?*)collectionView:(UICollectionView?*)collectionViewcellForItemAtIndexPath:(NSIndexPath?*)indexPath{????UICollectionViewCell?*?cell??=?[collectionViewdequeueReusableCellWithReuseIdentifier:@"cellid"forIndexPath:indexPath];????cell.backgroundColor?=?[UIColorcolorWithRed:arc4random()%255/255.0?green:arc4random()%255/255.0blue:arc4random()%255/255.0alpha:1];returncell;}

效果如下:

同樣鹉究,如果內(nèi)容的大小超出一屏宇立,和tableView類似是可以進(jìn)行視圖滑動的。

還有一點(diǎn)細(xì)節(jié)坊饶,我們在上面設(shè)置布局方式的時候設(shè)置了垂直布局:

layout.scrollDirection?=UICollectionViewScrollDirectionVertical;//這個是水平布局//layout.scrollDirection?=?UICollectionViewScrollDirectionHorizontal;

這樣系統(tǒng)會在一行充滿后進(jìn)行第二行的排列泄伪,如果設(shè)置為水平布局,則會在一列充滿后匿级,進(jìn)行第二列的布局蟋滴,這種方式也被稱為流式布局

三染厅、UICollectionView中的常用方法和屬性

//通過一個布局策略初識化CollectionView-?(instancetype)initWithFrame:(CGRect)frame?collectionViewLayout:(UICollectionViewLayout*)layout;//獲取和設(shè)置collection的layout@property(nonatomic,strong)UICollectionViewLayout*collectionViewLayout;//數(shù)據(jù)源和代理@property(nonatomic,weak,nullable)id?delegate;@property(nonatomic,weak,nullable)id?dataSource;//從一個class或者xib文件進(jìn)行cell(item)的注冊-?(void)registerClass:(nullableClass)cellClass?forCellWithReuseIdentifier:(NSString*)identifier;-?(void)registerNib:(nullableUINib*)nib?forCellWithReuseIdentifier:(NSString*)identifier;//下面兩個方法與上面相似,這里注冊的是頭視圖或者尾視圖的類//其中第二個參數(shù)是設(shè)置?頭視圖或者尾視圖?系統(tǒng)為我們定義好了這兩個字符串//UIKIT_EXTERN?NSString?*const?UICollectionElementKindSectionHeader?NS_AVAILABLE_IOS(6_0);//UIKIT_EXTERN?NSString?*const?UICollectionElementKindSectionFooter?NS_AVAILABLE_IOS(6_0);-?(void)registerClass:(nullableClass)viewClass?forSupplementaryViewOfKind:(NSString*)elementKind?withReuseIdentifier:(NSString*)identifier;-?(void)registerNib:(nullableUINib*)nib?forSupplementaryViewOfKind:(NSString*)kind?withReuseIdentifier:(NSString*)identifier;//這兩個方法是從復(fù)用池中取出cell或者頭尾視圖-?(__kindofUICollectionViewCell*)dequeueReusableCellWithReuseIdentifier:(NSString*)identifier?forIndexPath:(NSIndexPath*)indexPath;-?(__kindofUICollectionReusableView*)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind?withReuseIdentifier:(NSString*)identifier?forIndexPath:(NSIndexPath*)indexPath;//設(shè)置是否允許選中?默認(rèn)yes@property(nonatomic)BOOLallowsSelection;//設(shè)置是否允許多選?默認(rèn)no@property(nonatomic)BOOLallowsMultipleSelection;//獲取所有選中的item的位置信息-?(nullableNSArray?*)indexPathsForSelectedItems;//設(shè)置選中某一item津函,并使視圖滑動到相應(yīng)位置肖粮,scrollPosition是滑動位置的相關(guān)參數(shù),如下:/*

typedef?NS_OPTIONS(NSUInteger,?UICollectionViewScrollPosition)?{

????//無

????UICollectionViewScrollPositionNone?????????????????=?0,

????//垂直布局時使用的?對應(yīng)上中下

????UICollectionViewScrollPositionTop??????????????????=?1?<<?0,

????UICollectionViewScrollPositionCenteredVertically???=?1?<<?1,

????UICollectionViewScrollPositionBottom???????????????=?1?<<?2,

????//水平布局時使用的??對應(yīng)左中右

????UICollectionViewScrollPositionLeft?????????????????=?1?<<?3,

????UICollectionViewScrollPositionCenteredHorizontally?=?1?<<?4,

????UICollectionViewScrollPositionRight????????????????=?1?<<?5

};

*/-?(void)selectItemAtIndexPath:(nullableNSIndexPath*)indexPath?animated:(BOOL)animated?scrollPosition:(UICollectionViewScrollPosition)scrollPosition;//將某一item取消選中-?(void)deselectItemAtIndexPath:(NSIndexPath*)indexPath?animated:(BOOL)animated;//重新加載數(shù)據(jù)-?(void)reloadData;//下面這兩個方法尔苦,可以重新設(shè)置collection的布局涩馆,后面的方法多了一個布局完成后的回調(diào),iOS7后可以用//使用這兩個方法可以產(chǎn)生非常炫酷的動畫效果-?(void)setCollectionViewLayout:(UICollectionViewLayout*)layout?animated:(BOOL)animated;-?(void)setCollectionViewLayout:(UICollectionViewLayout*)layout?animated:(BOOL)animated?completion:(void(^?__nullable)(BOOLfinished))completionNS_AVAILABLE_IOS(7_0);//下面這些方法更加強(qiáng)大允坚,我們可以對布局更改后的動畫進(jìn)行設(shè)置//這個方法傳入一個布局策略layout魂那,系統(tǒng)會開始進(jìn)行布局渲染,返回一個UICollectionViewTransitionLayout對象//這個UICollectionViewTransitionLayout對象管理動畫的相關(guān)屬性稠项,我們可以進(jìn)行設(shè)置-?(UICollectionViewTransitionLayout*)startInteractiveTransitionToCollectionViewLayout:(UICollectionViewLayout*)layout?completion:(nullableUICollectionViewLayoutInteractiveTransitionCompletion)completionNS_AVAILABLE_IOS(7_0);//準(zhǔn)備好動畫設(shè)置后涯雅,我們需要調(diào)用下面的方法進(jìn)行布局動畫的展示,之后會調(diào)用上面方法的block回調(diào)-?(void)finishInteractiveTransitionNS_AVAILABLE_IOS(7_0);//調(diào)用這個方法取消上面的布局動畫設(shè)置展运,之后也會進(jìn)行上面方法的block回調(diào)-?(void)cancelInteractiveTransitionNS_AVAILABLE_IOS(7_0);//獲取分區(qū)數(shù)-?(NSInteger)numberOfSections;//獲取某一分區(qū)的item數(shù)-?(NSInteger)numberOfItemsInSection:(NSInteger)section;//下面兩個方法獲取item或者頭尾視圖的layout屬性活逆,這個UICollectionViewLayoutAttributes對象//存放著布局的相關(guān)數(shù)據(jù),可以用來做完全自定義布局拗胜,后面博客會介紹-?(nullableUICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath;-?(nullableUICollectionViewLayoutAttributes*)layoutAttributesForSupplementaryElementOfKind:(NSString*)kind?atIndexPath:(NSIndexPath*)indexPath;//獲取某一點(diǎn)所在的indexpath位置-?(nullableNSIndexPath*)indexPathForItemAtPoint:(CGPoint)point;//獲取某個cell所在的indexPath-?(nullableNSIndexPath*)indexPathForCell:(UICollectionViewCell*)cell;//根據(jù)indexPath獲取cell-?(nullableUICollectionViewCell*)cellForItemAtIndexPath:(NSIndexPath*)indexPath;//獲取所有可見cell的數(shù)組-?(NSArray<__kindofUICollectionViewCell*>?*)visibleCells;//獲取所有可見cell的位置數(shù)組-?(NSArray?*)indexPathsForVisibleItems;//下面三個方法是iOS9中新添加的方法蔗候,用于獲取頭尾視圖-?(UICollectionReusableView*)supplementaryViewForElementKind:(NSString*)elementKind?atIndexPath:(NSIndexPath*)indexPathNS_AVAILABLE_IOS(9_0);-?(NSArray?*)visibleSupplementaryViewsOfKind:(NSString*)elementKindNS_AVAILABLE_IOS(9_0);-?(NSArray?*)indexPathsForVisibleSupplementaryElementsOfKind:(NSString*)elementKindNS_AVAILABLE_IOS(9_0);//使視圖滑動到某一位置,可以帶動畫效果-?(void)scrollToItemAtIndexPath:(NSIndexPath*)indexPath?atScrollPosition:(UICollectionViewScrollPosition)scrollPosition?animated:(BOOL)animated;//下面這些方法用于動態(tài)添加埂软,刪除锈遥,移動某些分區(qū)獲取items-?(void)insertSections:(NSIndexSet*)sections;-?(void)deleteSections:(NSIndexSet*)sections;-?(void)reloadSections:(NSIndexSet*)sections;-?(void)moveSection:(NSInteger)section?toSection:(NSInteger)newSection;-?(void)insertItemsAtIndexPaths:(NSArray?*)indexPaths;-?(void)deleteItemsAtIndexPaths:(NSArray?*)indexPaths;-?(void)reloadItemsAtIndexPaths:(NSArray?*)indexPaths;-?(void)moveItemAtIndexPath:(NSIndexPath*)indexPath?toIndexPath:(NSIndexPath*)newIndexPath;


iOS流布局UICollectionView系列二——UICollectionView的代理方法

一、引言

????????在上一篇博客中仰美,介紹了最基本的UICollectionView的使用和其中我們常用的屬性和方法迷殿,也介紹了瀑布流布局的過程與思路,這篇博客來討論關(guān)于UICollectionView的代理方法的使用咖杂。

二庆寺、UICollectionViewDataSource協(xié)議

????????這個協(xié)議主要用于collectionView相關(guān)數(shù)據(jù)的處理,包含方法如下:

首先诉字,有兩個方法是我們必須實(shí)現(xiàn)的:

設(shè)置每個分區(qū)的Item個數(shù)

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;

設(shè)置返回每個item的屬性

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;

下面的方法是可選實(shí)現(xiàn)的:

雖然這個方法是可選的懦尝,一般我們都會去實(shí)現(xiàn),設(shè)置分區(qū)數(shù)

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;

對頭視圖或者尾視圖進(jìn)行設(shè)置

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;


設(shè)置某個item是否可以被移動壤圃,返回NO則不能移動

- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);

移動item的時候陵霉,會調(diào)用這個方法

- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath;

三伍绳、UICollectionViewDelegate協(xié)議

????????這個協(xié)議用來設(shè)置和處理collectionView的功能和一些邏輯踊挠,所有方法都是可選實(shí)現(xiàn):

是否允許某個Item的高亮,返回NO,則不能進(jìn)入高亮狀態(tài)

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath;

當(dāng)item高亮?xí)r觸發(fā)的方法

- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath;


結(jié)束高亮狀態(tài)時觸發(fā)的方法

- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath;


是否可以選中某個Item效床,返回NO睹酌,則不能選中

- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;


是否可以取消選中某個Item

- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath;


已經(jīng)選中某個item時觸發(fā)的方法

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;


取消選中某個Item時觸發(fā)的方法

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;


將要加載某個Item時調(diào)用的方法

- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);


將要加載頭尾視圖時調(diào)用的方法

- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);


已經(jīng)展示某個Item時觸發(fā)的方法

- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;


已經(jīng)展示某個頭尾視圖時觸發(fā)的方法

- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;


這個方法設(shè)置是否展示長按菜單

- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath;

長按菜單中可以觸發(fā)一下類復(fù)制粘貼的方法,效果如下:

這個方法用于設(shè)置要展示的菜單選項(xiàng)

- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;


這個方法用于實(shí)現(xiàn)點(diǎn)擊菜單按鈕后的觸發(fā)方法,通過測試剩檀,只有copy憋沿,cut和paste三個方法可以使用

- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;

通過下面的方式可以將點(diǎn)擊按鈕的方法名打印出來:

-(void)collectionView:(UICollectionView*)collectionView?performAction:(SEL)action?forItemAtIndexPath:(NSIndexPath*)indexPath?withSender:(id)sender{NSLog(@"%@",NSStringFromSelector(action));}


collectionView進(jìn)行重新布局時調(diào)用的方法

- (nonnull UICollectionViewTransitionLayout *)collectionView:(UICollectionView *)collectionView transitionLayoutForOldLayout:(UICollectionViewLayout *)fromLayout newLayout:(UICollectionViewLayout *)toLayout;

iOS流布局UICollectionView系列三——使用FlowLayout進(jìn)行更靈活布局

一、引言

????????前面的博客介紹了UICollectionView的相關(guān)方法和其協(xié)議中的方法沪猴,但對布局的管理類UICollectionViewFlowLayout沒有著重探討辐啄,這篇博客介紹關(guān)于布局的相關(guān)設(shè)置和屬性方法。通過layout的設(shè)置运嗜,我們可以編寫更加靈活的布局效果壶辜。

二、將九宮格式的布局進(jìn)行升級

????????在第一篇博客中洗出,通過UICollectionView士复,我們很輕松的完成了一個九宮格的布局,但是如此中規(guī)中矩的布局方式翩活,有時候并不能滿足我們的需求,有時我們需要每一個Item展示不同的大小便贵,代碼如下:

-?(void)viewDidLoad?{????[superviewDidLoad];//?Do?any?additional?setup?after?loading?the?view,?typically?from?a?nib.UICollectionViewFlowLayout*layout?=?[[UICollectionViewFlowLayoutalloc]init];????layout.scrollDirection?=UICollectionViewScrollDirectionVertical;UICollectionView*collect?=?[[UICollectionViewalloc]initWithFrame:CGRectMake(0,0,320,400)?collectionViewLayout:layout];????collect.delegate=self;????collect.dataSource=self;????????[collect?registerClass:[UICollectionViewCellclass]?forCellWithReuseIdentifier:@"cellid"];??;????[self.view?addSubview:collect];????????}//設(shè)置每個item的大小菠镇,雙數(shù)的為50*50?單數(shù)的為100*100-(CGSize)collectionView:(UICollectionView*)collectionView?layout:(UICollectionViewLayout*)collectionViewLayout?sizeForItemAtIndexPath:(NSIndexPath*)indexPath{if(indexPath.row%2==0)?{returnCGSizeMake(50,50);????}else{returnCGSizeMake(100,100);????}}//代理相應(yīng)方法-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView{return1;}-(NSInteger)collectionView:(UICollectionView*)collectionView?numberOfItemsInSection:(NSInteger)section{return100;}-(UICollectionViewCell*)collectionView:(UICollectionView*)collectionView?cellForItemAtIndexPath:(NSIndexPath*)indexPath{UICollectionViewCell*?cell??=?[collectionView?dequeueReusableCellWithReuseIdentifier:@"cellid"forIndexPath:indexPath];????cell.backgroundColor?=?[UIColorcolorWithRed:arc4random()%255/255.0green:arc4random()%255/255.0blue:arc4random()%255/255.0alpha:1];returncell;}

效果如下:

現(xiàn)在的布局效果是不是炫酷了許多。

三承璃、UICollectionViewFlowLayout相關(guān)屬性方法

????????UICollectionViewFlowLayout是系統(tǒng)提供給我們一個封裝好的流布局設(shè)置類利耍,其中有一些布局屬性我們可以進(jìn)行設(shè)置:?

設(shè)置行與行之間的間距最小距離

@property(nonatomic) CGFloat minimumLineSpacing;


設(shè)置列與列之間的間距最小距離

@property(nonatomic) CGFloat minimumInteritemSpacing;


設(shè)置每個item的大小

@property(nonatomic) CGSize itemSize;


設(shè)置每個Item的估計大小,一般不需要設(shè)置

@property(nonatomic) CGSize estimatedItemSize NS_AVAILABLE_IOS(8_0);


設(shè)置布局方向

@property(nonatomic) UICollectionViewScrollDirection scrollDirection;

這個UICollectionViewScrollDirection的枚舉如下:

typedefNS_ENUM(NSInteger,UICollectionViewScrollDirection)?{UICollectionViewScrollDirectionVertical,//水平布局UICollectionViewScrollDirectionHorizontal//垂直布局};


設(shè)置頭視圖尺寸大小

@property (nonatomic) CGSize headerReferenceSize;


設(shè)置尾視圖尺寸大小

@property (nonatomic) CGSize footerReferenceSize;


設(shè)置分區(qū)的EdgeInset

@property (nonatomic) UIEdgeInsets sectionInset;

這個屬性可以設(shè)置分區(qū)的偏移量盔粹,例如我們在剛才的例子中添加如下設(shè)置:

layout.sectionInset?=UIEdgeInsetsMake(20,20,20,20);

效果如下隘梨,會看到分區(qū)的邊界閃出了20像素

下面這兩個方法設(shè)置分區(qū)的頭視圖和尾視圖是否始終固定在屏幕上邊和下邊

?@property (nonatomic) BOOL sectionHeadersPinToVisibleBounds NS_AVAILABLE_IOS(9_0);

@property (nonatomic) BOOL sectionFootersPinToVisibleBounds NS_AVAILABLE_IOS(9_0);

四、動態(tài)的配置layout的相關(guān)屬性UICollectionViewDelegateFlowLayout

????????上面的方法在創(chuàng)建FlowLayout時靜態(tài)的進(jìn)行設(shè)置舷嗡,如果我們需要動態(tài)的設(shè)置這些屬性轴猎,就像我們例子中的,每個item的大小會有差異进萄,我們可以通過代理來實(shí)現(xiàn)捻脖。

? ? ? ? UICollectionViewDelegateFlowLayout是UICollectionViewDelegate的子協(xié)議,其中常用方法如下中鼠,我們只需要實(shí)現(xiàn)我們需要的即可:


動態(tài)設(shè)置每個Item的尺寸大小

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;


動態(tài)設(shè)置每個分區(qū)的EdgeInsets

?- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;


動態(tài)設(shè)置每行的間距大小

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;


動態(tài)設(shè)置每列的間距大小

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;


動態(tài)設(shè)置某個分區(qū)頭視圖大小

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;


動態(tài)設(shè)置某個分區(qū)尾視圖大小

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;

iOS流布局UICollectionView系列四——自定義FlowLayout進(jìn)行瀑布流布局

一可婶、引言

????????前幾篇博客從UICollectionView的基礎(chǔ)應(yīng)用到設(shè)置UICollectionViewFlowLayout更加靈活的進(jìn)行布局,但都限制在系統(tǒng)為我們準(zhǔn)備好的布局框架中援雇,還是有一些局限性矛渴,例如,如果我要進(jìn)行瀑布流似的不定高布局惫搏,前面的方法就很難滿足我們的需求了具温,如下:

這種布局無疑在app的應(yīng)用中更加廣泛蚕涤,商品的展示,書架書目的展示桂躏,都會傾向于采用這樣的布局方式钻趋,當(dāng)然,通過自定義FlowLayout剂习,我們也很容易實(shí)現(xiàn)蛮位。

二、進(jìn)行自定義瀑布流布局

????????首先鳞绕,我們新建一個文件繼承于UICollectionViewFlowLayout:

@interface?MyLayout?:?UICollectionViewFlowLayout

為了演示的方便失仁,這里我不做更多的封裝,只添加一個屬性们何,直接讓外界將item個數(shù)傳遞進(jìn)來萄焦,我們把重心方法重寫布局的方法上:

@interfaceMyLayout?:?UICollectionViewFlowLayout@property(nonatomic,assign)int?itemCount;@end

前面說過,UICollectionViewFlowLayout是一個專門用來管理collectionView布局的類冤竹,因此拂封,collectionView在進(jìn)行UI布局前,會通過這個類的對象獲取相關(guān)的布局信息鹦蠕,F(xiàn)lowLayout類將這些布局信息全部存放在了一個數(shù)組中冒签,數(shù)組中是UICollectionViewLayoutAttributes類,這個類是對item布局的具體設(shè)置钟病,以后咱們在討論這個類萧恕。總之肠阱,F(xiàn)lowLayout類將每個item的位置等布局信息放在一個數(shù)組中票唆,在collectionView布局時,會調(diào)用FlowLayout類layoutAttributesForElementsInRect:方法來獲取這個布局配置數(shù)組屹徘。因此走趋,我們需要重寫這個方法,返回我們自定義的配置數(shù)組缘回,另外吆视,F(xiàn)lowLayout類在進(jìn)行布局之前,會調(diào)用prepareLayout方法酥宴,所以我們可以重寫這個方法啦吧,在里面對我們的自定義配置數(shù)據(jù)進(jìn)行一些設(shè)置。

簡單來說拙寡,自定義一個FlowLayout布局類就是兩個步驟:

1授滓、設(shè)計好我們的布局配置數(shù)據(jù) prepareLayout方法中

2、返回我們的配置數(shù)組?layoutAttributesForElementsInRect方法中

示例代碼如下:

@implementation?MyLayout{//這個數(shù)組就是我們自定義的布局配置數(shù)組NSMutableArray*?_attributeAttay;}//數(shù)組的相關(guān)設(shè)置在這個方法中//布局前的準(zhǔn)備會調(diào)用這個方法-(void)prepareLayout{????_attributeAttay?=?[[NSMutableArrayalloc]init];????[superprepareLayout];//演示方便?我們設(shè)置為靜態(tài)的2列//計算每一個item的寬度floatWIDTH?=?([UIScreenmainScreen].bounds.size.width-self.sectionInset.left-self.sectionInset.right-self.minimumInteritemSpacing)/2;//定義數(shù)組保存每一列的高度//這個數(shù)組的主要作用是保存每一列的總高度,這樣在布局時般堆,我們可以始終將下一個Item放在最短的列下面CGFloatcolHight[2]={self.sectionInset.top,self.sectionInset.bottom};//itemCount是外界傳進(jìn)來的item的個數(shù)?遍歷來設(shè)置每一個item的布局for(inti=0;?i<_itemCount;?i++)?{//設(shè)置每個item的位置等相關(guān)屬性NSIndexPath*index?=?[NSIndexPathindexPathForItem:i?inSection:0];//創(chuàng)建一個布局屬性類在孝,通過indexPath來創(chuàng)建UICollectionViewLayoutAttributes*?attris?=?[UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:index];//隨機(jī)一個高度?在40——190之間CGFloathight?=?arc4random()%150+40;//哪一列高度小?則放到那一列下面//標(biāo)記最短的列intwidth=0;if(colHight[0]colHight[1])?{self.itemSize?=CGSizeMake(WIDTH,?(colHight[0]-self.sectionInset.top)*2/_itemCount-self.minimumLineSpacing);????}else{self.itemSize?=CGSizeMake(WIDTH,?(colHight[1]-self.sectionInset.top)*2/_itemCount-self.minimumLineSpacing);????}????}//這個方法中返回我們的布局?jǐn)?shù)組-(NSArray?*)layoutAttributesForElementsInRect:(CGRect)rect{return_attributeAttay;}@end

自定義完成FlowLayout后,我們在ViewController中進(jìn)行使用:

-?(void)viewDidLoad?{????[superviewDidLoad];//?Do?any?additional?setup?after?loading?the?view,?typically?from?a?nib.MyLayout?*?layout?=?[[MyLayout?alloc]init];????layout.scrollDirection?=UICollectionViewScrollDirectionVertical;????layout.itemCount=100;UICollectionView*?collect??=?[[UICollectionViewalloc]initWithFrame:CGRectMake(0,0,320,400)?collectionViewLayout:layout];????collect.delegate=self;????collect.dataSource=self;????????[collect?registerClass:[UICollectionViewCellclass]?forCellWithReuseIdentifier:@"cellid"];??????[self.view?addSubview:collect];????????}-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView{return1;}-(NSInteger)collectionView:(UICollectionView*)collectionView?numberOfItemsInSection:(NSInteger)section{return100;}-(UICollectionViewCell*)collectionView:(UICollectionView*)collectionView?cellForItemAtIndexPath:(NSIndexPath*)indexPath{UICollectionViewCell*?cell??=?[collectionView?dequeueReusableCellWithReuseIdentifier:@"cellid"forIndexPath:indexPath];????cell.backgroundColor?=?[UIColorcolorWithRed:arc4random()%255/255.0green:arc4random()%255/255.0blue:arc4random()%255/255.0alpha:1];returncell;}

運(yùn)行效果就是我們引言中的截圖淮摔。

三私沮、UICollectionViewLayoutAttributes類中我們可以配置的屬性

????????通過上面的例子,我們可以了解和橙,collectionView的item布局其實(shí)是LayoutAttributes類具體配置的仔燕,這個類可以配置的布局屬性不止是frame這么簡單,其中還有許多屬性:

//配置item的布局位置@property(nonatomic)CGRectframe;//配置item的中心@property(nonatomic)CGPointcenter;//配置item的尺寸@property(nonatomic)CGSizesize;//配置item的3D效果@property(nonatomic)CATransform3Dtransform3D;//配置item的bounds@property(nonatomic)CGRectboundsNS_AVAILABLE_IOS(7_0);//配置item的旋轉(zhuǎn)@property(nonatomic)CGAffineTransformtransformNS_AVAILABLE_IOS(7_0);//配置item的alpha@property(nonatomic)CGFloatalpha;//配置item的z坐標(biāo)@property(nonatomic)NSIntegerzIndex;//?default?is?0//配置item的隱藏@property(nonatomic,getter=isHidden)BOOLhidden;//item的indexpath@property(nonatomic,strong)NSIndexPath*indexPath;//獲取item的類型@property(nonatomic,readonly)UICollectionElementCategoryrepresentedElementCategory;@property(nonatomic,readonly,nullable)NSString*representedElementKind;//一些創(chuàng)建方法+?(instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath*)indexPath;+?(instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString*)elementKind?withIndexPath:(NSIndexPath*)indexPath;+?(instancetype)layoutAttributesForDecorationViewOfKind:(NSString*)decorationViewKind?withIndexPath:(NSIndexPath*)indexPath;

通過上面的屬性魔招,可以布局出各式各樣的炫酷效果晰搀,正如一句話:沒有做不到,只有想不到办斑。

iOS流布局UICollectionView系列五——圓環(huán)布局的實(shí)現(xiàn)

一外恕、引言

????????前邊的幾篇博客,我們了解了UICollectionView的基本用法以及一些擴(kuò)展乡翅,在不定高的瀑布流布局中鳞疲,我們發(fā)現(xiàn),可以通過設(shè)置具體的布局屬性類UICollectionViewLayoutAttributes來設(shè)置設(shè)置每個item的具體位置蠕蚜,我們可以再擴(kuò)展一下建丧,如果位置我們可以自由控制,那個布局我們也可以更加靈活波势,就比如創(chuàng)建一個如下的circleLayout:

這種布局方式在apple的官方文檔中也有介紹,是UICollectionView的一個應(yīng)用示例橄维。

二尺铣、設(shè)計一個圓環(huán)布局

? ? ? ? 先自定義一個layout類,這個類繼承于UICollectionViewLayout争舞,UICollectionLayout是一個布局抽象基類凛忿,我們要使用自定義的布局方式,必須將其子類化竞川,可能你還記得店溢,我們在進(jìn)行瀑布流布局的時候使用過UICollectionViewFlowLayout類,這個類就是繼承于UICollectionViewLayout類委乌,系統(tǒng)為我們實(shí)現(xiàn)好的一個布局方案床牧。

@interfaceMyLayout?:?UICollectionViewLayout//這個int值存儲有多少個item@property(nonatomic,assign)int?itemCount;@end


我們需要重寫這個類的三個方法,來進(jìn)行圓環(huán)布局的設(shè)置遭贸,首先是prepareLayout戈咳,為布局做一些準(zhǔn)備工作,使用collectionViewContentSize來設(shè)置內(nèi)容的區(qū)域大小,最后使用layoutAttributesForElementsInRect方法來返回我們的布局信息字典著蛙,這個和前面瀑布流布局的思路是一樣的:

@implementation?MyLayout{NSMutableArray*?_attributeAttay;}-(void)prepareLayout{????[superprepareLayout];//獲取item的個數(shù)_itemCount?=?(int)[self.collectionView?numberOfItemsInSection:0];????_attributeAttay?=?[[NSMutableArrayalloc]init];//先設(shè)定大圓的半徑?取長和寬最短的CGFloatradius?=?MIN(self.collectionView.frame.size.width,self.collectionView.frame.size.height)/2;//計算圓心位置CGPointcenter?=CGPointMake(self.collectionView.frame.size.width/2,self.collectionView.frame.size.height/2);//設(shè)置每個item的大小為50*50?則半徑為25for(inti=0;?i<_itemCount;?i++)?{UICollectionViewLayoutAttributes*?attris?=?[UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:[NSIndexPathindexPathForItem:i?inSection:0]];//設(shè)置item大小attris.size?=CGSizeMake(50,50);//計算每個item的圓心位置/*

?????????.

?????????.?.

?????????.???.?r

?????????.?????.

?????????.........

?????????*///計算每個item中心的坐標(biāo)//算出的x?y值還要減去item自身的半徑大小floatx?=?center.x+cosf(2*M_PI/_itemCount*i)*(radius-25);floaty?=?center.y+sinf(2*M_PI/_itemCount*i)*(radius-25);?????????????attris.center?=CGPointMake(x,?y);????????[_attributeAttay?addObject:attris];????}???????????}//設(shè)置內(nèi)容區(qū)域的大小-(CGSize)collectionViewContentSize{returnself.collectionView.frame.size;}//返回設(shè)置數(shù)組-(NSArray?*)layoutAttributesForElementsInRect:(CGRect)rect{return_attributeAttay;}


在viewController中代碼如下:

-?(void)viewDidLoad?{????[superviewDidLoad];//?Do?any?additional?setup?after?loading?the?view,?typically?from?a?nib.MyLayout?*?layout?=?[[MyLayout?alloc]init];UICollectionView*?collect??=?[[UICollectionViewalloc]initWithFrame:CGRectMake(0,0,320,400)?collectionViewLayout:layout];????collect.delegate=self;????collect.dataSource=self;????????[collect?registerClass:[UICollectionViewCellclass]?forCellWithReuseIdentifier:@"cellid"];????[self.view?addSubview:collect];}-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView{return1;}-(NSInteger)collectionView:(UICollectionView*)collectionView?numberOfItemsInSection:(NSInteger)section{return10;}-(UICollectionViewCell*)collectionView:(UICollectionView*)collectionView?cellForItemAtIndexPath:(NSIndexPath*)indexPath{UICollectionViewCell*?cell??=?[collectionView?dequeueReusableCellWithReuseIdentifier:@"cellid"forIndexPath:indexPath];????cell.layer.masksToBounds?=YES;????cell.layer.cornerRadius?=25;????cell.backgroundColor?=?[UIColorcolorWithRed:arc4random()%255/255.0green:arc4random()%255/255.0blue:arc4random()%255/255.0alpha:1];returncell;}


如上非常簡單的一些邏輯控制删铃,我們就實(shí)現(xiàn)哦圓環(huán)布局,隨著item的多少踏堡,布局會自動調(diào)整猎唁,如果不是UICollectionView的功勞,實(shí)現(xiàn)這樣的功能顷蟆,我們可能要寫上一陣子了^_^诫隅。

iOS流布局UICollectionView系列六——將布局從平面應(yīng)用到空間

一、引言

????????前面慕的,我們將布局由線性的瀑布流布局?jǐn)U展到了圓環(huán)布局阎肝,這使我們使用UICollectionView的布局思路大大邁進(jìn)了一步,這次肮街,我們玩的更加炫一些风题,想辦法將布局應(yīng)用到空間。之前在管理布局的item的具體屬性的類UICollectionViewLayoutAttributrs類中嫉父,有transform3D這個屬性沛硅,通過這個屬性的設(shè)置,我們真的可以在空間的坐標(biāo)系中進(jìn)行布局設(shè)計绕辖。iOS系統(tǒng)的控件中摇肌,也并非沒有這樣的先例,UIPickerView就是很好的一個實(shí)例仪际,這篇博客围小,我們就通過使用UICollectionView實(shí)現(xiàn)一個類似系統(tǒng)的UIPickerView的布局視圖,來體會UICollectionView在3D控件布局的魅力树碱。系統(tǒng)的pickerView效果如下:

二肯适、先來實(shí)現(xiàn)一個炫酷的滾輪空間布局

????????萬丈的高樓也是由一磚一瓦堆砌而成,在我們完全模擬系統(tǒng)pickerView前成榜,我們應(yīng)該先將視圖的布局?jǐn)[放這一問題解決框舔。我們依然來創(chuàng)建一個類,繼承于UICollectionViewLayout:

@interfaceMyLayout?:?UICollectionViewLayout@end

對于.m文件的內(nèi)容赎婚,前幾篇博客中我們都是在prepareLayout中進(jìn)行布局的靜態(tài)設(shè)置刘绣,那是因?yàn)槲覀兦皫灼┛椭械牟季侄际庆o態(tài)的,布局并不會隨著我們的手勢操作而發(fā)生太大的變化挣输,因此我們?nèi)吭趐repareLayout中一次配置完了纬凤。而我們這次要討論的布局則不同,pickerView會隨著我們手指的拖動而進(jìn)行滾動歧焦,因此UICollectionView中的每一個item的布局是在不斷變化的移斩,所以這次肚医,我們采用動態(tài)配置的方式,在layoutAttributesForItemAtIndexPath方法中進(jìn)行每個item的布局屬性設(shè)置向瓷。

????????至于layoutAttributesForItemAtIndexPath方法肠套,它也是UICollectionViewLayout類中的方法,用于我們自定義時進(jìn)行重寫猖任,至于為什么動態(tài)布局要在這里面配置item的布局屬性你稚,后面我們會了解到。

? ? ? ? 在編寫我們的布局類之前朱躺,先做好準(zhǔn)備工作刁赖,在viewController中,實(shí)現(xiàn)如下代碼:

-?(void)viewDidLoad?{????[superviewDidLoad];//?Do?any?additional?setup?after?loading?the?view,?typically?from?a?nib.MyLayout?*?layout?=?[[MyLayout?alloc]init];UICollectionView*?collect??=?[[UICollectionViewalloc]initWithFrame:CGRectMake(0,0,320,400)?collectionViewLayout:layout];????collect.delegate=self;????collect.dataSource=self;????[collect?registerClass:[UICollectionViewCellclass]?forCellWithReuseIdentifier:@"cellid"];????[self.view?addSubview:collect];}-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView{return1;}-(NSInteger)collectionView:(UICollectionView*)collectionView?numberOfItemsInSection:(NSInteger)section{return10;}-(UICollectionViewCell*)collectionView:(UICollectionView*)collectionView?cellForItemAtIndexPath:(NSIndexPath*)indexPath{UICollectionViewCell*?cell??=?[collectionView?dequeueReusableCellWithReuseIdentifier:@"cellid"forIndexPath:indexPath];????cell.backgroundColor?=?[UIColorcolorWithRed:arc4random()%255/255.0green:arc4random()%255/255.0blue:arc4random()%255/255.0alpha:1];UILabel*?label?=?[[UILabelalloc]initWithFrame:CGRectMake(0,0,250,80)];????label.text?=?[NSStringstringWithFormat:@"我是第%ld行",(long)indexPath.row];????[cell.contentView?addSubview:label];returncell;}

上面我創(chuàng)建了10個Item长搀,并且在每個Item上添加了一個標(biāo)簽宇弛,標(biāo)寫是第幾行。

在我們自定義的布局類中重寫layoutAttributesForElementsInRect源请,在其中返回我們的布局?jǐn)?shù)組:

-(NSArray?*)layoutAttributesForElementsInRect:(CGRect)rect{NSMutableArray*?attributes?=?[[NSMutableArrayalloc]init];//遍歷設(shè)置每個item的布局屬性for(inti=0;?i<[self.collectionView?numberOfItemsInSection:0];?i++)?{????????[attributes?addObject:[selflayoutAttributesForItemAtIndexPath:[NSIndexPathindexPathForItem:i?inSection:0]]];????}returnattributes;}

之后枪芒,在我們布局類中重寫layoutAttributesForItemAtIndexPath方法:

-(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath{//創(chuàng)建一個item布局屬性類UICollectionViewLayoutAttributes*?atti?=?[UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:indexPath];//獲取item的個數(shù)intitemCounts?=?(int)[self.collectionView?numberOfItemsInSection:0];//設(shè)置每個item的大小為260*100atti.size?=CGSizeMake(260,100);/*

???后邊介紹的代碼添加在這里


???*/returnatti;}

上面的代碼中,我們什么都沒有做谁尸,下面我們一步步來實(shí)現(xiàn)3D的滾輪效果舅踪。

首先,我們先將所有的item的位置都設(shè)置為collectionView的中心:

atti.center?=CGPointMake(self.collectionView.frame.size.width/2,self.collectionView.frame.size.height/2);

這時良蛮,如果我們運(yùn)行程序的話抽碌,所有item都將一層層貼在屏幕的中央,如下:

很丑對吧决瞳,之后我們來設(shè)置每個item的3D效果,在上面的布局方法中添加如下代碼:

//創(chuàng)建一個transform3D類//CATransform3D是一個類似矩陣的結(jié)構(gòu)體//CATransform3DIdentity創(chuàng)建空得矩陣CATransform3Dtrans3D?=CATransform3DIdentity;//這個值設(shè)置的是透視度货徙,影響視覺離投影平面的距離trans3D.m34?=-1/900.0;//下面這些屬性?后面會具體介紹//這個是3D滾輪的半徑CGFloatradius?=50/tanf(M_PI*2/itemCounts/2);//計算每個item應(yīng)該旋轉(zhuǎn)的角度CGFloatangle?=?(float)(indexPath.row)/itemCounts*M_PI*2;//這個方法返回一個新的CATransform3D對象,在原來的基礎(chǔ)上進(jìn)行旋轉(zhuǎn)效果的追加//第一個參數(shù)為旋轉(zhuǎn)的弧度皮胡,后三個分別對應(yīng)x破婆,y,z軸胸囱,我們需要以x軸進(jìn)行旋轉(zhuǎn)trans3D?=CATransform3DRotate(trans3D,?angle,1.0,0,0);//進(jìn)行設(shè)置atti.transform3D?=?trans3D;

????????對于上面的radius屬性,運(yùn)用了一些簡單的幾何和三角函數(shù)的知識瀑梗。如果我們將系統(tǒng)的pickerView沿著y軸旋轉(zhuǎn)90°烹笔,你會發(fā)現(xiàn)側(cè)面的它是一個規(guī)則的正多邊形,這里的radius就是這個多邊形中心到其邊的垂直距離抛丽,也是內(nèi)切圓的半徑谤职,所有的item拼成了一個正多邊形,示例如下:

通過簡單的數(shù)學(xué)知識亿鲜,h/2弦對應(yīng)的角的弧度為2*pi/(邊數(shù))/2允蜈,在根據(jù)三角函數(shù)相關(guān)知識可知冤吨,這個角的正切值為h/2/radius,這就是我們radius的由來饶套。?

????????對于angle屬性漩蟆,它是每一個item的x軸旋轉(zhuǎn)度數(shù),如果我們將所有item的中心都放在一點(diǎn)妓蛮,通過旋轉(zhuǎn)讓它們散開如下圖所示:

每個item旋轉(zhuǎn)的弧度就是其索引/(2*pi)怠李。

通過上面的設(shè)置,我們再運(yùn)行代碼蛤克,效果如下:

仔細(xì)觀察我們可以發(fā)現(xiàn)捺癞,item以x中軸線進(jìn)行了旋轉(zhuǎn)平均布局,側(cè)面的效果就是我們上面的簡筆畫那樣构挤,下面要進(jìn)行我們的第三步了髓介,將這個item,全部沿著其Z軸向前拉筋现,就可以成為我們滾輪的效果唐础,示例圖如下:

我們繼續(xù)在剛才的代碼后面添加這行代碼:

//這個方法也返回一個transform3D對象,追加平移效果夫否,后面三個參數(shù)彻犁,對應(yīng)平移的x,y凰慈,z軸汞幢,我們沿z軸平移trans3D?=CATransform3DTranslate(trans3D,0,0,?radius);

再次運(yùn)行,效果如下:

布局的效果我們已經(jīng)完成了微谓,離成功很近了對吧森篷,只是現(xiàn)在的布局是靜態(tài)的,我們不能滑動這個滾輪豺型,我們還需要用動態(tài)滑動做一些處理仲智。

三、讓滾輪滑動起來

????????????通過上面的努力姻氨,我們已經(jīng)靜態(tài)布局出了一個類似pickerView的滾輪钓辆,現(xiàn)在我們再來添加滑動滾動的效果

????????首先,我們需要給collectionView一個滑動的范圍肴焊,我們以一屏collectionView的滑動距離來當(dāng)做滾輪滾動一下的參照前联,我們在布局類中的如下方法中返回滑動區(qū)域:

-(CGSize)collectionViewContentSize{returnCGSizeMake(self.collectionView.frame.size.width,self.collectionView.frame.size.height*[self.collectionView?numberOfItemsInSection:0]);}

這時我們的collectionView已經(jīng)可以進(jìn)行滑動,但是并不是我們想要的效果娶眷,滾輪并沒有滾動似嗤,而是隨著滑動出了屏幕,因此届宠,我們需要在滑動的時候不停的動態(tài)布局烁落,將滾輪始終固定在collectionView的中心乘粒,先需要在布局類中實(shí)現(xiàn)如下方法:

//返回yes,則一有變化就會刷新布局-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{returnYES;????}

將上面的布局的中心點(diǎn)設(shè)置加上一個動態(tài)的偏移量:

atti.center?=CGPointMake(self.collectionView.frame.size.width/2,self.collectionView.frame.size.height/2+self.collectionView.contentOffset.y);

現(xiàn)在在運(yùn)行伤塌,會發(fā)現(xiàn)滾輪會隨著滑動始終固定在中間灯萍,但是還是不如人意,滾輪并沒有轉(zhuǎn)動起來寸谜,我們還需要動態(tài)的設(shè)置每個item的旋轉(zhuǎn)角度竟稳,這樣連續(xù)看起來,滾輪就轉(zhuǎn)了起來熊痴,在上面設(shè)置布局的方法中他爸,我們在添加一些處理:

//獲取當(dāng)前的偏移量floatoffset?=self.collectionView.contentOffset.y;//在角度設(shè)置上,添加一個偏移角度floatangleOffset?=?offset/self.collectionView.frame.size.height;CGFloatangle?=?(float)(indexPath.row+angleOffset)/itemCounts*M_PI*2;

再看看效果果善,沒錯诊笤,就是這么簡單,滾輪已經(jīng)轉(zhuǎn)了起來巾陕。

四讨跟、讓其循環(huán)滾動的邏輯

????????我們再進(jìn)一步,如果滾動可以循環(huán)鄙煤,這個控件將更加炫酷晾匠,添加這樣的邏輯也很簡單,通過監(jiān)測scrollView的偏移量梯刚,我們可以對齊進(jìn)行處理凉馆,因?yàn)閏ollectionView繼承于scrollView,我們可以直接在ViewController中實(shí)現(xiàn)其代理方法亡资,如下:

-(void)scrollViewDidScroll:(UIScrollView*)scrollView{//小于半屏?則放到最后一屏多半屏if(scrollView.contentOffset.y<200)?{????????scrollView.contentOffset?=CGPointMake(0,?scrollView.contentOffset.y+10*400);//大于最后一屏多一屏?放回第一屏}elseif(scrollView.contentOffset.y>11*400){????????scrollView.contentOffset?=CGPointMake(0,?scrollView.contentOffset.y-10*400);????}}

因?yàn)樵蹅兊沫h(huán)狀布局澜共,上面的邏輯剛好可以無縫對接,但是會有新的問題锥腻,一開始運(yùn)行嗦董,滾輪就是出現(xiàn)在最后一個item的位置,而不是第一個瘦黑,并且有些相關(guān)的地方京革,我們也需要一些適配:

在viewController中:

//一開始將collectionView的偏移量設(shè)置為1屏的偏移量collect.contentOffset?=CGPointMake(0,400);

在layout類中:

//將滾動范圍設(shè)置為(item總數(shù)+2)*每屏高度?-(CGSize)collectionViewContentSize{returnCGSizeMake(self.collectionView.frame.size.width,self.collectionView.frame.size.height*([self.collectionView?numberOfItemsInSection:0]+2));}

//將計算的具體item角度向前遞推一個CGFloatangle?=?(float)(indexPath.row+angleOffset-1)/itemCounts*M_PI*2;

OK,我們終于大功告成了幸斥,可以發(fā)現(xiàn)存崖,實(shí)現(xiàn)這樣一個布局效果炫酷的控件,代碼其實(shí)并沒有多少睡毒,相比,數(shù)學(xué)邏輯要比編寫代碼本身困難冗栗,這十分類似數(shù)學(xué)中的幾何問題演顾,如果你弄清了邏輯供搀,解決是分分鐘的事,我們可以通過這樣的一個思路挠乳,設(shè)計更多3D或者平面特效的布局方案诀豁,抽獎的轉(zhuǎn)動圓盤钱豁,書本的翻頁,甚至立體的標(biāo)簽云屿脐,UICollectionView都可以實(shí)現(xiàn),這篇博客中的代碼在下面的連接中宪卿,疏漏之處的诵,歡迎指正!

http://pan.baidu.com/s/1jGCmbKM

iOS流布局UICollectionView系列七——三維中的球型布局

一佑钾、引言

????????通過6篇的博客西疤,從平面上最簡單的規(guī)則擺放的布局,到不規(guī)則的瀑布流布局休溶,再到平面中的圓環(huán)布局代赁,我們突破了線性布局的局限,在后面兽掰,我們將布局?jǐn)U展到了空間芭碍,在Z軸上進(jìn)行了平移,我們實(shí)現(xiàn)了一個類似UIPickerView的布局模型孽尽,其實(shí)我們還可以再進(jìn)一步窖壕,類比于平面布局,picKerView只是線性排列布局在空間上的旋轉(zhuǎn)與平移泻云,這次艇拍,我們更加充分了利用一下空間的尺寸,來設(shè)計一個圓球的布局模型宠纯。

二卸夕、將布局?jǐn)U展為空間球型

????????在viewController中先實(shí)現(xiàn)一些準(zhǔn)備代碼:

-?(void)viewDidLoad?{????[superviewDidLoad];//?Do?any?additional?setup?after?loading?the?view,?typically?from?a?nib.MyLayout?*?layout?=?[[MyLayout?alloc]init];UICollectionView*?collect??=?[[UICollectionViewalloc]initWithFrame:CGRectMake(0,0,320,400)?collectionViewLayout:layout];????collect.delegate=self;????collect.dataSource=self;//這里設(shè)置的偏移量是為了無縫進(jìn)行循環(huán)的滾動,具體在上一篇博客中有解釋collect.contentOffset?=CGPointMake(320,400);????[collect?registerClass:[UICollectionViewCellclass]?forCellWithReuseIdentifier:@"cellid"];????[self.view?addSubview:collect];}-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView{return1;}//我們返回30的標(biāo)簽-(NSInteger)collectionView:(UICollectionView*)collectionView?numberOfItemsInSection:(NSInteger)section{return30;}-(UICollectionViewCell*)collectionView:(UICollectionView*)collectionView?cellForItemAtIndexPath:(NSIndexPath*)indexPath{UICollectionViewCell*?cell??=?[collectionView?dequeueReusableCellWithReuseIdentifier:@"cellid"forIndexPath:indexPath];????cell.backgroundColor?=?[UIColorcolorWithRed:arc4random()%255/255.0green:arc4random()%255/255.0blue:arc4random()%255/255.0alpha:1];UILabel*?label?=?[[UILabelalloc]initWithFrame:CGRectMake(0,0,30,30)];????label.text?=?[NSStringstringWithFormat:@"%ld",(long)indexPath.row];????[cell.contentView?addSubview:label];returncell;}-?(void)didReceiveMemoryWarning?{????[superdidReceiveMemoryWarning];//?Dispose?of?any?resources?that?can?be?recreated.}//這里對滑動的contentOffset進(jìn)行監(jiān)控婆瓜,實(shí)現(xiàn)循環(huán)滾動-(void)scrollViewDidScroll:(UIScrollView*)scrollView{if(scrollView.contentOffset.y<200)?{????????scrollView.contentOffset?=CGPointMake(scrollView.contentOffset.x,?scrollView.contentOffset.y+10*400);????}elseif(scrollView.contentOffset.y>11*400){????????scrollView.contentOffset?=CGPointMake(scrollView.contentOffset.x,?scrollView.contentOffset.y-10*400);????}if(scrollView.contentOffset.x<160)?{????????scrollView.contentOffset?=CGPointMake(scrollView.contentOffset.x+10*320,scrollView.contentOffset.y);????}elseif(scrollView.contentOffset.x>11*320){????????scrollView.contentOffset?=CGPointMake(scrollView.contentOffset.x-10*320,scrollView.contentOffset.y);????}}


這里面的代碼比較上一篇博客中的并沒有什么大的改動快集,只是做了橫坐標(biāo)的兼容。

在我們的layout類中廉白,將代碼修改成如下:

-(void)prepareLayout{????[superprepareLayout];????}//返回的滾動范圍增加了對x軸的兼容-(CGSize)collectionViewContentSize{returnCGSizeMake(self.collectionView.frame.size.width*([self.collectionView?numberOfItemsInSection:0]+2),self.collectionView.frame.size.height*([self.collectionView?numberOfItemsInSection:0]+2));}-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{returnYES;}-(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath{UICollectionViewLayoutAttributes*?atti?=?[UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:indexPath];//獲取item的個數(shù)intitemCounts?=?(int)[self.collectionView?numberOfItemsInSection:0];????atti.center?=CGPointMake(self.collectionView.frame.size.width/2+self.collectionView.contentOffset.x,self.collectionView.frame.size.height/2+self.collectionView.contentOffset.y);????atti.size?=CGSizeMake(30,30);CATransform3Dtrans3D?=CATransform3DIdentity;????trans3D.m34?=-1/900.0;CGFloatradius?=15/tanf(M_PI*2/itemCounts/2);//根據(jù)偏移量?改變角度//添加了一個x的偏移量floatoffsety?=self.collectionView.contentOffset.y;floatoffsetx?=self.collectionView.contentOffset.x;//分別計算偏移的角度floatangleOffsety?=?offsety/self.collectionView.frame.size.height;floatangleOffsetx?=?offsetx/self.collectionView.frame.size.width;CGFloatangle1?=?(float)(indexPath.row+angleOffsety-1)/itemCounts*M_PI*2;//x个初,y的默認(rèn)方向相反CGFloatangle2?=?(float)(indexPath.row-angleOffsetx-1)/itemCounts*M_PI*2;//這里我們進(jìn)行四個方向的排列if(indexPath.row%4==1)?{????????trans3D?=CATransform3DRotate(trans3D,?angle1,1.0,0,0);????}elseif(indexPath.row%4==2){????????trans3D?=CATransform3DRotate(trans3D,?angle2,0,1,0);????}elseif(indexPath.row%4==3){????????trans3D?=CATransform3DRotate(trans3D,?angle1,0.5,0.5,0);????}else{????????trans3D?=CATransform3DRotate(trans3D,?angle1,0.5,-0.5,0);????}????????trans3D?=CATransform3DTranslate(trans3D,0,0,?radius);????????atti.transform3D?=?trans3D;returnatti;}-(NSArray?*)layoutAttributesForElementsInRect:(CGRect)rect{NSMutableArray*?attributes?=?[[NSMutableArrayalloc]init];//遍歷設(shè)置每個item的布局屬性for(inti=0;?i<[self.collectionView?numberOfItemsInSection:0];?i++)?{????????[attributes?addObject:[selflayoutAttributesForItemAtIndexPath:[NSIndexPathindexPathForItem:i?inSection:0]]];????}returnattributes;}


布局效果如下:

滑動屏幕,這個圓球是可以進(jìn)行滾動的猴蹂。

TIP:這里我們只平均分配了四個方向上的布局院溺,如果item更加小也更加多,我們可以分配到更多的方向上磅轻,使球體更加充實(shí)珍逸。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逐虚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谆膳,更是在濱河造成了極大的恐慌叭爱,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漱病,死亡現(xiàn)場離奇詭異买雾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)杨帽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門漓穿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人睦尽,你說我怎么就攤上這事器净。” “怎么了当凡?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵山害,是天一觀的道長。 經(jīng)常有香客問我沿量,道長浪慌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任朴则,我火速辦了婚禮权纤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乌妒。我一直安慰自己汹想,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布撤蚊。 她就那樣靜靜地躺著古掏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侦啸。 梳的紋絲不亂的頭發(fā)上槽唾,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音光涂,去河邊找鬼庞萍。 笑死,一個胖子當(dāng)著我的面吹牛忘闻,可吹牛的內(nèi)容都是我干的钝计。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼私恬!你這毒婦竟也來了交播?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤践付,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后缺厉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體永高,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年提针,在試婚紗的時候發(fā)現(xiàn)自己被綠了命爬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡辐脖,死狀恐怖饲宛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嗜价,我是刑警寧澤艇抠,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站久锥,受9級特大地震影響家淤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瑟由,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一絮重、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧歹苦,春花似錦青伤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至痴施,卻和暖如春擎厢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辣吃。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工动遭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人神得。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓厘惦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宵蕉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

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