iOS基础 - 相片浏览器
一、需求分析
点击照片从当前照片位置动画弹出新的视图控制器显示选中的照片,新的视图控制器为全屏显示,背景为黑色,再次点击照片动画缩小至当前选中的照片位置,双击放大照片,如果已经放大则缩小,在新的视图控制器中滑动手指,可以在照片之间切换,当前显示的照片支持手指捏合手势,放大或缩小照片,照片切换时,在照片的下方显示标签提示当前显示的照片数量及照片总数。
性能优化:点击照片从当前照片位置动画弹出新的视图控制器显示选中的照片(非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基础 - 相片浏览器的更多相关文章
- IOS基础学习-2: UIButton
IOS基础学习-2: UIButton UIButton是一个标准的UIControl控件,UIKit提供了一组控件:UISwitch开关.UIButton按钮.UISegmentedContro ...
- iOS 基础日记-修饰符
今晚随便温习了一下iOS 基础关于修饰符这块的东西,下面简单的来描述一下,其中有的也是在网络学习到的: strong与weak是由ARC新引入的对象变量属性 ARC的解释:ARC引入了新的对象的生命周 ...
- iOS基础问答面试
<简书社区 — Timhbw>iOS基础问答面试题连载(一)-附答案:http://www.jianshu.com/p/1ebf7333808d <简书社区 — Timhbw> ...
- [iOS基础控件 - 5.5] 代理设计模式 (基于”APP列表"练习)
A.概述 在"[iOS基础控件 - 4.4] APP列表 进一步封装,初见MVC模式”上进一步改进,给“下载”按钮加上效果.功能 1.按钮点击后,显示为“已下载”,并且不 ...
- [置顶] IOS 基础入门教程
IOS 基础入门教程 教程列表: IOS 简介 IOS环境搭建 Objective C 基础知识 创建第一款iPhone应用程序 IOS操作(action)和输出口(Outlet) iOS - 委托( ...
- js基础--获取浏览器当前页面的滚动条高度的兼容写法
欢迎访问我的个人博客:http://www.xiaolongwu.cn 前言 在开发中,兼容性问题是最常见的,今天就来介绍一下关于获取滚动条高度的兼容性写法,宽度同理,我在这里就不一一解释了 各浏览器 ...
- iOS中Safari浏览器select下拉列表文字太长被截断的处理方法
网页中的select下拉列表,文字太长的话在iOS的Safari浏览器里会被自动截断,显示成下面这种: 安卓版的浏览器则没有这个问题. 如何让下拉列表中的文字在iOS的Safari浏览器里显示完整呢? ...
- iOS 基础:Frames、Bounds 和 CGGeometry
https://segmentfault.com/a/1190000004695617 原文:<iOS Fundamentals: Frames, Bounds, and CGGeometry& ...
- ios系统微信浏览器、safari浏览器中h5页面上拉下滑导致悬浮层脱离窗口的解决方法
一. 运行环境: iphone所有机型的qq浏览器,safari浏览器,微信内置浏览器(qq浏览器内核)等. 二. 异常现象: 1. 大幅度上下滑动h5页面,然后停止滑动,有时候会影响到页面滚动,如局 ...
随机推荐
- thinkphp学习笔记5—模块化设计
原文:thinkphp学习笔记5-模块化设计 1.模块结构 完整的ThinkPHP用用围绕模块/控制器/操作设计,并支持多个入口文件盒多级控制.ThinkPHP默认PATHINFO模式,如下: htt ...
- Git 基本原理与经常使用命令
平时使用过两种版本号控制软件 SVN 和 Git,平心而论,假设纯粹自己使用,那么绝对 Git 更加适合,本地库.远程库.离线工作.强大而灵活的分支.大名鼎鼎的Github, 这些都是选择 Git 的 ...
- 博客测试:博客系统i94web beta1.0 申请测试
如何做了最近的博客更新,因为已经在线路和代码,我写了一个小博客系统:i94web,草草宣布beta1.0,请求您测试各种漏洞. 先看几张截图. 首页: watermark/2/text/aHR0cDo ...
- hdu Jungle Roads(最小生成树)
Problem Description The Head Elder of the tropical island of Lagrishan has a problem. A burst of for ...
- 多校训练赛2 ZCC loves cards
ZCC loves cards Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) ...
- linux_sed 正则替换
目标 替换 test.data文件中的 原正则: (\d{4}[^)]*?) sed不支持 \d 改为0-9 测试 :head test.data| sed -r "s/([0-9]{4}[ ...
- 兼容安卓的javaproject1.0
<pre class="java" name="code"> //兼容安卓的系统package cn.com.likeshow; import ja ...
- 使用ArcGIS API for Silverlight + Visifire绘制地图统计图
原文:使用ArcGIS API for Silverlight + Visifire绘制地图统计图 最近把很久之前做的统计图又拿出来重新做了一遍,感觉很多时候不复习,不记录就真的忘了,时间是最好的稀释 ...
- 乐趣与你rabbitMQ 源代码
RabbitMQ API RabbitMQ Server它提供了丰富的http api. 对于列子 须要HTTP基本身份验证.默认的username/password为guest/guest. 这些返 ...
- wamp 已安装cakephp Fatal error: You must enable the intl extension to use CakePHP. in XXX
今wamp已安装cakephp3.x什么时候.报告这样的错误:Fatal error: You must enable the intl extension to use CakePHP. in D: ...