StoryBoard,俗称sb,是iOS下可视化编程的诀要之一,另一种是xib。使用storyBoard能够方便连忙的拖拽视图调整器,控件,设置根视图调节器,并且能够直观的观察地方大小,效果等,借使能听得多了就能说的清楚应用storyBoard,能够少写过多代码。因为多storyBoard还没怎么用过,这里只说单storyBoard,也正是Main.storyBoard。

UICollectionView的基础是UITableView, UITableView
咱们自然用得很熟稔,UITableView中的只支持单排列表,未有艺术援助网格列表,在IOS6
中就出了UICollectionView,UICollectionView能更加好的辅助网格实图

纵然如此近日市道上有局地不易的加密相册App,但不是停放广告,正是对上传的张数有所限制。本文介绍了一个加密相册的营造进度,该加密相册将囊括多密码(输入区别的密码就能够访谈分歧的空中,可掩人眼界)、WiFi传图、照片文件加密等职能。前段时间项目和小说会同有时候升高,项目标源代码可以在github上下载。点击前往GitHub

![Uploading storyBoard-1_280278.gif . .
.]](

UICollectionView与UITableView基本相似,分化的地方正是UICollectionViewFlowLayout,UICollectionView的delegate和UITableView基本相似。下边包车型大巴话一下不等的地方

上一篇作品重要介绍了照片浏览器的缩略图预览界面设计,本文主要介绍照片的保留、删除批处理的完结。

图片 1storyBoard.png

a. UICollectionViewFlowLayout的属性

交互设计

打开编辑情势的艺术为点击尾部工具栏的编排按键,如下图所示。

图片 2进去编辑形式

跻身编辑形式后,工具栏会被交换,从左到右按键分别是脱离编辑方式、批量封存和批量去除,那时点击图片可以采取和反选,如下图所示。

图片 3编写形式

当选必要保留和删除的图纸,点击工具栏按键就能够实行相关操作,前段时间提供的保留作用为保留到系统相册。

留心,在拉长后面先点击A。然后就足以在右下角往视图调整器上拖想要的控件,不再详细介绍

UICollectionViewFlowLayout能够用属性设置UICollectionViewCell显示的尺寸,间隔,排列格局等。

拍卖选中与反选

上节讲到用于缩略图展现的collectionView的Cell有三个sg_select属性用于拍卖选中掩盖层的显得与掩饰。

@interface SGPhotoCell : UICollectionViewCell@property (nonatomic, strong) SGPhotoModel *model;// 记录选中状态,为了与系统的select属性区分@property (nonatomic, assign) BOOL sg_select;+ (instancetype)cellWithCollectionView:(UICollectionView *)collectionView forIndexPaht:(NSIndexPath *)indexPath;@end

遮蔽层与缩略图的职位关系如下图所示。

图片 4此间写图片描述

从后到前分别是Cell的contentView、用于缩略图显示的ImageView、选择掩盖层。sg_select依照模型数据中的选中与否来决定,彰显掩盖层在sg_select为YES时才会议及展览示。

当点击了工具栏的编写按键后,会进来编辑形式,在toolBar对象上有二个用来记录是否处于编辑情势的property,称为isEditing,当is艾德iting为YES时,collectionView的didSelectItemAtIndexPath:将会直接重返,防止步向查看原图的浏览格局,同期经过didUnhighlightItemAtIndexPath:方法来管理选中与反选,为了便于早先时期批管理,将具有入选的模子记录下来,具体落成如下。

- collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { if (self.toolBar.isEditing) { return; } // 进入原图浏览模式,下一篇文章介绍 SGPhotoViewController *vc = [SGPhotoViewController new]; vc.browser = self; vc.index = indexPath.row; [self.navigationController pushViewController:vc animated:YES];}- collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(nonnull NSIndexPath *)indexPath { if (self.toolBar.isEditing) { // 得到模型,翻转模型的选中与未选中状态 SGPhotoModel *model = self.photoAtIndexHandler(indexPath.row); model.isSelected = !model.isSelected; // 记录所有被选中的模型 if (model.isSelected) { [self.selectModels addObject:model]; } else { [self.selectModels removeObject:model]; } // 更新当前操作的Cell的模型,从而更新选取状态 SGPhotoCell *cell = (SGPhotoCell *)[collectionView cellForItemAtIndexPath:indexPath]; cell.model = model; return; }}

在更新Cell的model时,会调用model的setter。

- setModel:(SGPhotoModel *)model { _model = model; NSURL *thumbURL = model.thumbURL; if ([thumbURL isFileURL]) { self.imageView.image = [UIImage imageWithContentsOfFile:thumbURL.path]; } else { [self.imageView sd_setImageWithURL:thumbURL]; } // 设置sg_select self.sg_select = model.isSelected;}

在设置sg_select时,会调用其setter,对掩饰层视图进行掩饰和出示。

- setSg_select:sg_select { _sg_select = sg_select; self.selectMaskView.hidden = !_sg_select;}

掩盖层视图包罗了背景与对号标识,背景透明而对号标识不透明,由于对号标识是向来抬高到背景上的,假使平昔设置背景的阿尔法,会耳熟能详到子视图,要减轻那几个难题,能够透过UIColor的colorWithAlphaComponent:方法来安装背景观,那样背景透明,而子视图不受影响,具体贯彻如下。

self.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.6f];

到这里甘休,已经实现了对中选和反选的管理,并且能够将具备入选的数据模型存入selectModels属性。

首先在storyBoard上拖拽第三个视图调节器,和您成立的类二个品种

图片 5

拍卖工具栏

工具栏为UIToolBar的子类,为了有助于,使用block回调UIBarButtonItem的点击事件,由于有多处供给使用工具栏,首先设计二个toolBar的基类,完成如下。

// 回调block的类型定义typedef void(^SGBlockToolBarActionBlock)(UIBarButtonItem *item);@interface SGBlockToolBar : UIToolbar// 用于设置回调的setter- setButtonActionHandlerBlock:(SGBlockToolBarActionBlock)handler;// 子类用于触发block回调的接口- btnClick:(UIBarButtonItem *)sender;// 快速创建UIBarButtonItem的方法- (UIBarButtonItem *)createBarButtomItemWithSystemItem:(UIBarButtonSystemItem)systemItem;- (UIBarButtonItem *)createBarButtomItemWithImage:(UIImage *)image;// 创建弹簧,用于UIBarButtonItem的分散排布- (UIBarButtonItem *)createSpring;@end

@interface SGBlockToolBar ()// 回调block属性@property (nonatomic, copy) SGBlockToolBarActionBlock actionHandler;@end@implementation SGBlockToolBar- (instancetype)initWithFrame:frame { if (self = [super initWithFrame:frame]) { self.backgroundColor = [UIColor whiteColor]; } return self;}- setButtonActionHandlerBlock:(SGBlockToolBarActionBlock)handler { self.actionHandler = handler;}- (UIBarButtonItem *)createBarButtomItemWithSystemItem:(UIBarButtonSystemItem)systemItem { return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:systemItem target:self action:@selector(btnClick:)];}- (UIBarButtonItem *)createBarButtomItemWithImage:(UIImage *)image { return [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:@selector(btnClick:)];}- (UIBarButtonItem *)createSpring { return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];}- btnClick:(UIBarButtonItem *)sender { if (self.actionHandler) { self.actionHandler; }}@end

工具栏包涵主工具栏和二级工具栏,都三番六回SGBlockToolBar,只须要设定toolBar的items就能够,回调由调控器管理,使用tag区分不相同的按钮。以二级工具栏SGBrowserSecondToolBar为例,具体贯彻如下。

@implementation SGBrowserSecondToolBar- (instancetype)initWithFrame:frame { if (self = [super initWithFrame:frame]) { self.translucent = YES; [self setupViews]; } return self;}- setupViews { UIBarButtonItem *editBtn = [self createBarButtomItemWithImage:[UIImage imageNamed:@"BackButton"]]; // tag在管理主工具栏和二级工具栏的类中定义,下文有介绍 editBtn.tag = SGBrowserToolButtonBack; UIBarButtonItem *actionBtn = [self createBarButtomItemWithSystemItem:UIBarButtonSystemItemAction]; actionBtn.tag = SGBrowserToolButtonAction; UIBarButtonItem *trashBtn = [self createBarButtomItemWithSystemItem:UIBarButtonSystemItemTrash]; trashBtn.tag = SGBrowserToolButtonTrash; self.items = @[editBtn,[self createSpring],actionBtn,[self createSpring],trashBtn];}@end

主工具栏和二级工具栏通过贰个承接UIView的SGBrowserToolBar治本,同时定义了全体工具栏按键的tag。它最首要的个性是isEditing,用于记录是不是处在编辑状态,相同的时间经过其setter来设置主工具栏和二级工具栏的切换,达成如下。

#define SGBrowserToolButtonEdit -1#define SGBrowserToolButtonBack -2#define SGBrowserToolButtonAction -3#define SGBrowserToolButtonTrash -4@interface SGBrowserToolBar : UIView@property (nonatomic, weak) SGBrowserMainToolBar *mainToolBar;@property (nonatomic, weak) SGBrowserSecondToolBar *secondToolBar;@property (nonatomic, assign) BOOL isEditing;@end

工具栏的frame有三种,左边荧屏外为outFrame,显示器内为frontFrame,侧边显示屏外为backFrame,首先依据显示器尺寸总括出八个frame,开始情形下,主工具栏处于frontFrame、二级工具栏处于outFrame,当步向编辑状态时,主工具栏处于backFrame、二级工具栏处于frontFrame,从而完结工具栏的切换,切换在isEditing的setter中处理,具体贯彻如下。

- setIsEditing:isEditing { _isEditing = isEditing; if (self.isEditing) { [UIView animateWithDuration:0.3 animations:^{ self.mainToolBar.frame = self.outFrame; self.secondToolBar.frame = self.frontFrame; }]; } else { [UIView animateWithDuration:0.3 animations:^{ self.mainToolBar.frame = self.frontFrame; self.secondToolBar.frame = self.backFrame; }]; }}

肖像浏览器的调整器SGPhotoBrowser肩负将toolBar增多到视图上,并且管理回调,具体贯彻如下。

- setupViews { CGFloat barW = self.view.bounds.size.width; CGFloat barH = 44; CGFloat barX = 0; CGFloat barY = self.view.bounds.size.height - barH; SGBrowserToolBar *toolBar = [[SGBrowserToolBar alloc] initWithFrame:CGRectMake(barX, barY, barW, barH)]; self.toolBar = toolBar; [self.view addSubview:toolBar]; __weak typeof weakToolBar = self.toolBar; // 主工具栏的回调,用于处理编辑状态的切换 [toolBar.mainToolBar setButtonActionHandlerBlock:^(UIBarButtonItem *item) { switch  { case SGBrowserToolButtonEdit: weakToolBar.isEditing = YES; break; } }]; WS(); // 用于定义weakSelf,防止循环引用 // 二级工具栏的回调,用于处理图片的批处理 [toolBar.secondToolBar setButtonActionHandlerBlock:^(UIBarButtonItem *item) { switch  { // 退出编辑模式时,将选中的状态全部切换为未选中 case SGBrowserToolButtonBack: { weakToolBar.isEditing = NO; for (NSUInteger i = 0; i < weakSelf.selectModels.count; i++) { SGPhotoModel *model = weakSelf.selectModels[i]; model.isSelected = NO; [weakSelf reloadData]; } break; } // 处理保存,通过ActionSheet来选择保存到哪里,这里使用了block会掉的ActionSheet,其源码可以在文首给出的源码中找到。 case SGBrowserToolButtonAction: { [[[SGBlockActionSheet alloc] initWithTitle:@"Save To Where" callback:^(UIActionSheet *actionSheet, NSInteger buttonIndex) { switch (buttonIndex) { case 1: // 保存批处理方法,下文讲解 [weakSelf handleBatchSave]; break; } } cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitlesArray:@[@"Photo Library"]] showInView:self.view]; break; } // 处理删除 case SGBrowserToolButtonTrash: { // 删除批处理方法,下文讲解 [weakSelf handleBatchDelete]; break; } } }];}

保存图片通过ALAssetsLibrary的writeImageToSavedPhotosAlbum:::方法完成,该形式是异步的,为了确定保证一苏缘杰张的存入何况呈现存款和储蓄进程,使用NSBlockOperation来管理每多少个囤积任务,通过设置重视关系保障图片是一孙乐张的被保存进而不利的体现速度,通过MBProgressHUD来呈现速度,具体贯彻如下。

- handleBatchSave { NSInteger count = self.selectModels.count; // 如果没有选中图片,则提示用户并返回 if (count == 0) { [MBProgressHUD showError:@"Select Images Before Save"]; return; } // 记录当前保存完成的数量 __block NSInteger currentCount = 0; // 新建library用来保存图片到相册 ALAssetsLibrary *lib = [ALAssetsLibrary new]; // 记录上一个任务来设置依赖关系,后面的任务依次依赖前面的任务 NSBlockOperation *lastOp = nil; // 记录所有任务,以便在定义结束后全部开启 NSMutableArray<NSBlockOperation *> *ops = @[].mutableCopy; MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.navigationController.view]; hud.removeFromSuperViewOnHide = YES; hud.mode = MBProgressHUDModeAnnularDeterminate; hud.label.text = [NSString stringWithFormat:@"Saving %@/%@",@(currentCount),@]; [hud showAnimated:YES]; [self.navigationController.view addSubview:hud]; // 每个任务完成时的回调,由于在多个地方用到,使用block来记录该代码块,具体逻辑为更新hud的内容,当所有任务完成时,延时移除hud。 void (^opCompletionBlock)() = ^{ dispatch_async(dispatch_get_main_queue(), ^{ currentCount++; hud.progress = currentCount / count; hud.label.text = [NSString stringWithFormat:@"Saving %@/%@",@(currentCount),@]; if (currentCount == count) { [hud hideAnimated:YES afterDelay:0.25f]; } }); }; // 根据选中模型数组,将每一个模型转化为一个保存任务 for (NSUInteger i = 0; i < count; i++) { SGPhotoModel *model = self.selectModels[i]; NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ __block BOOL finish = NO; NSURL *url = model.photoURL; // 根据URL类型处理,如果是网络URL,则先使用SDWebImage下载再保存 if (![url isFileURL]) { [[SDWebImageManager sharedManager] downloadImageWithURL:url options:SDWebImageDownloaderHighPriority|SDWebImageDownloaderUseNSURLCache progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { [lib writeImageToSavedPhotosAlbum:image.CGImage metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) { opCompletionBlock(); finish = YES; }]; }]; } else { UIImage *image = [UIImage imageWithContentsOfFile:url.path]; [lib writeImageToSavedPhotosAlbum:image.CGImage metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) { opCompletionBlock(); finish = YES; }]; } // 用于防止block返回导致任务提前完成,在把图片保存到相册后,才将finish置为YES,从而保证任务在保存到相册后结束。 while ; }]; // 设置逐次依赖关系 if (lastOp != nil) { [op addDependency:lastOp]; } lastOp = op; [ops addObject:op]; } // 开启所有任务 NSOperationQueue *queue = [NSOperationQueue new]; for (NSUInteger i = 0; i < ops.count; i++) { [queue addOperation:ops[i]]; }}

透过模型内记录的path移除沙盒中的原图和缩略图像和文字件,而且经过图片浏览器的多寡源回调reloadHandler要求子类重新加载文件,该部分的内部原因能够在上一篇文章中的小节照片浏览器的利用中找到。将照片批量刨除的切实可行达成如下。

- handleBatchDelete { NSInteger count = self.selectModels.count; if (count == 0) { [MBProgressHUD showError:@"Select Images Before Delete"]; return; } NSFileManager *mgr = [NSFileManager defaultManager]; for (NSUInteger i = 0; i < count; i++) { SGPhotoModel *model = self.selectModels[i]; [mgr removeItemAtPath:model.photoURL.path error:nil]; } self.reloadHandler();}

本文首要介绍了照片浏览器中的缩略图浏览分界面包车型地铁批管理完成,包涵了批量保存到系统相册和批量刨除,项目的源码能够在文首的地点中找到,迎接关怀项目继续。

图片 6

b. UICollectionViewFlowLayout的代理

那样就已经和你创制的类关联上了,storyBoardID便是一定于该页面包车型地铁举世无双标志,而identifier是segue的标志符,会基于区别的segue标记符处理数量假若在别的类里引进该类时,就须要用storyBoardID找到这么些页面,而不可能重复创造初阶化,以页面跳转为例,storyBoard之间的跳转都使用如下方法:

当然你也得以安装使用代理方法UICollectionViewDelegateFlowLayout,

- prepareForSegue:(UIStoryboardSegue *)segue sender:sender{ if ([segue.identifier isEqualToString:@"secondPush"]) { SecondViewController *secondVC = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]]instantiateViewControllerWithIdentifier:@"second"]; secondVC.CustomLabel.backgroundColor = [UIColor cyanColor]; } }

– collectionView:(UICollectionView*)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath*)indexPath;

此间的@“second”正是刚刚写的storyBoardID.

图片 7每一个cell的大小

自定义segue步骤步骤一 : 新建 个类承接 UIStoryboardSegue步骤 二: 选中前
个调节器,按住control鼠标 标帮衬完结连线,选 择custom。步骤三: 选中 定义
segue,设置 segue的identifier 以及关系类。步骤四: 在
segue类里重写perform方法(界 间跳转默许实行的办法), 自定义跳转职能。

– (UIEdgeInsets)collectionView:(UICollectionView*)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section;

发表评论

电子邮件地址不会被公开。 必填项已用*标注