废话开篇:使用 UICollectionView 简单完成一个图片阅读效果。

一、效果展现

利用 UICollectionView 实现图片浏览效果

二、完成思路

1、封装 UICollectionViewLayout ,完成内部 UICollectionViewCell 的布局。

UICollectionViewLayout 在封装瀑布流的时分会用到,并且担负着中心功能的完成。其实从另一个视点也能够把 UICollectionViewLayout 了解成“数据源”,这个数据不是 UI 的展现项,而是 UI 的尺寸项。在内部进行预核算 UICollectionViewCellframe

UICollectionViewUIScrollView的子类,只不过,它里面子控件经过“重用”机制完成了优化,一些复用的杂乱逻辑还是扔给了体系处理。开发过程中只担任对 UICollectionViewLayout 什么时分需求干什么进行自定义即可。

2、获取 UICollectionView 目前可见的 cells,经过进行缩放、旋转改换完成一些简单的效果。
3、自定义 cell ,修正锚点属性。

三、代码整理

1、PhotoBrowseViewLayout

这儿有一点需求注意的,在 UICollectionViewLayout 内部会进行核算每一个 cellframe,在核算过程中,为了更好的展现旋转改换,cell 的锚点会修正到 (0.5,1),那么,为了确保 UI 展现不变,那么,就需求将 y 添加 cell 高度的一半

#import "PhotoBrowseViewLayout.h"
@interface PhotoBrowseViewLayout()
@property(nonatomic,strong) NSMutableArray * attributeArray;
@property(nonatomic,assign) CGFloat cellWidth;
@property(nonatomic,assign) CGFloat cellHeight;
@property(nonatomic,assign) CGFloat sep;
@property(nonatomic,assign) int showCellNum;
@end
@implementation PhotoBrowseViewLayout
- (instancetype)init
{
  if (self = [super init]) {
    self.sep = 20;
    self.showCellNum = 2;
  }
  return self;
}
//核算cell的frame
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
  if (self.cellWidth == 0) {
    self.cellWidth = **self**.collectionView.frame.size.width * 2 / 3.0;
  }
  if (self.cellHeight == 0) {
    self.cellHeight = self.collectionView.frame.size.height;
  }
  CGFloat x = (self.cellWidth + self.sep) * indexPath.item;
    //这儿y值需求进行如此设置,以抵抗cell修正锚点导致的UI紊乱
  CGFloat y = self.collectionView.frame.size.height / 2.0;
  UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
  attrs.frame = CGRectMake(x, y, self.cellWidth, self.cellHeight);
  return attrs;
}
//预备布局
- (void)prepareLayout
{
  [super prepareLayout];
  NSInteger count = [self.collectionView numberOfItemsInSection:0];
  for (int i = 0; i <count; i++) {
    UICollectionViewLayoutAttributes *attris = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
    [self.attributeArray addObject:attris];
  }
}
//返回全部cell的布局调集
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
  return self.attributeArray;
}
//一次性供给UICollectionView 的 contentSize
- (CGSize)collectionViewContentSize
{
  NSInteger count = [self.collectionView numberOfItemsInSection:0];
  CGFloat maxWidth = count * self.cellWidth + (count - 1) * self.sep;
  return CGSizeMake(maxWidth, 0);
}
- (NSMutableArray *)attributeArray
{
  if (!_attributeArray) {
    _attributeArray = [[NSMutableArray alloc] init];
  }
  return _attributeArray;
}
@end
2、PhotoBrowseCollectionViewCell

这儿主要是进行了锚点修正(0.5,1),代码很简单。

#import "PhotoBrowseCollectionViewCell.h"
@interface PhotoBrowseCollectionViewCell()
@property(nonatomic,strong) UIImageView * imageView;
@end
@implementation PhotoBrowseCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame
{
  if (self = [super initWithFrame:frame]) {
        //设置(0.5,1)锚点,以底部中点为轴旋转
    self.layer.anchorPoint = CGPointMake(0.5, 1);
    self.layer.masksToBounds = YES;
    self.layer.cornerRadius = 8;
  }
  return self;
}
- (void)setImage:(UIImage *)image
{
  self.imageView.image = image;
}
- (UIImageView *)imageView
{
  if (!_imageView) {
    _imageView = [[UIImageView alloc] init];
    _imageView.contentMode = UIViewContentModeScaleAspectFill;
    _imageView.backgroundColor = [UIColor groupTableViewBackgroundColor];
    [self.contentView addSubview:_imageView];
  }
  return _imageView;
}
- (void)layoutSubviews
{
  [super layoutSubviews];
  self.imageView.frame = **self**.contentView.bounds;
}
@end
3、CollectPhotoBrowseView

CollectPhotoBrowseView 担任进行一些 cell 的图形改换。

#import "CollectPhotoBrowseView.h"
#import "PhotoBrowseCollectionViewCell.h"
#import "PhotoBrowseViewLayout.h"
@interface CollectPhotoBrowseView()<UICollectionViewDelegate,UICollectionViewDataSource>
@property(nonatomic,strong) UICollectionView * photoCollectView;
@end
@implementation CollectPhotoBrowseView
- (instancetype)initWithFrame:(CGRect)frame
{
  if (self = [super initWithFrame:frame]) {
    [self makeUI];
  }
  return self;
}
- (void)makeUI{
    //设置自定义 UICollectionViewLayout
  PhotoBrowseViewLayout * photoBrowseViewLayout = [[PhotoBrowseViewLayout alloc] init];
  self.photoCollectView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:photoBrowseViewLayout];
  self.photoCollectView.delegate = self;
  self.photoCollectView.dataSource = self;
  [self.photoCollectView registerClass:[PhotoBrowseCollectionViewCell class] forCellWithReuseIdentifier:@"CELL"];
  self.photoCollectView.showsHorizontalScrollIndicator = NO;
  [self addSubview:self.photoCollectView];
    //执行一次可见cell的图形改换
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [self visibleCellTransform];
  });
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
  return 20;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
  PhotoBrowseCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CELL" forIndexPath:indexPath];
  [cell setImage: [UIImage imageNamed:[NSString stringWithFormat:@"fd%ld",indexPath.item % 3 + 1]]];
  return cell;
}
#pragma mark - 翻滚进行图形改换
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    //滑动的时分,动态进行cell图形改换
  [self visibleCellTransform];
}
#pragma mark - 图形改变
- (void)visibleCellTransform
{
    //获取当时可见cell的indexPath调集
  NSArray * visibleItems = [self.photoCollectView indexPathsForVisibleItems];
    //遍历动态进行图形改换
  for (NSIndexPath * visibleIndexPath in visibleItems) {
    UICollectionViewCell * visibleCell = [self.photoCollectView cellForItemAtIndexPath:visibleIndexPath];
    [self transformRotateWithView:visibleCell];
  }
}
//进行图形转换
- (void)transformRotateWithView:(UICollectionViewCell *)cell
{
    //获取cell在当时视图的方位
  CGRect rect = [cell convertRect:cell.bounds toView:self];
    //核算当时cell中轴线与中轴线的距离的比值
  float present = ((CGRectGetMidX(rect) - self.center.x) / (self.frame.size.width / 2.0));
    //依据方位设置选择视点
  CGFloat radian = (M_PI_2 / 15) * present;
    //图形视点改换
  CGAffineTransform transformRotate = CGAffineTransformIdentity;
  transformRotate = CGAffineTransformRotate(transformRotate, radian);
    //图形缩放改换
  CGAffineTransform transformScale = CGAffineTransformIdentity
  transformScale = CGAffineTransformScale(transformScale,1 - 0.2 * fabs(present),1 - 0.2 * fabsf(present));
    //合并改换
  cell.transform = CGAffineTransformConcat(transformRotate,transformScale);
}
@end

四、总结与考虑

UICollectionView 也是 View,只不过体系为了更好的服务于开发者,快速高效的完成某些开发场景,进行了封装与优化,将杂乱的逻辑独自的封装成一个管理类,这儿便是 UICollectionViewLayout,交给它去做一些固定且杂乱的逻辑。所以,自定义杂乱UI的时分,就需求将功能模块足够细化,以完成更好的代码衔接。代码拙劣,大神勿笑[抱拳][抱拳][抱拳]