iOS 图片选择器

  • 废话不多说,先看效果图,如果基本满足你的需求就继续看下去,不满足就去找能满足你的。
  • 不知道什么原因这个gif图看着很僵硬呢?简单文字介绍下吧!
  • 图片选择器支持相机拍照和相册选择两种方式,相机拍照的图片应用之后自动保存到相册里;我这里设置的是最大限制图片数是5张,刚好超出屏幕最大宽度,代码里直接设置的是支持左右滑动,不会自动换行。
gif图.gif
能继续翻下来的朋友,也许是因为这个小工具满足你的需求,又可能是你根本就不需要,只是想看下怎么实现的,接下来开始正题!

为了方便,我直接贴代码,该说明的地方都在代码里,我觉得注释的很清楚了,为了节约时间,复制黏贴可以直接用;想要搞清楚原理的朋友们可以时候慢慢看

1、文件结构

结构图.png

2、相机小封装

  • .h文件
#import <Foundation/Foundation.h>

typedef void (^ LYCameraBlock)(NSData *data);

@interface LYCamera : NSObject

+(LYCamera *)shareInit;

@property (nonatomic, copy)LYCameraBlock lyCameraBlock;

-(void)openWithView:(UIViewController *)view Block:(LYCameraBlock)block;

@end
  • .m文件
#import "LYCamera.h"
#import <AVFoundation/AVFoundation.h>

@interface LYCamera ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate,UIActionSheetDelegate>

@property (nonatomic, strong) UIViewController *view;

@end

@implementation LYCamera

+(LYCamera *)shareInit{
    
    static LYCamera *data = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        data = [[LYCamera alloc] init];
    });
    return data;
    
}

-(void)openWithView:(UIViewController *)view Block:(LYCameraBlock)block{
    
    _view = view;
    _lyCameraBlock = block;
    
    NSString *mediaType = AVMediaTypeVideo;//读取媒体类型
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];//读取设备授权状态
    if(authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied){
        
        [[[LYAlertController alloc]init] showWithController:_view Message:@"允许打开相机" Confirm:^{
            NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
            [[UIApplication sharedApplication] openURL:url];
        }];
        
    }else{
        
        UIActionSheet *actionSheet = [[UIActionSheet alloc]
                                      initWithTitle:nil
                                      delegate:self
                                      cancelButtonTitle:@"取消"
                                      destructiveButtonTitle:nil
                                      otherButtonTitles:@"拍照",@"相册",nil];
        actionSheet.actionSheetStyle = UIActionSheetStyleDefault;
        [actionSheet showInView:_view.view];
        
    }
    
}

-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
    
    if (buttonIndex == 0) {
        
        if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
            NSLog(@"失败");
        }else{
            UIImagePickerController * imagePicker = [[UIImagePickerController alloc]init];
            imagePicker.delegate = self;
            imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;//设置源类型
//            [imagePicker setEditing:YES animated:YES];//允许编辑
//            imagePicker.allowsEditing = YES;
            [_view presentViewController:imagePicker animated:YES completion:nil];
        }
        
    }else if (buttonIndex == 1){
        
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]){
            UIImagePickerController * imagePicker = [[UIImagePickerController alloc]init];
            imagePicker.delegate = self;
            imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;//设置源类型
//            [imagePicker setEditing:YES animated:YES];//允许编辑
//            imagePicker.allowsEditing = YES;
            [_view presentViewController:imagePicker animated:YES completion:nil];
        }
        
    }
    
}

//打开相册之后选择的方法
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
    
    NSString * mediaType = [info valueForKey:UIImagePickerControllerMediaType];
    __weak typeof(self) wSelf = self;
    if ([mediaType hasSuffix:@"image"]){
//        UIImage *image = [info valueForKey:UIImagePickerControllerEditedImage];//允许编辑
        UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage];
        //返回的就是jpeg格式的data
        NSData *dataImage = UIImageJPEGRepresentation(image, 0.5);
        
        if (picker.sourceType != UIImagePickerControllerSourceTypePhotoLibrary) {
            //把图片保存到相册
            [self saveImageToPhotos:image];
        }
        
        [_view dismissViewControllerAnimated:YES completion:^{
            if (wSelf.lyCameraBlock) {
                wSelf.lyCameraBlock(dataImage);
            }
        }];
        
    }
    
}

//实现该方法
- (void)saveImageToPhotos:(UIImage*)savedImage{
    //因为需要知道该操作的完成情况,即保存成功与否,所以此处需要一个回调方法image:didFinishSavingWithError:contextInfo:
    UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
}

//回调方法
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
    
    NSString *msg = nil;
    if(error != NULL){
        msg = @"保存失败";
    }else{
        msg = @"保存成功";
    }
//    [WKProgressHUD popMessage:msg inView:[UIApplication sharedApplication].keyWindow duration:1.0 animated:YES];
    
}

@end

3、存储图片小Model

  • .h文件
#import <Foundation/Foundation.h>

@interface LYUploadImgModel : NSObject

@property (nonatomic, strong) UIImage *img;

/**
 0 表示手动上传的图片
 1 表示默认的占位图片
 */
@property (nonatomic) NSInteger imgType;

@end
  • .m文件
#import "LYUploadImgModel.h"

@implementation LYUploadImgModel

@end

4、展示图片小Cell

  • .h文件
#import <UIKit/UIKit.h>

@class LYUploadImgModel;

@interface LYUploadImgViewCell : UICollectionViewCell

-(void)setDataSourceWithModel:(LYUploadImgModel *)model Index:(NSInteger)index;

-(void)deleteImgSelectWithBlock:(void (^)(NSInteger index))block;

@end
  • .m文件
#import "LYUploadImgViewCell.h"
#import "LYUploadImgModel.h"

typedef void(^ DeleteBlock)(NSInteger);

@interface LYUploadImgViewCell ()

@property (nonatomic, strong) UIImageView *ivImg;
@property (nonatomic, strong) UIButton *bDelete;
@property (nonatomic) NSInteger index;

@property (nonatomic, copy) DeleteBlock block;

@end

@implementation LYUploadImgViewCell

-(instancetype)initWithFrame:(CGRect)frame
{
    if (self == [super initWithFrame:frame]) {
        
        self.backgroundColor = [UIColor whiteColor];
        
        //图片
        self.ivImg = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
        self.ivImg.backgroundColor = [UIColor whiteColor];
        self.ivImg.layer.cornerRadius = 5;
        self.ivImg.layer.masksToBounds = YES;
        [self addSubview:self.ivImg];
        
        //删除按钮
        CGFloat btnWidth = 15;
        self.bDelete = [[UIButton alloc] initWithFrame:CGRectMake(self.frame.size.width - btnWidth, 0, btnWidth, btnWidth)];
        [self.bDelete setImage:LYImageName(@"icon_delete_img") forState:UIControlStateNormal];
        self.bDelete.backgroundColor = [UIColor whiteColor];
        self.bDelete.layer.cornerRadius = btnWidth / 2;
        self.bDelete.layer.masksToBounds = YES;
        [self.bDelete addTarget:self action:@selector(deleteAction) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:self.bDelete];
        
    }
    
    return self;
}

-(void)deleteAction{
    if (_block) {
        _block(_index);
    }
}

-(void)deleteImgSelectWithBlock:(void (^)(NSInteger))block{
    _block = block;
}

-(void)setDataSourceWithModel:(LYUploadImgModel *)model Index:(NSInteger)index{
    
    _index = index;
    [self.ivImg setImage:model.img];
    
    //占位图的时候不显示删除按钮
    if (model.imgType == 1) {
        self.ivImg.layer.cornerRadius = 0;
        self.bDelete.hidden = YES;
    }
    //非占位图的时候显示删除按钮
    else{
        self.ivImg.layer.cornerRadius = 5;
        self.bDelete.hidden = NO;
    }
    
}

@end

5、正规军 — 图片选择器

  • .h文件
#import <UIKit/UIKit.h>

typedef void (^ LYUploadImgViewBlock)(NSMutableArray *imgDataSource);

@interface LYUploadImgView : UIView

@property (nonatomic, copy) LYUploadImgViewBlock block;

@property (nonatomic) NSInteger maxCount;

@end
  • .m文件
#import "LYUploadImgView.h"
#import "LYUploadImgViewCell.h"
#import "LYUploadImgModel.h"

static NSString *ident = @"Item";

@interface LYUploadImgView ()<UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout>

@property (nonatomic) NSInteger maxDefaultCount;//默认最大上传数量,最多5张

@property (nonatomic) NSInteger currentImgCount;//当前上传图片数量
@property (nonatomic, strong) UIButton *buttonUpload;//上传图片按钮
@property (nonatomic, strong) UICollectionViewFlowLayout *layout;
@property (nonatomic, strong) UICollectionView *collectionView;

@property (nonatomic, strong) NSMutableArray *dataSource;//数据源数组
@property (nonatomic, strong) NSMutableArray *imgDataSource;//回调的上传图片数组

@end

@implementation LYUploadImgView

-(instancetype)init{
    
    if (self == [super init]) {
        self.backgroundColor = [UIColor whiteColor];
        [self createUI];
        [self initDataSource];
    }
    return self;
    
}

-(void)layoutSubviews{
    [super layoutSubviews];
    _layout.itemSize = CGSizeMake(self.frame.size.height, self.frame.size.height);
    _collectionView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
    _buttonUpload.frame = CGRectMake(0, 0, self.frame.size.height, self.frame.size.height);
}

#pragma mark - 创建UI
-(void)createUI{
    [self addSubview:self.buttonUpload];
    [self addSubview:self.collectionView];
    [self isHideUploadView:NO];
}

#pragma mark - 控制上传按钮和Collection的隐藏与显示
-(void)isHideUploadView:(BOOL)isHide{
    self.buttonUpload.hidden = isHide;
    self.collectionView.hidden = !isHide;
}

#pragma mark - 添加图片按钮
-(UIButton *)buttonUpload{
    
    if (!_buttonUpload) {
        _buttonUpload = [[UIButton alloc] init];
        [_buttonUpload setImage:LYImageName(@"icon_add_img") forState:0];
        [_buttonUpload addTarget:self action:@selector(buttonUploadSelect:) forControlEvents:UIControlEventTouchUpInside];
    }
    return _buttonUpload;
    
}

-(void)buttonUploadSelect:(UIButton *)btn{
    
    __weak typeof(self) wSelf = self;
    //调起相机
    [[LYCamera shareInit] openWithView:SuperController(self) Block:^(NSData *data) {
        
        //判断上传图片数量是否小于最大限制
        if (self.currentImgCount < self.maxDefaultCount) {
            
            LYUploadImgModel *model = [[LYUploadImgModel alloc] init];
            model.img = [UIImage imageWithData:data];
            model.imgType = 0;
            [self.dataSource insertObject:model atIndex:self.dataSource.count-1];//新上传的图片存储位置永远都在默认图片的前面
            
            if (wSelf.block) {
                //图片转码base64,存储并回调出去
                NSString *encodedImageStr = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
                [self.imgDataSource addObject:encodedImageStr];
                wSelf.block(self.imgDataSource);
            }
            
        }
        
        //先把currentImgCount置0,然后遍历手动上传图片的个数,并记录
        self.currentImgCount = 0;
        for (LYUploadImgModel *model in self.dataSource) {
            if (model.imgType == 0) {
                self.currentImgCount ++;
            }
        }
        
        //手动上传图片的个数大于等于最大限制数时,移除最后一个占位图
        if (self.currentImgCount >= self.maxDefaultCount) {
            [self.dataSource removeObjectAtIndex:self.maxDefaultCount];
        }
        
        [self.collectionView reloadData];
        [self isHideUploadView:YES];
        
    }];
    
}

#pragma mark - CollectionView
-(UICollectionView *)collectionView{
    
    if (!_collectionView) {
        _layout = [[UICollectionViewFlowLayout alloc] init];
        _layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
        _layout.minimumLineSpacing = 10;
        _layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
        
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_layout];
        _collectionView.backgroundColor = [UIColor whiteColor];
        _collectionView.showsHorizontalScrollIndicator = NO;
        _collectionView.userInteractionEnabled = YES;
        [_collectionView registerClass:[LYUploadImgViewCell class] forCellWithReuseIdentifier:ident];
        _collectionView.delegate = self;
        _collectionView.dataSource = self;
    }
    return _collectionView;
    
}

#pragma mark -- UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return self.dataSource.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    
    LYUploadImgViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ident forIndexPath:indexPath];
    
    LYUploadImgModel *model = self.dataSource[indexPath.row];
    [cell setDataSourceWithModel:model Index:indexPath.row];
    
    //删除已上传图片的回调
    __weak typeof(self) wSelf = self;
    [cell deleteImgSelectWithBlock:^(NSInteger index) {
        
        //移除源数据数组中对应位置的图片
        [self.dataSource removeObjectAtIndex:index];
        
        if (wSelf.block) {
            //从回调数组中移除对应位置的图片,并回调出去
            [wSelf.imgDataSource removeObjectAtIndex:index];
            wSelf.block(wSelf.imgDataSource);
        }
        
        //遍历占位图是否存在,存在立刻结束遍历,并跳出当前方法
        BOOL isCancel = NO;
        for (LYUploadImgModel *model in self.dataSource) {
            if (model.imgType == 1) {
                isCancel = YES;
                break;
            }
        }
        
        //如果占位图不存在,重新添加占位图到源数组中
        if (!isCancel) {
            [self initModel];
        }
        
        [self.collectionView reloadData];
        
        //先把currentImgCount置0,然后遍历手动上传图片的个数,并记录
        self.currentImgCount = 0;
        for (LYUploadImgModel *model in self.dataSource) {
            if (model.imgType == 0) {
                self.currentImgCount ++;
            }
        }
        
        //如果手动上传图片个数为0,恢复初始状态
        if (self.currentImgCount == 0) {
            [self isHideUploadView:NO];
        }
        
    }];
    
    return cell;
    
}

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    if (self.currentImgCount < self.maxDefaultCount) {
        if (indexPath.row == (self.dataSource.count-1)) {
            [self buttonUploadSelect:nil];
        }
    }
}

#pragma mark - 数据源
-(NSMutableArray *)dataSource{
    if (!_dataSource) {
        _dataSource = [NSMutableArray new];
    }
    return _dataSource;
}

-(NSMutableArray *)imgDataSource{
    if (!_imgDataSource) {
        _imgDataSource = [NSMutableArray new];
    }
    return _imgDataSource;
}

#pragma mark - 初始化数据源
-(void)initDataSource{
    self.currentImgCount = 0;
    self.maxDefaultCount = 5;
    [self initModel];
}

-(void)initModel{
    LYUploadImgModel *model = [[LYUploadImgModel alloc] init];
    UIImage *img = LYImageName(@"icon_add_img");
    model.img = img;
    model.imgType = 1;
    [self.dataSource addObject:model];
}

-(void)setMaxCount:(NSInteger)maxCount{
    self.maxDefaultCount = maxCount;//最大限制数根据设置重新赋值
}

@end

6、调用

引入头文件#import "LYUploadImgView.h"

  • 初始化
LYUploadImgView *selector = [LYUploadImgView new];
[self addSubview:selector];

[selector mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.mas_equalTo(self.lOrderNo.mas_left);
    make.top.mas_equalTo(self.lVoucher.mas_bottom).offset(10);
    make.right.mas_equalTo(self.tvExplain.mas_right);
    make.height.mas_equalTo(75);
}];
  • 图片回调
selector.block = ^(NSMutableArray *imgDataSource) {
    NSLog(@"imgDataSource = %@",imgDataSource);
};

7、 全剧终

demo

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

推荐阅读更多精彩内容