android horizontalscrollview 3d,使用UICollectionView+CATransform3D实现3D滑动效果

首先来看一下效果图:

6cef86e566cb?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

效果图.gif

看完效果图觉得还不错那就继续往下看哦👇

实现思路

抛开滑动的3D效果,我们先来实现普通的分页滑动效果。

要做这种卡片式的分页滑动,最先想到的应该就是用UICollectionView来实现了。但是UICollectionView的分页只能相对于本身的frame来做分页,要根据cell 的大小来做分页就需要来自定义分页了。

现在来解决第一个问题:自定义分页。

自定义分类想到两种方式:

1 、一种是开启UICollectionView的分页,把UICollectionView的大小设置成分页的一个页面大小,然后再把clipsToBounds设置成NO,这样就可以使用自带的分页效果,并且被遮挡的部分也能显示出来了,但是这样 在当前页的边上部分是不能接受事件的,所以还需要自己处理事件接收。

2 、实现UIScrollView的协议方法,监听滑动结束,计算出滑动到的当前页位置,然后自定义结束位置,将得到的当前页面居中。

第二种方法只需要在滑动结束后,计算需要居中显示的页面,使用起来相对更加灵活。所以使用第二种来实现✨

首先新建一个UICollectionView的子类,定义好需要使用的属性。

@interface DCollectionView : UICollectionView

@property (nonatomic, retain) NSArray *imgDatas;

@property (nonatomic, assign) NSInteger currentIndex;

@end

.m中实现协议方法

- (instancetype)initWithFrame:(CGRect)frame {

_layout = [[LayoutView alloc]init];

_layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

self = [super initWithFrame:frame collectionViewLayout:_layout];

if(self){

[self _initView];

}

return self;

}

- (void)_initView {

_curIndex = 1;

self.delegate = self;

self.dataSource = self;

self.pagingEnabled = NO;

[self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"UICollectionViewCell"];

}

#pragma mark collection协议

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

return _imgDatas.count;

}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"UICollectionViewCell" forIndexPath:indexPath];

cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:_imgDatas[indexPath.row]]];

cell.layer.masksToBounds = YES;

cell.layer.cornerRadius = 8;

return cell;

}

// 设置每个块的宽、高

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

return CGSizeMake(itemWidth, itemHight);

}

// 四个边缘的间隙

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

return UIEdgeInsetsMake(0, 5, 0, 5);

}

UICollectionView继承自UIScrollView,在UIScrollViewDelegate的协议方法scrollViewDidEndDragging中,根据滑动减速停止时的位置计算当前需要居中的页面。

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {

NSInteger scrIndex = scrollView.contentOffset.x/(itemWidth + 10);

[self scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:scrIndex + 1 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];

_curIndex = scrIndex + 1;

}

这样在滑动完成后继续滑动到计算出的cell页面,但这样只实现了在当手离开屏幕时,页面还在滑动时的效果,还需要在scrollViewDidEndDragging中计算出居中的页面。

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {

NSInteger scrIndex = scrollView.contentOffset.x/(itemWidth + 10);

// 当停止拖拽时,view不在滑动时才执行,还在滑动则调用scrollViewDidEndDecelerating

if(!decelerate){

[self scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:scrIndex + 1 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];

_curIndex = scrIndex + 1;

}

}

最后再给点击cell,将点击的cell滑动居中即可:

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

[self scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];

_curIndex = indexPath.row;

}

自定义分页实现完成,下面在滑动中使用CATransform3D加上3D效果

在上面UICollectionView的初始化方法中可以看到,collectionViewLayout是使用的自定义UICollectionViewFlowLayout。

下面就在子类化UICollectionViewFlowLayout中来实现3D滑动的效果。

主要在layoutAttributesForElementsInRect协议方法中,给视图元素利用CATransform3DMakeScale加上效果。那这个 layoutAttributesForElementsInRect协议方法是做什么的呢?

- (nullable NSArray<__kindof uicollectionviewlayoutattributes> *)layoutAttributesForElementsInRect:(CGRect)rect;

这个方法返回一个数组,是返回存放在CGRect范围内所有视图的布局属性的一个数组。下面实现方法:

// 返回存放在CGRect范围内所有视图的布局属性的一个数组

- (nullable NSArray<__kindof uicollectionviewlayoutattributes> *)layoutAttributesForElementsInRect:(CGRect)rect {

NSArray *attArray = [super layoutAttributesForElementsInRect:rect];

CGRect visRect;

visRect.origin = self.collectionView.contentOffset;

visRect.size = self.collectionView.bounds.size;

for (UICollectionViewLayoutAttributes *layoutAttribute in attArray) {

CGFloat distance = CGRectGetMidX(visRect) - layoutAttribute.center.x;

CGFloat norDistance = fabs(distance/ActiveDistance);

CGFloat zoom = 1- ScaleFactor * norDistance;

layoutAttribute.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0);

layoutAttribute.zIndex = 1;

}

return attArray;

}

完成上面那个方法后,会发现3D效果是有了,但是滑动时并没有实时刷新layout,还需要实现shouldInvalidateLayoutForBoundsChange这个协议:

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {

return YES;

}

到此效果图上面的效果已经实现了。最后给上demo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值