iOS流布局UICollectionView系列七——三维中的球型布局
一、引言
通过6篇的博客,从平面上最简单的规则摆放的布局,到不规则的瀑布流布局,再到平面中的圆环布局,我们突破了线性布局的局限,在后面,我们将布局扩展到了空间,在Z轴上进行了平移,我们实现了一个类似UIPickerView的布局模型,其实我们还可以再进一步,类比于平面布局,picKerView只是线性排列布局在空间上的旋转与平移,这次,我们更加充分了利用一下空间的尺寸,来设计一个圆球的布局模型。以下是前几篇博客地址:
1.初识与简单实用UICollectionView:http://my.oschina.net/u/2340880/blog/522613
2.UICollectionView的代理方法:http://my.oschina.net/u/2340880/blog/522682
3.实用FlowLayout进行更灵活布局:http://my.oschina.net/u/2340880/blog/522748
4.自定义FlowLayout进行瀑布流布局:http://my.oschina.net/u/2340880/blog/522806
5.平面圆环布局的实现:http://my.oschina.net/u/2340880/blog/523064
6.将布局从平面应用到空间:http://my.oschina.net/u/2340880/blog/523341
二、将布局扩展为空间球型
在viewController中先实现一些准备代码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 
 | - (void)viewDidLoad {[super viewDidLoad];
 // Do any additional setup after loading the view, typically from a nib.
 MyLayout * layout = [[MyLayout alloc]init];
 UICollectionView * collect  = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 0, 320, 400) collectionViewLayout:layout];
 collect.delegate=self;
 collect.dataSource=self;
 //这里设置的偏移量是为了无缝进行循环的滚动,具体在上一篇博客中有解释
 collect.contentOffset = CGPointMake(320, 400);
 [collect registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cellid"];
 [self.view addSubview:collect];
 }
 
 -(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
 return 1;
 }
 //我们返回30的标签
 -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
 return 30;
 }
 -(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];
 UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 30, 30)];
 label.text = [NSString stringWithFormat:@"%ld",(long)indexPath.row];
 [cell.contentView addSubview:label];
 return cell;
 }
 
 - (void)didReceiveMemoryWarning {
 [super didReceiveMemoryWarning];
 // Dispose of any resources that can be recreated.
 }
 //这里对滑动的contentOffset进行监控,实现循环滚动
 -(void)scrollViewDidScroll:(UIScrollView *)scrollView{
 if (scrollView.contentOffset.y<200) {
 scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, scrollView.contentOffset.y+10*400);
 }else if(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);
 }else if(scrollView.contentOffset.x>11*320){
 scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x-10*320,scrollView.contentOffset.y);
 }
 }
 
 | 
这里面的代码比较上一篇博客中的并没有什么大的改动,只是做了横坐标的兼容。
在我们的layout类中,将代码修改成如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 
 | -(void)prepareLayout{[super prepareLayout];
 
 }
 //返回的滚动范围增加了对x轴的兼容
 -(CGSize)collectionViewContentSize{
 return CGSizeMake( self.collectionView.frame.size.width*([self.collectionView numberOfItemsInSection:0]+2), self.collectionView.frame.size.height*([self.collectionView numberOfItemsInSection:0]+2));
 }
 -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
 return YES;
 }
 
 -(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
 UICollectionViewLayoutAttributes * atti = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
 //获取item的个数
 int itemCounts = (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);
 
 CATransform3D trans3D = CATransform3DIdentity;
 trans3D.m34 = -1/900.0;
 
 CGFloat radius = 15/tanf(M_PI*2/itemCounts/2);
 //根据偏移量 改变角度
 //添加了一个x的偏移量
 float offsety = self.collectionView.contentOffset.y;
 float offsetx = self.collectionView.contentOffset.x;
 //分别计算偏移的角度
 float angleOffsety = offsety/self.collectionView.frame.size.height;
 float angleOffsetx = offsetx/self.collectionView.frame.size.width;
 CGFloat angle1 = (float)(indexPath.row+angleOffsety-1)/itemCounts*M_PI*2;
 //x,y的默认方向相反
 CGFloat angle2 = (float)(indexPath.row-angleOffsetx-1)/itemCounts*M_PI*2;
 //这里我们进行四个方向的排列
 if (indexPath.row%4==1) {
 trans3D = CATransform3DRotate(trans3D, angle1, 1.0,0, 0);
 }else if(indexPath.row%4==2){
 trans3D = CATransform3DRotate(trans3D, angle2, 0, 1, 0);
 }else if(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;
 return atti;
 }
 
 
 -(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
 NSMutableArray * attributes = [[NSMutableArray alloc]init];
 //遍历设置每个item的布局属性
 for (int i=0; i<[self.collectionView numberOfItemsInSection:0]; i++) {
 [attributes addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]];
 }
 return attributes;
 }
 
 | 
布局效果如下:

滑动屏幕,这个圆球是可以进行滚动的。
TIP:这里我们只平均分配了四个方向上的布局,如果item更加小也更加多,我们可以分配到更多的方向上,使球体更加充实。
专注技术,热爱生活,交流技术,也做朋友。
——珲少 QQ群:203317592