UICollectionView简单实现3D滑动居中的卡片

1,152 阅读1分钟

主要实现的是滑动居中和点击居中两个小功能

1主要实现:自定义UICollectionViewFlowLayout代码如下

// // Card3DViewFlowLayout.m // Card3D // // Created by wangcj on 2020/9/9. // Copyright © 2020 wangcj. All rights reserved. //

#import "Card3DViewFlowLayout.h"

CGFloat ActiveDistance = 400; //垂直缩放除以系数 ,越小缩放越厉害
CGFloat ScaleFactor = 1;   //缩放系数  越大缩放越大

@implementation Card3DViewFlowLayout

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
  CGRect targectRect = CGRectMake(proposedContentOffset.x, 0.0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
NSArray * attriArray = [super layoutAttributesForElementsInRect:targectRect];
CGFloat horizontalCenterX = proposedContentOffset.x + ([UIScreen mainScreen].bounds.size.width);
CGFloat offsetAdjustment = CGFLOAT_MAX;
for (UICollectionViewLayoutAttributes * layoutAttributes in attriArray) {
    CGFloat itemHorizontalCenterX = layoutAttributes.center.x;
    if (fabs(itemHorizontalCenterX-horizontalCenterX) < fabs(offsetAdjustment)) {
        offsetAdjustment = itemHorizontalCenterX - horizontalCenterX;
    }
}
return CGPointMake(proposedContentOffset.x , proposedContentOffset.y);
}

 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray * array = [super layoutAttributesForElementsInRect:rect];
CGRect  visibleRect = CGRectZero;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;
for (UICollectionViewLayoutAttributes *attributes  in array) {
    CGFloat distance = CGRectGetMidX(visibleRect) - attributes.center.x;
    CGFloat normalizedDistance = fabs(distance / ActiveDistance);
    CGFloat zoom = 1 - ScaleFactor * normalizedDistance;
    attributes.transform3D = CATransform3DMakeScale(zoom, zoom, zoom);
}
return array;
}

//防止报错 先复制attributes

- (NSArray *)getCopyOfAttributes:(NSArray *)attributes
{
NSMutableArray *copyArr = [NSMutableArray new];
for (UICollectionViewLayoutAttributes *attribute in attributes) {
    [copyArr addObject:[attribute copy]];
}
return copyArr;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return true;
}


@end

2:vc中的主要实现代码如下

// // Card3DViewController.m // Card3D // // Created by wangcj on 2020/9/9. // Copyright © 2020 wangcj. All rights reserved. //

#import "ViewController.h"
#import "Card3DCell.h"
#import "Card3DViewFlowLayout.h"

#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
#define SCREEN_RATE  ([UIScreen mainScreen].bounds.size.width/375.0)
static float imageHeight = 120;
static NSString * const cellID = @"CollectionViewCell";

@interface ViewController ()<UICollectionViewDelegate,UICollectionViewDataSource>

@property (nonatomic,strong) UICollectionView * collectionView;
@property (nonatomic, assign) NSIndexPath * selectedIndex ;
@property (nonatomic,strong) NSMutableArray * modelArray;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
NSArray *appArray = [[self getDict] objectForKey:@"dictInfo"];
for (int i = 0; i < appArray.count; i++) {
    [self.modelArray addObject:[NSString stringWithFormat:@"%i.png",i + 1]];
}
[self createCollectionView];
}

 - (void)createCollectionView {
CGFloat pading = 30 * SCREEN_WIDTH/375;
Card3DViewFlowLayout * layout = [[Card3DViewFlowLayout alloc]init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.minimumLineSpacing = pading;
layout.minimumInteritemSpacing = pading;
layout.sectionInset = UIEdgeInsetsMake(0, (UIScreen.mainScreen.bounds.size.width - 120) / 2.0, 0, (UIScreen.mainScreen.bounds.size.width - 120) / 2.0);
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 100, [UIScreen mainScreen].bounds.size.width, imageHeight * SCREEN_RATE) collectionViewLayout:layout];
_collectionView.tag = 33;
_collectionView.dataSource = self;
_collectionView.delegate = self;
_collectionView.bounces = NO;
_collectionView.alwaysBounceHorizontal = NO;
_collectionView.alwaysBounceVertical = NO;
_collectionView.backgroundColor = [UIColor grayColor];
_collectionView.showsHorizontalScrollIndicator = NO;
_collectionView.showsVerticalScrollIndicator = NO;
[self.view addSubview:_collectionView];
[_collectionView registerClass:[Card3DCell class] forCellWithReuseIdentifier:cellID];
 }

  - (NSDictionary *)getDict {
NSString * string  = @"{\"dictInfo\":[{\"title\":\"你好啊\",\"url\":\"1.png\"},{\"title\":\"你好啊\",\"url\":\"2.png\"},{\"title\":\"你好啊\",\"url\":\"3.png\"},{\"title\":\"你好啊\",\"url\":\"4.png\"},{\"title\":\"你好啊\",\"url\":\"5.png\"},{\"title\":\"你好啊\",\"url\":\"6.png\"},{\"title\":\"是很好\",\"url\":\"7.png\"}]}";
NSDictionary *infoDic = [self dictionaryWithJsonString:string];
return infoDic;

}

-(NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString {
if (jsonString == nil) {
    return nil;
}
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData
                                                    options:NSJSONReadingMutableContainers
                                                      error:&err];
if(err)
{
    NSLog(@"json解析失败:%@",err);
    return nil;
}
return dic;
}

 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.modelArray.count;
}

  - (NSMutableArray *)modelArray {
if (!_modelArray) {
    _modelArray = [NSMutableArray array];
}
return _modelArray;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
NSString *img = self.modelArray[indexPath.row];

Card3DCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
[cell configCell:img];
return cell;
 }

// 返回每个item的大小

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat CWidth = 120 * SCREEN_RATE;
CGFloat CHeight = 120 * SCREEN_RATE;
return CGSizeMake(CWidth, CHeight);
}

#pragma mark - UICollectionViewDelegate点击事件

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

CGFloat collectionViewHeight = CGRectGetWidth(self.collectionView.frame);
UICollectionViewCell * cell =  [self.collectionView cellForItemAtIndexPath:indexPath];
CGPoint offset = CGPointMake(cell.center.x - collectionViewHeight/2, 0);
[self.collectionView setContentOffset:offset animated:YES];
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
 }

//居中

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
CGPoint originalTargetContentOffset = CGPointMake(targetContentOffset->x, targetContentOffset->y);
CGPoint targetCenter = CGPointMake(originalTargetContentOffset.x + CGRectGetWidth(self.collectionView.bounds)/2, CGRectGetHeight(self.collectionView.bounds) / 2);
NSIndexPath *indexPath = nil;
NSInteger i = 0;
while (indexPath == nil) {
    targetCenter = CGPointMake(originalTargetContentOffset.x + CGRectGetWidth(self.collectionView.bounds)/2 + 10*i, CGRectGetHeight(self.collectionView.bounds) / 2);
    indexPath = [self.collectionView indexPathForItemAtPoint:targetCenter];
    i++;
}
self.selectedIndex = indexPath;
//这里用attributes比用cell要好很多,因为cell可能因为不在屏幕范围内导致cellForItemAtIndexPath返回nil
UICollectionViewLayoutAttributes *attributes = [self.collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath];
if (attributes) {
    *targetContentOffset = CGPointMake(attributes.center.x - CGRectGetWidth(self.collectionView.bounds)/2, originalTargetContentOffset.y);
} else {

}

}

 @end