UICollectionView

UICollectionView

注意:类似tableView,可以设置控制器的view就是collectionView,也可以添加一个比较小的collectionView作为控制器的view的子控件

流水布局:

  • 系统会自动布局,一排放不下,自动放到第二排
  • 界面要通过流水布局对象来设置各种约束
    // 创建一个流水布局对象
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];

    // 设置cell的尺寸
    layout.itemSize = CGSizeMake(100, 100);

    // 行间距
    layout.minimumLineSpacing = 50;

    // 设置滚动的方向
    layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

    // 设置cell之间的间距
    layout.minimumInteritemSpacing = 50;

    // 组间距
    layout.sectionInset = UIEdgeInsetsMake(100, 20, 0, 30);

collectionView的一些设置

  • 关掉弹簧效果 self.collectionView.bounces = NO;

  • 去掉导航条 self.collectionView.showsHorizontalScrollIndicator = NO;

  • 分页效果 self.collectionView.pagingEnabled = YES;

创建

  • 创建的时候必须设置布局对象
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
UICollectionView *collV = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
  • 如果有多种布局方式,可以设置布局方式来切换
[self.collectionView setCollectionViewLayout:[[XMGCircleLayout alloc] init] animated:YES];
  • 如果是UICollectionViewController,一般把流水布局的创建和控制器的创建封装起来:重写init方法
- (instancetype)init{
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    return [super initWithCollectionViewLayout:layout];
}
  • 注意,在UICollectionViewController中,self.view不等于self.collectionView
  • 拿到self.collectionView才能设置背景颜色,要加子控件也是加在这里

数据源方法

  • 类似于UITableViewController,先设置数据源,一般为控制器,控制器遵守数据源协议
  • 必须用注册cell的方式
    // 注册cell,先定义ID
    static NSString *ID = @"cell";
    [collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:ID];
  • 设置cell的样式
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{


    // 从缓存池里取
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
    //indexPath不同于tableView,这里是用indexPath.item来区分每个cell
    cell.backgroundColor = [UIColor greenColor];
    return cell;
}
  • 有多少个cell
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 12;
}

自定义cell

  • 如果要给cell里面加点东西,那就必须自定义,因为它不同于tableView的cell有各种样式。
  • 在initwithframe方法里添加子控件(
    • 懒加载不然每次get都会创建一个imageView)(
    • 类似于tableView的cell自定义,子控件也是加到contontView上),
  • layoutsubViews里布局子控件,注意frame要设置为cell的bounds,详见几何问题总结
  • 要给imageView传图片数据,一般也是给cell添加一个imageName属性,然后重写imageName的set方法,在这里传递数据。

代理方法

  • 协议
  • 实现图像错位移动:监听停止滑动的时候切换图像,通过当前偏移量和上次偏移量的差值来计算图像的移动

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    CGFloat currentOffSetX = scrollView.contentOffset.x;
    CGFloat delata = currentOffSetX - self.lastOffSetX;
    int index = currentOffSetX / scrollView.width + 1;
    self.lastOffSetX = currentOffSetX;

    //先让图像往滑动方向移动2个屏幕
    self.guide.x += 2 * delata;
    //再让图像往回移动1个屏幕,并且设置动画效果
    [UIView animateWithDuration:0.25 animations:^{
        self.guide.x -= delata;
    }];
    //通过currentOffSetX来计算当前显示哪一张图片
    NSString *guideImageName = [NSString stringWithFormat:@"guide%d",index];
    self.guide.image = [UIImage imageNamed:guideImageName];
}

自定义布局对象

流水布局UICollectionViewFlowLayout继承于UICollectionViewLayout,通过自定义布局可以实现一些不同的排布方式

自定义继承于流水布局的布局对象

  • 流水布局对象有下面这个方法:通过调用super可以拿到所有cell的布局属性UICollectionViewLayoutAttributes,它们组成了一个数组,一个cell的布局属性对应一个数组的元素。可以通过更改布局属性的属性来调整cell的布局方式。
  • 其中的rect是指当前显示的区域
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    // 调用super来拿到所有cell的布局方式
    NSArray *attrsArray = [super layoutAttributesForElementsInRect:rect];
    // 遍历数组拿出每一个UICollectionViewLayoutAttributes类型的元素
    for (UICollectionViewLayoutAttributes *attrs in attrsArray) {

        // 可以更改cell的各种属性
        attrs.center.x = 100// 形变也可以改
        attrs.transform = CGAffineTransformMakeScale(0.5, 0.5);
    }
    return attrsArray;
}
  • UICollectionViewLayoutAttributes具体有如下属性
@property (nonatomic) CGRect frame;
@property (nonatomic) CGPoint center;
@property (nonatomic) CGSize size;
@property (nonatomic) CATransform3D transform3D;
@property (nonatomic) CGRect bounds NS_AVAILABLE_IOS(7_0);
@property (nonatomic) CGAffineTransform transform NS_AVAILABLE_IOS(7_0);
@property (nonatomic) CGFloat alpha;
@property (nonatomic) NSInteger zIndex; // default is 0
@property (nonatomic, getter=isHidden) BOOL hidden; // As an optimization, UICollectionView might not create a view for items whose hidden attribute is YES
@property (nonatomic, retain) NSIndexPath *indexPath;
  • 这个方法默认什么时候调用呢?刚加载collectionView的时候会调用,然后用力拖拽collectionView的时候会调用,而轻轻滑动的时候不会调用
  • 如何才能做到轻滑也能调用呢?比如项目做的左右滑动缩放效果,必须时刻调用这个方法来改变图片的大小
  • 需要实现下面这个方法,返回YES即可。这个方法的意思是当bounds改变的时候,是否要重新布局
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}
  • 如何停止滚动的时候,cell会自动往中间靠,起到类似分页pagingEnabled的效果?
  • 有两个方法,看起来类似,但是这个没有效果
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset{}
  • 我们要用的是这个方法:这个方法在collectionView将要停止滚动时给出了期望停止位置的点和加速度
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{
  • 这个方法什么时候调用?当collectionView将要停止滚动的时候调用
  • proposedContentOffset这个点就是滚动慢下来准备停了,那个最终会停的位置。(此处设定了滚动方向为水平,所以点的y值没用)
  • 要达到分页的效果,我们还是要拿到cell的布局属性来进行设置,因此需要手动调用
    layoutAttributesForElementsInRect方法。上面的方法里rect由系统传过来,但是这里的rect需要手动设定。(当然不设置rect也行,那就拿到所有cell的布局属性,这么做在cell多的时候会影响性能)
  • rect的x应该是最终collectionView停下来的时候的offset.x,根据上一条proposedContentOffset的描述,可以判断rect的x就是proposedContentOffset.x
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{
    CGFloat minMargin = MAXFLOAT;
    // 手动调用layoutAttributesForElementsInRect方法来获得cell的布局属性
    NSArray *attrsArray = [self layoutAttributesForElementsInRect:CGRectMake(proposedContentOffset.x, 0, self.collectionView.frame.size.width, self.collectionView.frame.size.height)];
    // 遍历属性数组,找出和cell中心点间距最小的间距,
    for (UICollectionViewLayoutAttributes *attrs in attrsArray) {
        CGFloat cellCenterX = attrs.center.x;
        CGFloat collectionViewCenterX = self.collectionView.frame.size.width / 2;
        CGFloat margin = cellCenterX - collectionViewCenterX - proposedContentOffset.x;
        // 遍历,遇到更小的margin就把最小间距设置为这个margin
        if (ABS(margin) <= ABS(minMargin)) {
            minMargin = margin;
        }
    }
    // 最后把期望的停止位置再偏移一个margin间距,就达到目的了
    proposedContentOffset.x += minMargin;
    return proposedContentOffset;
}

自定义继承于UICollectionViewLayout的布局对象

  • 这个布局方式就不是流水布局了,一切都按自定义来。
  • 在layoutAttributesForElementsInRect中,一切都是空的,UICollectionViewLayoutAttributes没有设置属性。需要我们自己实现这个方法,在这里设置好所有的布局然后返回
    • 为什么上面能拿到设置好的布局属性?因为上面的自定义布局继承于流水布局,它的父类流水布局已经把布局方式设置好了,只需要调用super就能拿到。
  • 下面是实现layoutAttributesForElementsInRect并且完成了让图片围成一个圈的步骤
    • 其中有获得collectionView中指定section中cell的个数的方法,indexPath和UICollectionViewLayoutAttributes的创建方法
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSMutableArray *attrsArray = [NSMutableArray array];
    // 这个方法获得collectionView中Section:0的cell数
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i < count; i ++) {
        // 根据i创建一个Section:0的indexPath,这样indexPath.row = i,indexPath.section = 0
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
        // 根据cell的indexPath为每个cell创建UICollectionViewLayoutAttributes
        UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

        // 设置计算cell的位置和size
        attrs.size = CGSizeMake(60,60);
        // 2.3.1 圆心
        CGFloat centerX = self.collectionView.frame.size.width * 0.5;
        CGFloat centerY = self.collectionView.frame.size.height * 0.5;      
        // 平分角度
        NSInteger count = [self.collectionView numberOfItemsInSection:0];
        CGFloat averageAngle = M_PI * 2 / count;        
        // 2.3.2 获取每个图片角度
        CGFloat angle = averageAngle * i;        
        // 半径
        CGFloat radius =  centerY  - 30;
        // 2.3.3 计算a b 值
        CGFloat a = sin(angle) * radius;
        CGFloat b = cos(angle) * radius;        
        attrs.center = CGPointMake(a + centerX,b + centerY);
        // 别忘了把设置好的UICollectionViewLayoutAttributes添加到数组中
        [attrsArray addObject:attrs];
    }
    return attrsArray;
}
  • 但是如果我们想切换布局,比如点击某个按钮让它变成流水布局,再点又变成圆形,光这么设置的话不够
  • 切换布局的时候会调用下面这个方法:需要告诉系统每个cell的位置,如果不实现,那就崩了
    • 把计算cell属性的代码全搬到这里来,然后返回就可以了。注意这里不同cell参照的是indexPath
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{   
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attrs.各种属性 = xxxx;
    return attrs;
}

竖直布局

如何做到如下的布局?

- 自定义布局,需要实现layoutAttributesForElementsInRect方法
- 注意这里的rect不用设置,不设置就意味着把所有cell的布局都设置好

  • 布局完上面6个的frame,后面的循环体只需要获取上面对应位置的布局属性
        }else if(i == 5){
            cellH = cellW;
            cellX = cellW;
            cellY = cellH;
        }else{
            UICollectionViewLayoutAttributes *lastLayoutAtt = self.layoutAtts[i - 6];
            CGRect lastFrm = lastLayoutAtt.frame;

            cellH = lastFrm.size.height;
            cellX = lastFrm.origin.x;
            cellY = lastFrm.origin.y + 2 * cellW;
  • 注意:这个collectionView可以拖动,而继承viewLayout的布局需要手动设置contentSize
  • 在这个方法里设置contentSize即可
- (CGSize)collectionViewContentSize{
    return CGSizeMake(self.collectionView.frame.size.width, row * cellH);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值