UICollectionView 實現圖片瀏覽效果

一、效果展示

廢話開篇:利用 UICollectionView 簡單實現一個圖片瀏覽效果。

二、實現思路

1.封裝 UICollectionViewLayout ,實現內部 UICollectionViewCell 的佈局。

UICollectionViewLayout 在封裝瀑佈流的時候會用到,而且擔負著核心功能的實現。其實從另一個角度也可以把 UICollectionViewLayout 理解成“數據源”,這個數據不是 UI 的展示項,而是 UI 的尺寸項。在內部進行預計算 UICollectionViewCell 的 frame。

UICollectionView 是 UIScrollView的子類,隻不過,它裡面子控件通過“重用”機制實現瞭優化,一些復用的復雜邏輯還是扔給瞭系統處理。開發過程中隻負責對 UICollectionViewLayout 什麼時候需要幹什麼進行自定義即可。

2.獲取 UICollectionView 目前可見的 cells,通過進行縮放、旋轉變換實現一些簡單的效果。

3、自定義 cell ,修改錨點屬性。

三、代碼整理

1、PhotoBrowseViewLayout

這裡有一點需要註意的,在 UICollectionViewLayout 內部會進行計算每一個 cell 的 frame,在計算過程中,為瞭更好的展示旋轉變換,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的時候,就需要將功能模塊足夠細化,以實現更好的代碼銜接。

以上就是UICollectionView 實現圖片瀏覽效果的詳細內容,更多關於UICollectionView 圖片瀏覽的資料請關註WalkonNet其它相關文章!

推薦閱讀: