自定义collectionViewFlowlayout

这次回学校做毕业设计准备毕业了,突然觉得时间过的好快啊,忙完了之后今天终于有时间写点东西了.然后就写了这个自定义collectionViewFlowlayout.有篇博客写的很好,地址先给大家看看效果图吧.github下载地址

录屏2.gif

首先先定义一个Model,里面有3个属性,分别是图片的url,图片的宽,图片的高.

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface Model : NSObject
@property(nonatomic,copy)NSString * img;
@property(nonatomic,assign)CGFloat w;
@property(nonatomic,assign)CGFloat h;
@end
#import "Model.h"
@implementation Model
@end

我在网上找了个plist文件,里面就有宽,高和url.如图:


屏幕快照 2016-06-01 下午4.05.41.png

然后自定义一个flowlayout.

XMHFlowLayout.h

#import <UIKit/UIKit.h>
@class XMHFlowLayout;
@protocol XMHFlowLayoutDelegate <NSObject>
/**
 *  这个代理方法用于在viewcontroller中通过Width来计算高度
 *
 *  @param Flow      flowlayout
 *  @param width     图片的宽
 *  @param indexPath indexPath
 *
 *  @return 图片的高
 */
-(CGFloat)Flow:(XMHFlowLayout *)Flow heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath*)indexPath;

@end

@interface XMHFlowLayout : UICollectionViewFlowLayout
@property(nonatomic,assign)UIEdgeInsets sectionInset;
@property(nonatomic,assign)CGFloat rowMagrin;//行间距
@property(nonatomic,assign)CGFloat colMagrin;//列间距
@property(nonatomic,assign)CGFloat colCount;//多少列
@property(nonatomic,weak)id<XMHFlowLayoutDelegate>degelate;
@end

XMHFlowLayout.m

#import "XMHFlowLayout.h"

@interface XMHFlowLayout ()
@property(nonatomic,retain)NSMutableDictionary * maxYdic;
@property (nonatomic, strong) NSIndexPath *pinchedItem;
@property (nonatomic) CGSize pinchedItemSize;
@end

@implementation XMHFlowLayout
-(NSMutableDictionary *)maxYdic
{
    if (!_maxYdic) {
        self.maxYdic = [[NSMutableDictionary alloc] init];
    }
    return _maxYdic;
}
-(instancetype)init
{
    if (self=[super init]) {
        self.colMagrin = 10;
        self.rowMagrin = 10;
        self.sectionInset = UIEdgeInsetsMake(40, 10, 10, 10);
        self.colCount = 2;
    }
    return self;
}
-(void)prepareLayout
{
    [super prepareLayout];
}

-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}
-(CGSize)collectionViewContentSize
{
    __block NSString * maxCol = @"0";
    //找出最长的列
    [self.maxYdic enumerateKeysAndObjectsUsingBlock:^(NSString * column, NSNumber *maxY, BOOL *stop) {
        if ([maxY floatValue]>[self.maxYdic[maxCol] floatValue]) {
            maxCol = column;
        }
    }];
    return CGSizeMake(0, [self.maxYdic[maxCol] floatValue]);
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    __block NSString * minCol = @"0";
    //找出最短的列
    [self.maxYdic enumerateKeysAndObjectsUsingBlock:^(NSString * column, NSNumber *maxY, BOOL *stop) {
        if ([maxY floatValue]<[self.maxYdic[minCol] floatValue]) {
            minCol = column;
        }
    }];
    //    计算宽度
    CGFloat width = (self.collectionView.frame.size.width-self.sectionInset.left-self.sectionInset.right-(self.colCount - 1)*self.colMagrin)/self.colCount;
    //    计算高度
    CGFloat hight = [self.degelate Flow:self heightForWidth:width atIndexPath:indexPath ];
    
    CGFloat x = self.sectionInset.left + (width + self.colMagrin)* [minCol intValue];
    CGFloat y =[self.maxYdic[minCol] floatValue]+self.rowMagrin;
    //   将之前的字典里每列对应得y的值加上高度,跟新每列最大的y值
    self.maxYdic[minCol] = @(y+hight);
    
    //    计算位置
    UICollectionViewLayoutAttributes * attri =[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attri.frame = CGRectMake(x, y, width, hight);
    return attri;
}
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    for(int i = 0;i<self.colCount;i++)
    {
        NSString * col = [NSString stringWithFormat:@"%d",i];
        self.maxYdic[col] = @0;
    }
    NSMutableArray * array = [NSMutableArray array];
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i < count; i++) {
        UICollectionViewLayoutAttributes * attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        [array addObject:attrs];
    }
    return  array;
}
@end

这里要仔细说说这里的原理.在Viewcontroller里面实现代理方法,代理方法实现如下:

-(CGFloat)Flow:(XMHFlowLayout *)Flow heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath*)indexPath{
    Model *model = self.dataArr[indexPath.row];
    return  model.h/model.w*width;
}

因为没列图片的宽度都是固定的,而高度也就是根据图片的宽高比得到的.
我们在这个代理方法和XMHFlowLayout.m中的-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath方法中打上断电试试.
会看见代理方法和这个方法之间不断的来回跳,因为这个方法中就用到了代理方法嘛,因为数据源中有50个元素,所以就会这样跳50次.这里我设置的是2列,那么字典里面有2个元素,分别代表第一列的高度和第二列的高度,每次获取到新的图片的高度后往比较低的那一列里面加,计算完成之后,那么这个collectionView的size的height就是最大的列的高度.

Viewcontroller.m

#import "ViewController.h"
#import "PhotoView.h"
#import "CollectionCell.h"
#import "XMHFlowLayout.h"
#import "Model.h"
#import "MJExtension.h"
#define SCREEN_HEIGHT     ([[UIScreen mainScreen] bounds].size.height)
#define SCREEN_WIDTH      ([[UIScreen mainScreen] bounds].size.width)
@interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate,XMHFlowLayoutDelegate,didRemovePictureDelegate>
@property (strong, nonatomic) IBOutlet UICollectionView *collection;
@property(nonatomic,strong)NSMutableArray * dataArr;
@property (nonatomic, assign)CGRect transformedFrame;
@property (nonatomic, strong)UIImageView *lookImg;
@property (nonatomic , strong)PhotoView* photoView;
@property (nonnull, strong)XMHFlowLayout *layOut;
@end

@implementation ViewController

-(NSMutableArray *)dataArr{
    if (!_dataArr) {
        _dataArr = [NSMutableArray array];
    }
    return _dataArr;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.navigationController.navigationBar.hidden = YES;
    _collection.delegate = self;
    _collection.dataSource = self;
    _layOut = [[XMHFlowLayout alloc] init];
    _layOut.degelate =self;
    [_collection setCollectionViewLayout:_layOut];
    
    //初始化数据
    NSArray * arr = [Model objectArrayWithFilename:@"1.plist"];
    [self.dataArr addObjectsFromArray:arr];

}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}

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

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"cell";
    CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    [cell setModel:_dataArr[indexPath.row]];
    NSLog(@"%@",NSStringFromCGRect(cell.frame));
    return cell;
}

-(CGFloat)Flow:(XMHFlowLayout *)Flow heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath*)indexPath{
    Model *model = self.dataArr[indexPath.row];
    return  model.h/model.w*width;
}

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    
    if (_lookImg) {
        return;
    }
    Model *model = _dataArr[indexPath.row];
    CollectionCell *cell = (CollectionCell *)[collectionView cellForItemAtIndexPath:indexPath];
    _transformedFrame = [cell convertRect:cell.image.frame toView:[UIApplication sharedApplication].keyWindow];
    _lookImg = [[UIImageView alloc]initWithFrame:_transformedFrame];
    _lookImg.image = cell.image.image;
    [[UIApplication sharedApplication].keyWindow addSubview:_lookImg];
    [UIView animateWithDuration:0.1 animations:^{
        _lookImg.frame = CGRectMake(10, 40, SCREEN_WIDTH - 20, model.h / model.w * (SCREEN_WIDTH - 20));
        self.view.alpha = 0;
        
    } completion:^(BOOL finished) {
        _photoView = [[PhotoView alloc]initWithFrame:[UIApplication sharedApplication].keyWindow.frame];
        [_photoView initWithPicArray:_dataArr picNo:indexPath.row];
        _photoView.removeDelegate = self;
        [[UIApplication sharedApplication].keyWindow addSubview:_photoView];
        
    }];
}

-(void)didremovePicture:(NSMutableArray *)shopArr{
    [[UIApplication sharedApplication]setStatusBarHidden:NO];
    NSInteger i =   _photoView.scrollView.contentOffset.x / SCREEN_WIDTH;
    CollectionCell *cell = (CollectionCell *)[_collection cellForItemAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
    _transformedFrame = [cell.superview convertRect:cell.frame toView:[UIApplication sharedApplication].keyWindow];
    _lookImg.image = cell.image.image;
    [UIView animateWithDuration:0.1 animations:^{
        self.view.alpha = 1;
        _lookImg.frame = _transformedFrame;
    } completion:^(BOOL finished) {
        [_lookImg removeFromSuperview];
        _lookImg = nil;
        _dataArr = [NSMutableArray arrayWithArray:shopArr];
        [_collection reloadData];
    }];
}
-(BOOL)prefersStatusBarHidden
{
    return YES;
}

-(void)deletePicture:(NSMutableArray *)dataArr
{
    _dataArr = dataArr;
    [_collection reloadData];
}

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

@end

PhotoView.h

#import <UIKit/UIKit.h>
#import "Model.h"

@protocol didRemovePictureDelegate <NSObject>

-(void)didremovePicture:(NSMutableArray *)dataArr;
-(void)deletePicture:(NSMutableArray *)dataArr;
@end

@interface PhotoView : UIView

@property (nonatomic, strong)UIScrollView *scrollView;
@property (nonatomic, assign)id<didRemovePictureDelegate>removeDelegate;

/**
 *  初始化方法
 *
 *  @param array  照片数组
 *  @param number 第几张照片
 */
-(void)initWithPicArray:(NSMutableArray *)array
                  picNo:(NSInteger)number;

@end

PhotoView.m

#import "PhotoView.h"
#import "UIImageView+WebCache.h"
#define SCREEN_HEIGHT     ([[UIScreen mainScreen] bounds].size.height)
#define SCREEN_WIDTH      ([[UIScreen mainScreen] bounds].size.width)
@interface PhotoView   ()<UIScrollViewDelegate>
@property (nonatomic, strong)NSMutableArray *dataArr;
@property (nonatomic, strong)NSMutableArray *scrollArr;
@end

@implementation PhotoView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        _scrollView = [[UIScrollView alloc]init];
        self.backgroundColor = [UIColor whiteColor];
        [[UIApplication sharedApplication]setStatusBarHidden:YES];
    }
    return self;
}

-(void)initWithPicArray:(NSMutableArray *)array picNo:(NSInteger)number
{
    _dataArr = [NSMutableArray arrayWithArray:array];
    _scrollArr = [NSMutableArray array];
    _scrollView = [[UIScrollView alloc]initWithFrame:self.frame];
    _scrollView.pagingEnabled = YES;
    _scrollView.contentSize = CGSizeMake(array.count * SCREEN_WIDTH, 1);
    _scrollView.contentOffset = CGPointMake(SCREEN_WIDTH * number, 0);
    [self addSubview:_scrollView];
    for (NSInteger i = 0; i < _dataArr.count; i++) {
        Model *model = _dataArr[i];
        UIScrollView *scroller = [[UIScrollView alloc]initWithFrame:CGRectMake(SCREEN_WIDTH * i, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
        scroller.delegate = self;
        scroller.minimumZoomScale = 1.0;
        scroller.maximumZoomScale = 2.0;
        [_scrollArr addObject:scroller];
        [_scrollView addSubview:scroller];
        UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(10, 40, SCREEN_WIDTH - 20, model.h / model.w * (SCREEN_WIDTH - 20))];
        imageView.layer.masksToBounds = YES;
        imageView.layer.cornerRadius = 3;
         imageView.tag = 1;
        [imageView sd_setImageWithURL:[NSURL URLWithString:[_dataArr[i] img]]];
        [scroller addSubview:imageView];
        UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        backBtn.frame = CGRectMake(10, 10, 32, 32);
        [backBtn setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal];
        [backBtn addTarget:self action:@selector(removeFromsuper) forControlEvents:UIControlEventTouchUpInside];
        [scroller addSubview:backBtn];
        
        UIButton *deleteBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        deleteBtn.frame = CGRectMake(60, 10, 32, 32);
        [deleteBtn setImage:[UIImage imageNamed:@"delete"] forState:UIControlStateNormal];
        [deleteBtn addTarget:self action:@selector(deletePhoto) forControlEvents:UIControlEventTouchUpInside];
        [scroller addSubview:deleteBtn];
        
        UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToZoom:)];
        doubleTap.numberOfTapsRequired = 2;
        doubleTap.numberOfTouchesRequired = 1;
        [scroller addGestureRecognizer:doubleTap];
        
    }
}

//双击放大或者缩小
-(void)tapToZoom:(UITapGestureRecognizer *)tap
{
    UIScrollView *zoomable = (UIScrollView*)tap.view;
    if (zoomable.zoomScale > 1.0) {
        [zoomable setZoomScale:1 animated:YES];
    } else {
        [zoomable setZoomScale:2 animated:YES];
    }
}
#pragma mark 缩放停止
-(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
{
    NSLog(@"缩放停止    %.2f", scale);
}

#pragma mark 缩放所对应的视图
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    if (scrollView != _scrollView) {
        UIImageView *imageView = [scrollView viewWithTag:1];
        return imageView;
        
    }
    return nil;
}

-(void)removeFromsuper
{
    [_scrollView removeFromSuperview];
    [self removeFromSuperview];
    if ([self.removeDelegate respondsToSelector:@selector(didremovePicture:)]) {
        [self.removeDelegate didremovePicture:_dataArr];
    }
}

-(void)deletePhoto
{
    NSInteger i =   _scrollView.contentOffset.x / SCREEN_WIDTH;
    
    UIScrollView *scroll = _scrollArr[i];
    [UIView animateWithDuration:0.5 animations:^{
        CGRect frame = scroll.frame;
        frame.origin.y -= SCREEN_HEIGHT;
        scroll.frame = frame;
    } completion:^(BOOL finished) {
         [scroll removeFromSuperview];
    }];
    [_scrollArr removeObjectAtIndex:i];
    [_dataArr removeObjectAtIndex:i];
    [self.removeDelegate deletePicture:_dataArr];
    if (i == _dataArr.count) {
        //最后的一页
    } else {
        //右边的依次往左移动一页
        for (NSInteger j = i ; j < _dataArr.count; j ++) {
            [UIView animateWithDuration:0.5 animations:^{
                UIScrollView *scroll =
![Upload 录屏2.gif failed. Please try again.]
![录屏2.gif](http://upload-images.jianshu.io/upload_images/1220329-24b2dbeee586a509.gif?imageMogr2/auto-orient/strip) _scrollArr[j];
                CGRect frame=scroll.frame;
                frame.origin.x -= SCREEN_WIDTH;
                scroll.frame=frame;
            }];
        }
    }
    if (_dataArr.count == 0) {
        [_scrollView removeFromSuperview];
        [self.removeDelegate didremovePicture:_dataArr];
        [self removeFromSuperview];
    }
    _scrollView.contentSize = CGSizeMake((_dataArr.count ) * SCREEN_WIDTH, 1);
}

@end

好了,就差不多这么些了.马上就要毕业了,我很开心能有一个自己很喜欢的工作,也祝大家能将兴趣变成自己的工作,伴随着自己的生活每天过的开开心心.😄

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,431评论 25 707
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,121评论 29 470
  • 第七品 无得无说分 须菩提。于意云何。如来得阿耨多罗三藐三菩提耶。如来有所说法耶。须菩提言。如我解佛所说义。无有定...
    笑看流年阅读 478评论 0 0
  • 我更愿意看两眼心跳的眼神,而不是深陷其中 结局没有那么糟,大部分人庸人自扰 带上帽子找好武装,看事情的打打闹闹 嘿...
    导演张升志阅读 254评论 0 0
  • 这次“15中车祸”重大交通事故,牵动着整座城市的心,今天得知了一个让人悲伤的消息,受伤最重的孩子已经去世了……不知...
    骁娆阅读 163评论 0 1