iOS 摩天轮动画效果的一两种实现思路,UICollectionView环形布局效果

motianlun.gif

如上配图,最近有项目需求,使用原生代码实现类似动画效果。还算是遇到的比较好玩的一个小效果。前后用了个把小时实现了这个效果。下面把大致实现思路和方式总结一下。


效果图一.png

看到这张UI效果图,你想怎么画它。容你思考1分钟,嘀嗒...嘀嗒...

emmm 想到了吧~ 我这里简述两种方式:

1. 创建9个控件,计算位置排布。

2. UICollectionView

本文中的实现,使用的是UICollectionView。至于第一种方式,预计要写的代码不够简洁,这里就不展示了,因为我没有写具体的代码。

开始~

既然使用UICollectionView自定义布局,创建9个单元格即可。简单~

重点:

继承UICollectionViewFlowLayout创建类,书写自定义布局代码。

- (void)prepareLayout {

[super prepareLayout];

 self.attributrAry = [NSMutableArray array];

 for(int i = 0; I < self.itemCount; i ++) {

        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];

        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];

        [self.attributrAry addObject:attrs];
    }
}
- (CGSize)collectionViewContentSize {
    return self.collectionView.frame.size;
}

- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return [self.attributrAry copy];
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {

    self.itemCount = (NSInteger)[self.collectionView numberOfItemsInSection:0];
    CGFloat radius = MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height) / 2;
    
    CGFloat center_x = self.collectionView.frame.size.width * 0.5;
    CGFloat center_y = self.collectionView.frame.size.height * 0.5;
    
    UICollectionViewLayoutAttributes *attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attris.size = CGSizeMake(85, 85);
    
    CGFloat angle = (2 * M_PI / self.itemCount) * indexPath.item - M_PI_2;
    CGFloat xx = center_x + cosf(angle) * (radius - 85 / 2);
    CGFloat yy = center_y + sinf(angle) * (radius - 85 / 2);
    attris.center = CGPointMake(xx, yy);
    
    return attris;
}

每个单元格位置计算核心代码:

CGFloat angle = (2 * M_PI / self.itemCount) * indexPath.item - M_PI_2;
    CGFloat xx = center_x + cosf(angle) * (radius - 85 / 2);
    CGFloat yy = center_y + sinf(angle) * (radius - 85 / 2);
    attris.center = CGPointMake(xx, yy);

观看上面第二张插图,我们需要把第一个单元格放在正上方。(2 * M_PI / self.itemCount) * indexPath.item;这句代码书写后,你会看到第一个单元格在最右边,所以我这里在后面减了一个M_PI_2,用来调整位置。

以上即是实现布局的代码,拿来创建对应的UICollectionView。

- (UICollectionView *)turnDataColView {
    if (!_turnDataColView){
        
        HXTurnCircleLayout *layout = [[HXTurnCircleLayout alloc] init];
        
        _turnDataColView = [[UICollectionView alloc] initWithFrame:CGRectMake((SCREEN_WIDTH - 334 - 30) / 2, 95 - 20, 334 + 30, 334 + 30) collectionViewLayout:layout];
        _turnDataColView.delegate = self;
        _turnDataColView.dataSource = self;
        [_turnDataColView registerClass:[HXTurnDataCell class] forCellWithReuseIdentifier:@"HXTurnDataCell"];
        
        _turnDataColView.showsHorizontalScrollIndicator = NO;
        _turnDataColView.showsVerticalScrollIndicator = NO;
        
        _turnDataColView.backgroundColor = [UIColor clearColor];
        
        _turnDataColView.userInteractionEnabled = NO;
    }
    return _turnDataColView;
}

把UICollectionView的代理方法添加好后,运行吧~

是不是发现,效果已经实现了。那么开始第二步,摩天轮的效果动画了。

这次思考3分钟吧。顺便再回头观察一下第一张配图,仔细看,有发现什么细节吗?
......
对的,底部在转动,上层的控件也在动,但上层的方向始终是垂直向上的。就像摩天轮一下,人坐在里面不能头朝下的,对吧~

到这里,好像觉得创建9个小控件,然后让这些小控件在一个圆周上做移动会比较好一些,改变每一个小控件的center。
emmm 这是一种实现方式,但是我数学不好,没有去这么弄。

话说回来,使用的是UICollectionView,底层控件的转动效果让UICollectionView绕Z轴转动就可以了。所以给UICollectionView添加了一个动画,0.1秒转动1度吧。

[UIView animateWithDuration:0.1 delay:0.0f options:options animations:^{

        self.turnDataColView.transform = CGAffineTransformRotate(self.turnDataColView.transform, M_PI / 180);
     
    }completion:^(BOOL finished) {
  
    }];

效果不错,开始转动了。

为了让它保持持续转动,我们添加以下代码。(isStart用来标记开始/结束)

- (void)turnWithOptions:(UIViewAnimationOptions)options {

    [UIView animateWithDuration:0.1 delay:0.0f options:options animations:^{

        self.turnDataColView.transform = CGAffineTransformRotate(self.turnDataColView.transform, M_PI / 180);

    }completion:^(BOOL finished) {

       if (finished) {

           if (self.isStart) {

                [self turnWithOptions:UIViewAnimationOptionCurveLinear];

            } else if (options != UIViewAnimationOptionCurveEaseOut) {

                [self turnWithOptions:UIViewAnimationOptionCurveEaseOut];
            }
       }
    }];
}

那单元格怎么弄?先想一想,别着急看下面一段文字。

OK~
请在脑海里模拟下面一段文字的内容,当然你也可以拿笔简单在纸上画一下。

以一个单元格为准,我们使用最顶部的那个。假如父控件是顺时针转动(上面代码的效果)。单元格随着父控件转动。注意我们的目的,是让单元格保持垂直。那么,我们选过程中一个比较特殊的位置,即父控件转动了45度的时候,这个单元格的倾斜也是45度,此时我们把单元格逆时针转动45度,它刚好垂直。再分别选着几个位置,90度,180度...,可得到相同的效果。

明白了吗?

结论:我们的父控件每转动1度,我们把子控件向反方向转动1度,就是我们要实现的效果。

那就相当简单了,相信我不把代码贴出来,你们也知道代码怎么写了。

到此,我们效果已经实现了。感谢猿同胞们在百忙中浏览到这篇文章。
感恩常在~

...
...
...

结论的实现代码,在父控件的转动效果中添加一句代码(这里不是为了凑字数 /斜眼笑):

- (void)turnWithOptions:(UIViewAnimationOptions)options {

    [UIView animateWithDuration:0.1 delay:0.0f options:options animations:^{

        self.turnDataColView.transform = CGAffineTransformRotate(self.turnDataColView.transform, M_PI / 180);
        
        self.cell0.transform = CGAffineTransformRotate(self.cell0.transform, -M_PI / 180);

    }completion:^(BOOL finished) {

       if (finished) {

           if (self.isStart) {

                [self turnWithOptions:UIViewAnimationOptionCurveLinear];

            } else if (options != UIViewAnimationOptionCurveEaseOut) {

                [self turnWithOptions:UIViewAnimationOptionCurveEaseOut];
            }
       }
    }];
}

开始转动动画:

- (void)startTurnAni {
    if (!self.isStart) {
        self.isStart = YES;
        
        NSIndexPath *idxP0 = [NSIndexPath indexPathForItem:0 inSection:0];
        HXTurnDataCell *cell0 = (HXTurnDataCell *)[self.turnDataColView cellForItemAtIndexPath:idxP0];
        self.cell0 = cell0;
        
        [self turnWithOptions:UIViewAnimationOptionCurveEaseIn];
    }
}

结束转动动画:

- (void)stopTurnAni {
    self.isStart = NO;
    self.turnDataColView.transform = CGAffineTransformIdentity;
    self.cell0.transform = CGAffineTransformIdentity;
}

这次真的结尾啦~
感谢~

你可能感兴趣的:(iOS 摩天轮动画效果的一两种实现思路,UICollectionView环形布局效果)