UITableView-图片浏览

一.工程链接

通过定义两套协议一层一层的传递数据,也就是一套协议传递一层(供参考,没有完全完成)
链接:
https://pan.baidu.com/s/1qXL0J1CTgcarJeZKmXE_Bw 密码:ioo8

完整工程
链接:
https://pan.baidu.com/s/1f4XGluebTlOYAOX668b4bQ 密码:5xj6

二.开发流程

1.预期的功能

读取程序内部保存的图片文件(一个图片对应一个文件)
使 UITableView展示
点击Edit 进入编辑状态 选择需要操作的图片
点击Delete删除选中的图片
点击Done完成操作

2.为了减少内存的消耗,保存图片需要保存两套,一套小图,一套原图


示意文件关系

3.定义一个类来管理文件的相关操作FileOperation,单例创建

单例创建

static FileOperation *instance = nil;

#pragma mark -------单例创建 ---------
//高级做法
+(FileOperation *)sharedOperation{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [FileOperation new];
    });
    
    return instance;
}

封装路径方法

#pragma mark -------获取documents的路径 ---------
-(NSString *)documentPath{    
    return NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
}

#pragma mark -------获取缩略图的路径 ---------
-(NSString *)thumbnailPath{
    
    return [[self documentPath] stringByAppendingPathComponent:@"small"];
}

#pragma mark -------获取原图的路径 ---------
-(NSString *)originalPath{
    
    return [[self documentPath] stringByAppendingPathComponent:@"org"];
}

#pragma mark ------- 拼接路径 获取缩略图的文件路径 ---------
-(NSString *)thumbnailPathWithName:(NSString *)name{
    
    return [[self documentPath] stringByAppendingPathComponent:[NSString stringWithFormat:@"small/%@",name]];
}

#pragma mark -------拼接路径 获取缩略图的文件路径 ---------
-(NSString *)originalPathWithName:(NSString *)name{
    
    return [[self documentPath] stringByAppendingPathComponent:[NSString stringWithFormat:@"org/%@",name]];
}

根据路径创建文件并将图片保存其中

#pragma mark -------创建文件夹 ---------
-(void)creatDirectoryWithName:(NSString *)name{
    
    NSString *path = [[self documentPath] stringByAppendingPathComponent:name];
    
    [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}


#pragma mark -------保存图片数组 ---------
-(void)saveImages:(NSArray *)imagesArray{
    
    //首先保存外部传来的数据,防止操作过程中对象被释放或者篡改
    NSArray *images = [imagesArray copy];
    
    //创建org和small目录
    [self creatDirectoryWithName:@"org"];
    [self creatDirectoryWithName:@"small"];
    
    //将所有的图片保存
    for (UIImage *img in images) {
        
        [self saveImage:img];
        
    }
}

#pragma mark ------获得图片名字路径并保存 ---------
-(void)saveImage:(UIImage *)img{
    
    //将图片裁剪为100×100
    UIImage *smallImage = [img scaleToSize:CGSizeMake(100, 100)];
    
    //得到唯一的名字 可以用时间作为文件的名字
    //NSString *name = [NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]];
    NSString *name = [NSString stringWithFormat:@"%@",[[NSUUID UUID] UUIDString]];
    NSLog(@"%@",name);
    
    //Documents/org/图片的名字
    NSString *orgPath = [self originalPathWithName:name];
    //保存这张图片
    [self saveImage:img filePath:orgPath];
    
    //Documents/small/图片的名字
    NSString *smallPath = [self thumbnailPathWithName:name];
    //保存这张图片
    [self saveImage:smallImage filePath:smallPath];
}

#pragma mark -------通过路径保存单个图片 ---------
-(void)saveImage:(UIImage *)img filePath:(NSString *)path{
    
    //1.判断文件是否存在
    if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
        NSLog(@"文件已存在");
        return;
    }
    
    //2.创建文件
    NSData *data = UIImagePNGRepresentation(img);
    BOOL result = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
    if (result == YES) {
        NSLog(@"创建成功");
    }else{
        NSLog(@"创建失败");
    }
    
}

读取图片和删除图片

#pragma mark -------读取文件内容 缩略图 ---------
-(NSArray *)loadData{
    
    //存放图片
    NSMutableArray *images = [NSMutableArray array];
    
    //1.获取缩略图路径
    NSString *thumbPath = [self thumbnailPath];
    //2.读取目录里面的所有内容 文件的名称组成的数组
    NSArray *fileNamesArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:thumbPath error:nil];
    //3.使用for循环读取所有文件的内容
    for (NSString *name in fileNamesArray) {
        //1.拼接文件的完整路径
        NSString *filePath = [thumbPath stringByAppendingPathComponent:name];
        //2.读取文件内容
        NSData *imageData = [NSData dataWithContentsOfFile:filePath];
        //3.转化图片
        UIImage *img = [UIImage imageWithData:imageData];
        
        //4.创建图片对应的模型
        PhotoViewModel *model = [PhotoViewModel new];
        model.image = img;
        model.index = [fileNamesArray indexOfObject:name];
        model.isSelected = NO;//默认状态,可以不写
        model.name = name;
        
        //4.添加到数组
        [images addObject:model];
    }
    
    return images;
    
}

#pragma mark -------读取缩略图对应的原图 ---------
-(UIImage *)originalImageWithName:(NSString *)name{
    
    //得到路径
    NSString *orgPath = [self originalPathWithName:name];
    //获取二进制数据
    NSData *data = [NSData dataWithContentsOfFile:orgPath];
    //二进制数据转化为图片
    UIImage *img = [UIImage imageWithData:data];
    
    return img;
}

#pragma mark -------删除某个图片文件 ---------
-(void)removeImage:(NSString *)name{
    //获取small路径
    NSString *thumPath = [self thumbnailPathWithName:name];
    
    //获取原图路径
    NSString *orgPath = [self originalPathWithName:name];
    
    //删除两个文件
    [[NSFileManager defaultManager] removeItemAtPath:thumPath error:nil];
    [[NSFileManager defaultManager] removeItemAtPath:orgPath error:nil];
}

4.显示的图片的数据封装为模型

/**模型的图片*/
@property (nonatomic,strong)  UIImage *image;

/**索引值-第几张图片*/
@property (nonatomic,assign) NSInteger index;

/**图片的状态 默认就是0*/
@property (nonatomic,assign) BOOL isSelected;

/**图片对应的文件名*/
@property (nonatomic,copy) NSString *name;

5.将图片写入到文件中,只执行一次

    //只做一次
    self.imgsArray = [NSMutableArray array];

    for (int i = 0; i < 6; i++) {

        NSString *name = [NSString stringWithFormat:@"%d",i+1];

        UIImage *img = [UIImage imageNamed:name];

        [self.imgsArray addObject:img];

    }

    NSLog(@"%ld",self.imgsArray.count);

    [[FileOperation sharedOperation] saveImages:self.imgsArray];

6.读取数据,并配置相关信息

//读取数据
self.imagesArray = [NSMutableArray arrayWithArray:[[FileOperation sharedOperation] loadData]];

//注册
[self.tableView registerClass:[PhotoCell class] forCellReuseIdentifier:@"cellID"];

#pragma mark -------UITableView ---------
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    
    //每行显示四个
    NSInteger row = self.imagesArray.count/4;
    
    if (_imagesArray.count%4 != 0) {
        
        row++;
    }
    
    return row;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    PhotoCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID"];
    
    //判断是不是最后一行
    NSInteger totol = [tableView numberOfRowsInSection:0];
    
    //正常四个
    NSRange range = NSMakeRange(indexPath.row*4, 4);
    
    //只有最后一行可能不是四个
    if (indexPath.row == totol-1) {
        
        //最后一行
        range.length = _imagesArray.count-indexPath.row*4;
    }
        
    //传数据
    cell.indexPath = indexPath;//必须先传,否则重写setImages有误
    cell.images = [_imagesArray subarrayWithRange:range];
    
    //样式
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    
    return cell;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return kHeight+kPadding*2;
}

7.自定义自己想要的cell

-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
    
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        
        //创建四个photoView
        for (int i = 0; i < 4; i++) {
            //创建
            PhotoView *pv = [[NSBundle mainBundle] loadNibNamed:@"PhotoView" owner:nil options:nil].lastObject;
            //设置tag值
            pv.tag = i+1;
            //隐藏
            pv.hidden = YES;
            //添加到cell
            [self.contentView addSubview:pv];
            
            pv.backgroundColor = [UIColor redColor];
        }
    }
    
    return self;
}

-(void)layoutSubviews{
    
    for (int i = 1; i <= 4; i++) {
        
        PhotoView *pv = [self viewWithTag:i];
        
        pv.frame = CGRectMake(kPadding+(i-1)*(kPadding+kWidth), kPadding, kWidth, kHeight);
    }
}

-(void)setImages:(NSArray *)images{
    _images = images;
    
    //显示数据
    for (int i = 0; i < 4; i++) {
        
        PhotoView *pv = [self viewWithTag:i+1];
        
        if (i < _images.count) {
            pv.hidden = NO;
            
            PhotoViewModel *model = images[I];
            model.index = _indexPath.row*4+I;
            
            pv.model = model;
        }else{
            
            pv.hidden = YES;
        }
    }
}

8.通过Xib完成Cell里面的视图的定义


Xib里面的内容

一个UIButton和一个UIImageView组成,UIBUtton用来显示我们的图片,UIImageView用来显示删除信息,并且在Xib文件里面就将其hidden设为YES

通过消息通知在PhotoView和ViewController之间传递数据

定义一套协议用来将点击的事件传递给viewController

#import <Foundation/Foundation.h>
#import "Constants.h"

//用单独的一个类来管理协议 降低耦合性

@protocol PhotoViewDelegate <NSObject>

//定义一套代理 方法
//1.第几个被选中了
//2.状态 选中 取消 正常

-(void)photoViewStatusChanged:(PhotoViewStatus)status Index:(NSInteger)index;

@end

PhotoView的配置

-(void)awakeFromNib{
    [super awakeFromNib];
    
    //注册
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeToEdit) name:kPhotoViewChangeStatusToEditNotificationName object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeToNormal) name:kPhotoViewChangeStatusToNormalNotificationName object:nil];
}

-(void)dealloc{
    //移除
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
//    [NSNotificationCenter defaultCenter] removeObserver:<#(nonnull id)#> name:<#(nullable NSNotificationName)#> object:<#(nullable id)#>
}

-(void)changeToEdit{
    
    _checkImageView.hidden = NO;
}

-(void)changeToNormal{

    _checkImageView.hidden = YES;
    
    _checkImageView.image = [UIImage imageNamed:@"CheckBox_Normal"];
}

-(void)setModel:(PhotoViewModel *)model{
    _model = model;
    
    //显示图片
    [_imageButton setImage:model.image forState:UIControlStateNormal];
    
    //判断当前view的状态
    if (model.isSelected == YES) {

        _checkImageView.image = [UIImage imageNamed:@"CheckBox_Selected"];
    }else{
        
        _checkImageView.image = [UIImage imageNamed:@"CheckBox_Normal"];
    }
}

- (IBAction)imageBtnDidClick:(UIButton *)sender {
    
    //定义一个变量 记录状态
    PhotoViewStatus status;
    
    if (_checkImageView.hidden == YES) {
        
        //正常状态 放大图片
        status = PhotoViewStatusNormal;
        
    }else{
        if (_model.isSelected == NO) {
            
            //选中状态
            status = PhotoViewStatusSelected;
            
            _checkImageView.image = [UIImage imageNamed:@"CheckBox_Selected"];
            //_checkImageView.tag = 1;
            
        }else{
            
            //取消选中
            status = PhotoViewStatusDeselected;
            
            _checkImageView.image = [UIImage imageNamed:@"CheckBox_Normal"];
            //_checkImageView.tag = 0;
            
        }
        
    }
    
    //将点击的事件传递给viewController
    //1.找到主界面
    ViewController *vc = (ViewController *)[UIApplication sharedApplication].keyWindow.rootViewController;
    //2.传递事件
    if ([vc respondsToSelector:@selector(photoViewStatusChanged:Index:)]) {
        [vc photoViewStatusChanged:status Index:_model.index];
    }
    
}

ViewControll实现代理的方法

#pragma mark -------PhotoViewDelegate ---------
-(void)photoViewStatusChanged:(PhotoViewStatus)status Index:(NSInteger)index{
    
    //获取index对应的数据模型
    PhotoViewModel *model = [self.imagesArray objectAtIndex:index];
    
    switch (status) {
            
        case PhotoViewStatusNormal:
            //放大浏览图片
            
            [BrowserView showBrowserViewAtIndex:index AndFrame:self.view.bounds];
            
            break;
        case PhotoViewStatusSelected:
            //选中图片
            [self.selectedArray addObject:model];
            self.deleteBtn.enabled = YES;
            //更改对当前模型的状态
            model.isSelected = YES;
            
            break;
        case PhotoViewStatusDeselected:
            //取消选中
            [self.selectedArray removeObject:model];
            if (self.selectedArray.count == 0) {
                self.deleteBtn.enabled = NO;
            }
            //更改对当前模型的状态
            model.isSelected = NO;
            
            break;
        default:
            break;
    }
}

9.多个地方需要用到的变量

#ifndef Constants_h
#define Constants_h

#define kPadding 10
#define kWidth (([UIScreen mainScreen].bounds.size.width-5*kPadding)/4.0)
#define kHeight (([UIScreen mainScreen].bounds.size.width-5*kPadding)/4.0)

#define kPhotoViewChangeStatusToEditNotificationName @"kPhotoViewChangeStatusToEditNotificationName"

#define kPhotoViewChangeStatusToNormalNotificationName @"kPhotoViewChangeStatusToNormalNotificationName"

//定义一个枚举记录PhotoView的状态
typedef NS_ENUM(NSUInteger,PhotoViewStatus) {
    PhotoViewStatusSelected,   //选中
    PhotoViewStatusDeselected, //取消选中
    PhotoViewStatusNormal      //浏览图片
};

#endif /* Constants_h */

10.自定义一个用于管理图片的类BrowserView

提供一个类方法创建

//创建浏览视图 并且显示出来
+(BrowserView *)showBrowserViewAtIndex:(NSInteger)index AndFrame:(CGRect)frame{
    
    BrowserView *bv = [[BrowserView alloc] initWithFrame:frame];
    
    //接受产生的索引值,加载默认的视图
    bv.index = index;
    
    //用于测试的背景颜色
    bv.backgroundColor = [UIColor blackColor];
    
    //显示
    [[UIApplication sharedApplication].keyWindow addSubview:bv];
    
    //渐变动画
    bv.alpha = 0.5;
    
    [UIView animateWithDuration:0.5 animations:^{
        bv.alpha = 1;
    }];
    
    return bv;
}

//重写initWithFrame方法 布局
-(instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]) {
        
        //用于滚动的视图
        self.scrollView = [ScrollView new];
        _scrollView.bounces = NO;
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.contentSize = CGSizeMake(3*kScrollWidth, kScrollHeight);
        _scrollView.pagingEnabled = YES;
        _scrollView.delegate = self;
        [self addSubview:_scrollView];
        
        //创建用于展示的三个容器视图
        self.leftView = [self creatImageView];
        self.middleView = [self creatImageView];
        self.rightView = [self creatImageView];
    }
    return self;
}

//创建图片容器视图
-(UIImageView *)creatImageView{
    
    UIImageView *imgView = [UIImageView new];
    imgView.contentMode = UIViewContentModeScaleAspectFit;
    [self.scrollView addSubview:imgView];
    
    return imgView;
}

//重新布局 重写initwithframe的时候不应该给ferame
-(void)layoutSubviews{
    [super layoutSubviews];
    
    //计算尺寸
    _scrollView.frame = CGRectMake(0, 0, kScrollWidth, kScrollHeight);
}

#pragma mark -------移除视图 ---------
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    //消失
    //得到browserView的对象
    [self removeFromSuperview];
}

重写index的set方法,数据一来就刷新显示的内容

//重写index的set方法,一有数据来就重新刷新视图
-(void)setIndex:(NSInteger)index{
    _index = index;
    
    //获取图片数据
    _middleView.image = [[DataCenter defaultCenter] imageAtIndex:index];
    _leftView.image = [[DataCenter defaultCenter] imageAtIndex:index-1];
    _rightView.image = [[DataCenter defaultCenter] imageAtIndex:index+1];
    
    //判断该如何显示
    if (_leftView.image == nil) {
        //第一张
        _middleView.frame = CGRectMake(0, 0, kScrollWidth, kScrollHeight);
        _rightView.frame = CGRectMake(kScrollWidth, 0, kScrollWidth, kScrollHeight);
        _scrollView.contentSize = CGSizeMake(2*kScrollWidth, 0);
    }else if(_rightView.image == nil){
        //最后一张
        _middleView.frame = CGRectMake(kScrollWidth, 0, kScrollWidth, kScrollHeight);
        _leftView.frame = CGRectMake(0, 0, kScrollWidth, kScrollHeight);
        _scrollView.contentSize = CGSizeMake(2*kScrollWidth, 0);
    }else{
        //中间
        _leftView.frame = CGRectMake(0, 0, kScrollWidth, kScrollHeight);
        _middleView.frame = CGRectMake(kScrollWidth, 0, kScrollWidth, kScrollHeight);
        _rightView.frame = CGRectMake(kScrollWidth*2, 0, kScrollWidth, kScrollHeight);
        _scrollView.contentSize = CGSizeMake(3*kScrollWidth, 0);
    }
    
    //始终显示中间的视图
    [_scrollView setContentOffset:CGPointMake(_middleView.frame.origin.x, 0) animated:NO];
    
    //重新设置偏移值
    _preOffsetX = _scrollView.contentOffset.x;
}

//拖拽结束做的事情
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    
    //比较前后偏移值判断滑动方向
    if (scrollView.contentOffset.x > _preOffsetX) {
        //右边
        if (_rightView.image != nil) {
            self.index = _index+1;
        }
    }else if(scrollView.contentOffset.x < _preOffsetX){
        //左边
        if (_leftView.image != nil) {
            self.index = _index-1;
        }
    }
    //没有滑动
}

移除视图的时候[self removeFromSuperview],这里的self是盖上去的视图,我们是要把这个盖上去的视图从其父视图上移除,重写一个类继承于UIScrollView传递点击事件(虽然UIScrollView上面放置了三个UIImageView,但是UIImageView的交互事件默认是关闭的)

#import "ScrollView.h"

@implementation ScrollView

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    //将点击事件传递给父视图
    [self.superview touchesBegan:touches withEvent:event];
}

@end

降低代码冗余,将数据的加载抽出来作为一个类

#import "DataCenter.h"
#import "FileOperation.h"
#import "PhotoViewModel.h"

static DataCenter *instance = nil;

@implementation DataCenter

+(DataCenter *)defaultCenter{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        instance = [DataCenter new];
        
    });
    
    return instance;
}

//通过懒加载读取数据
-(NSMutableArray *)dataArray{
    if (_dataArray == nil) {
        
        self.dataArray = [NSMutableArray arrayWithArray:[[FileOperation sharedOperation] loadData]];
    }
    return _dataArray;
}

-(UIImage *)imageAtIndex:(NSInteger)index{
    
    if (index < 0 || index >= self.dataArray.count) {
        return nil;
    }
    
    //根据model取相应的图片
    PhotoViewModel *model = self.dataArray[index];
    
    //返回图片
    return [[FileOperation sharedOperation] originalImageWithName:model.name];
}

@end

三.运行结果

运行结果
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,872评论 1 32
  • 效果图 布局 样式 JS
    嘿喵heyMeow阅读 1,135评论 0 0
  • 这一天,众鸟归还,谷物繁盛 每一棵秧苗,都肃穆于 土地的神袛,让阳光与雨水 自由与苦难,同来洗礼 谁的怀抱葱茏如故...
    后院的小小阅读 2,967评论 0 1
  • 滴滴! 新年到来之际,大家是否在考虑着为自己的房子装饰一翻,喜气洋洋的装饰更能体现出新年节日的欢悦...
    小徐2018阅读 2,206评论 0 0
  • 肚子上的肉越来越多之后,就想着是不是要锻炼锻炼,以前也有锻炼,但断断续续一直坚持不了,这次想要寻求专业的帮助,遂想...
    Lonestone阅读 5,357评论 0 0

友情链接更多精彩内容