一、需求分析

点击照片从当前照片位置动画弹出新的视图控制器显示选中的照片,新的视图控制器为全屏显示,背景为黑色,再次点击照片动画缩小至当前选中的照片位置,双击放大照片,如果已经放大则缩小,在新的视图控制器中滑动手指,可以在照片之间切换,当前显示的照片支持手指捏合手势,放大或缩小照片,照片切换时,在照片的下方显示标签提示当前显示的照片数量及照片总数。

性能优化:点击照片从当前照片位置动画弹出新的视图控制器显示选中的照片(非modal、非push)

新的视图控制器为全屏显示,背景为黑色(修改UIApplication的状态栏),再次点击照片动画缩小至当前选中的照片位置(照片视图需要与父视图中的UIImageView建立关联),在新的视图控制器中滑动手指,可以在照片之间切换(UIScrollView的分页支持),当前显示的照片支持手指捏合手势,放大或缩小照片(UIScrollView的代理方法支持图片缩放),照片切换时,在照片的下方显示标签提示当前显示的照片数量及照片总数(开启新的视图控制器前,需要传入照片数组)

二、调整瀑布流

因网络抓取JSON数据的问题,改为从本地plist文件加载照片数据文件,修改MGJData,增加大图URL,修改WaterFlowView中的generateCacheData方法,在重新加载数据前删除所有子视图,创建PhotoBowser分组,保存照片浏览器代码。

三、建立照片浏览器所需文件

PhotoBowserViewController:负责照片浏览控制

PhotoView:显示单张照片,继承自UIScrollView

PhotoToolbarView:照片工图栏视图,显示保存按钮和照片索引

PhotoModel:照片数据模型类,保存照片浏览器的数据

四、建立数据模型

+ (id)photoModelWithUrl:(NSURL *)url index:(NSInteger)index;

// 图像Url

@property (strong, nonatomic) NSURL *url;

// 图像索引

@property (assign, nonatomic) NSInteger index;

五、实例化视图

// 1. 隐藏状态栏

UIApplication *app = [UIApplication sharedApplication];

// 记录初始的状态栏隐藏情况

_defaultStatusBarHidden = app.statusBarHidden;

[app setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];

// 2. 实例化滚动视图

_scrollView = [[UIScrollView alloc]initWithFrame:[UIScreen mainScreen].bounds];

[_scrollView setBackgroundColor:[UIColor blackColor]];

// 暂时添加点按手势,关闭视图控制器

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap)];

[_scrollView addGestureRecognizer:tap];

// 3. 将滚动视图设置为根视图

self.view = _scrollView;

六、显示&隐藏

l 显示

// 为了与调用视图器完全解耦,使用app的window实现视图控制器的显示

UIWindow *window = [UIApplication sharedApplication].keyWindow;

// 将根视图添加到window中,会叠加在上级窗口上

[window addSubview:self.view];

// 将当前视图控制器作为子视图控制器,添加到window的根视图控制器中

[window.rootViewController addChildViewController:self];

l 隐藏

[[UIApplication sharedApplication]setStatusBarHidden:_defaultStatusBarHidden];

[self.view removeFromSuperview];

[self removeFromParentViewController];

七、修改WaterFlowView视图

将cellFramesArray属性移动到.h文件中,以保证照片浏览器视图控制器可以得到所有照片的位置

八、调整显示照片浏览器方法

for (MGJData *data in self.dataList) {

// 获取单元格视图的位置

CGRect frame = [self.waterFlowView.cellFramesArray[index]CGRectValue];

CGRect srcFrame = [self.waterFlowView convertRect:frame toView:controller.view];

PhotoModel *p = [PhotoModel photoModelWithUrl:data.largeImage index:index srcFrame:srcFrame];

[arrayM addObject:p];

index++;

}

controller.photoList = arrayM;

controller.currentPhotoIndex = indexPath.row;

九、照片视图

通过setter属性设置照片

// 占位图像

UIImage *image = [[UIImage alloc]init];

// 从网络加载图像

__unsafe_unretained PhotoView *photoView = self;

[_imageView setImageWithURL:_photo.url placeholderImage:image options:SDWebImageRetryFailed | SDWebImageProgressiveDownload progress:^(NSUInteger receivedSize, long long expectedSize) {

} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {

// 记录图像

[photo setImage:image];

[photoView adjustImageFrame];

}];

十、设置照片显示尺寸

// 2) 以宽度计算比例

CGFloat minScale = boundW / imageW;

if (minScale > 1.0) {

minScale = 1.0;

}

CGFloat maxScale = 2.0;

// 3) 设置视图的缩放比例

self.minimumZoomScale = minScale;

self.maximumZoomScale = maxScale;

// 如果图片超出屏幕大小,需要缩放显示

self.zoomScale = minScale;

// 4) 设置内容尺寸

CGRect imageFrame = CGRectMake(0, 0, boundW, imageH * minScale);

self.contentSize = CGSizeMake(imageFrame.size.width, imageFrame.size.height);

// 5) 计算图像位置

if (imageFrame.size.height < boundH) {

imageFrame.origin.y = (boundH - imageFrame.size.height) / 2.0;

}

十一、如果是初次显示,播放动画效果

// 6) 如果是初次显示,显示动画效果

if (_photo.isFirstShow) {

[_imageView setFrame:_photo.srcFrame];

[UIView animateWithDuration:0.3f animations:^{

[_imageView setFrame:imageFrame];

}];

} else {

[_imageView setFrame:imageFrame];

}

十二、照片视图的缩放处理

#pragma mark - UIScrollView代理方法

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView

{

return _imageView;

}

十三、照片视图,增加双击手势

如果照片已经放大则缩小至初始比例

否则根据用户点击位置放大图像

if (self.zoomScale == self.maximumZoomScale) {

[self setZoomScale:self.minimumZoomScale animated:YES];

} else {

CGPoint location = [recognizer locationInView:self];

[self zoomToRect:CGRectMake(location.x, location.y, 1, 1) animated:YES];

}

十四、照片视图,增加单击手势隐藏照片(坐标转换)

#pragma mark 隐藏照片视图

- (void)hide {

if (_isDoubleTap) return;

// 滚动视图复位

self.contentOffset = CGPointZero;

[UIView animateWithDuration:1.0f animations:^{

[_imageView setFrame:_photo.srcFrame];

[self.photoViewDelegate photoViewDidSingleTap:self];

} completion:^(BOOL finished) {

[self.photoViewDelegate photoViewDidZoomOut:self];

}];

}

#pragma mark 单击手势

- (void)singleTap:(UITapGestureRecognizer *)recognizer {

_isDoubleTap = NO;

// 增加时间延时,确保变量数值更改生效

[self performSelector:@selector(hide) withObject:nil afterDelay:0.2f];

}

十五、提示,隐藏照片时需要两个代理方法处理

#pragma mark 照片视图代理方法

- (void)photoViewDidSingleTap:(PhotoView *)photoView

{

[[UIApplication sharedApplication]setStatusBarHidden:_defaultStatusBarHidden];

// 设置成透明颜色

[self.view setBackgroundColor:[UIColor clearColor]];

}

- (void)photoViewDidZoomOut:(PhotoView *)photoView

{

[self.view removeFromSuperview];

[self removeFromParentViewController];

}

十六、查询可重用照片视图

#pragma mark 查询可重用照片视图

- (PhotoView *)dequeueReusablePhotoView

{

// 预设缓存集合已经存在并且工作正常

PhotoView *photoView = [_reusablePhotoViews anyObject];

// 从缓存集合中删除照片视图

if (photoView) {

[_reusablePhotoViews removeObject:photoView];

}

return photoView;

}

十七、利用photoList setter方法

#pragma mark photoList setter方法,处理视图缓存

- (void)setPhotoList:(NSArray *)photoList

{

_photoList = photoList;

_visiblePhotoViewDict = [NSMutableDictionary dictionary];

_reusablePhotoViews = [NSMutableSet set];

}

十八、通过可见视图字典做照片视图优化

// 在可见视图字典中查询是否存在照片视图

PhotoView *photoView = [_visiblePhotoViewDict objectForKey:@(index)];

if (photoView == nil) {

photoView = [self dequeueReusablePhotoView];

if (photoView == nil) {

photoView = [[PhotoView alloc]init];

[photoView setPhotoViewDelegate:self];

}

PhotoModel *photo = _photoList[index];

CGFloat w = self.view.bounds.size.width;

CGFloat h = self.view.bounds.size.height;

CGRect photoFrame = CGRectMake(index * w, 0, w, h);

[photoView setFrame:photoFrame];

[photoView setPhoto:photo];

[self.view addSubview:photoView];

// 加入可见视图字典

[_visiblePhotoViewDict setObject:photoView forKey:@(index)];

}

十九、在视图滚动停止时,调整缓存数据

NSInteger index = (NSInteger)_scrollView.contentOffset.x / _scrollView.bounds.size.width;

if (index != _currentPhotoIndex) {

PhotoView *photoView = _visiblePhotoViewDict[@(_currentPhotoIndex)];

[_visiblePhotoViewDict removeObjectForKey:@(_currentPhotoIndex)];

[photoView removeFromSuperview];

[_reusablePhotoViews addObject:photoView];

_currentPhotoIndex = index;

}

iOS基础 - 相片浏览器的更多相关文章

  1. IOS基础学习-2: UIButton

    IOS基础学习-2: UIButton   UIButton是一个标准的UIControl控件,UIKit提供了一组控件:UISwitch开关.UIButton按钮.UISegmentedContro ...

  2. iOS 基础日记-修饰符

    今晚随便温习了一下iOS 基础关于修饰符这块的东西,下面简单的来描述一下,其中有的也是在网络学习到的: strong与weak是由ARC新引入的对象变量属性 ARC的解释:ARC引入了新的对象的生命周 ...

  3. iOS基础问答面试

    <简书社区 — Timhbw>iOS基础问答面试题连载(一)-附答案:http://www.jianshu.com/p/1ebf7333808d <简书社区 — Timhbw> ...

  4. [iOS基础控件 - 5.5] 代理设计模式 (基于”APP列表"练习)

    A.概述      在"[iOS基础控件 - 4.4] APP列表 进一步封装,初见MVC模式”上进一步改进,给“下载”按钮加上效果.功能      1.按钮点击后,显示为“已下载”,并且不 ...

  5. [置顶] IOS 基础入门教程

    IOS 基础入门教程 教程列表: IOS 简介 IOS环境搭建 Objective C 基础知识 创建第一款iPhone应用程序 IOS操作(action)和输出口(Outlet) iOS - 委托( ...

  6. js基础--获取浏览器当前页面的滚动条高度的兼容写法

    欢迎访问我的个人博客:http://www.xiaolongwu.cn 前言 在开发中,兼容性问题是最常见的,今天就来介绍一下关于获取滚动条高度的兼容性写法,宽度同理,我在这里就不一一解释了 各浏览器 ...

  7. iOS中Safari浏览器select下拉列表文字太长被截断的处理方法

    网页中的select下拉列表,文字太长的话在iOS的Safari浏览器里会被自动截断,显示成下面这种: 安卓版的浏览器则没有这个问题. 如何让下拉列表中的文字在iOS的Safari浏览器里显示完整呢? ...

  8. iOS 基础:Frames、Bounds 和 CGGeometry

    https://segmentfault.com/a/1190000004695617 原文:<iOS Fundamentals: Frames, Bounds, and CGGeometry& ...

  9. ios系统微信浏览器、safari浏览器中h5页面上拉下滑导致悬浮层脱离窗口的解决方法

    一. 运行环境: iphone所有机型的qq浏览器,safari浏览器,微信内置浏览器(qq浏览器内核)等. 二. 异常现象: 1. 大幅度上下滑动h5页面,然后停止滑动,有时候会影响到页面滚动,如局 ...

随机推荐

  1. c语言获取符号位整数和浮点

    1. 为什么你应该得到的签位 非常多的时间,我们需要推断的数目值正和负,做了相应的逻辑处理.完成这一要求条件推断语句可以很好. 有时会出现以下情况, if (x > 0) { x = x - 1 ...

  2. MyEclipse2014 设备 checkstyle、PMD、findbugs 最简单的方法 详细说明

    最近的实验需要的代码审查和应用程序性能优化.在需求MyEclipse安装某些插件,由于如今的MyEclipse版本号和大多数教程的不一样了,一些安装选项也已经改变,所以安装起来非常费事,通过不断的尝试 ...

  3. 采纳ajax提交POST样本数据

    问题叙述性说明 我们会form该input和checkbox提交给异步数据phpserver.处理后的回. 提交之后显示的位置: 难点分析 採用from表单的onsubmit属性阻止表单的提交 < ...

  4. 【Leetcode】Sort List (Sorting)

    这个问题需要与归并排序排两个名单,基本思路分为切割与合并 合并后的代码Merge Two Sorted List里已经讲得非常清楚了. 所以这里直接给出代码. public ListNode merg ...

  5. POJ1201-Intervals(差动限制)

    Intervals Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 20786   Accepted: 7866 Descri ...

  6. WCF中的数据契约(DataContract)

    服务契约定义了远程访问对象和可供调用的方法,数据契约则是服务端和客户端之间要传送的自定义数据类型. 一旦声明一个类型为DataContract,那么该类型就可以被序列化在服务端和客户端之间传送,如下所 ...

  7. ssh 自动登录

    工作中经常会有这样的需求场景,因为要在其它电脑上做操作, 需要从PC A ssh 到 PC B,PC A 可能是自己的工作机,PC B 可能是服务器.一般会使用 SSH 登录到 server 上再进行 ...

  8. Android 记录的(MediaRecorder)而播放(MediaPlayer)

    经MediaRecorder和MediaPlayer实现声音记录和回放,代码比较简单,直接附着到代码. xml文档面对只有四个button不贴. UI watermark/2/text/aHR0cDo ...

  9. Linux httpd 跳转简单方法二

    使用mod_proxy 这种方法要添加上mod_proxy_http.so 在httpd.conf 中打开httpd-vhost 在 httpd-vhost 里面添加上 <VirtualHost ...

  10. 使用Scala操作Mongodb

    介绍 Scala是一种功能性面向对象语言.它融汇了很多前所未有的特性.而同一时候又执行于JVM之上.随着开发人员对Scala的兴趣日增,以及越来越多的工具支持,无疑Scala语言将成为你手上一件不可缺 ...