iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一)

nwah8807 9年前
   <p>写一个功能完善的图片浏览器</p>    <h2>最终效果 <a href="/misc/goto?guid=4959673422650160484">源码</a></h2>    <p><img src="https://simg.open-open.com/show/45bdfec8ea268468667288cc9ad6d191.gif" alt="iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一) " width="318" height="557"></p>    <p>网络.gif</p>    <p><img src="https://simg.open-open.com/show/b5c2b89e6ca7dc654a049db32dfb0339.gif" alt="iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一) " width="318" height="557"></p>    <p>本地图片.gif</p>    <h2>本篇文章后完成的效果</h2>    <p><img src="https://simg.open-open.com/show/a95318472ff3dfb72f8dd4a6fbb98fbd.gif" alt="iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一) " width="318" height="557"></p>    <p>效果.gif</p>    <p>首先来列出我们可能会实现的功能</p>    <p>1.基本功能</p>    <ul>     <li>支持双击缩小和放大点击的区域</li>     <li>单击退出</li>     <li>支持响应长按手势</li>     <li>支持图片的捏合实现缩放</li>     <li>支持浏览的时候显示页数(例如 12/40)</li>     <li>支持点击保存图片</li>     <li>支持加载本地图片</li>    </ul>    <p>2. 高级功能</p>    <ul>     <li>支持加载网络图片</li>     <li>支持显示加载的进度条</li>     <li>支持网络图片的缓存</li>     <li>支持指定下标到特定的图片</li>     <li>支持gif</li>     <li>可选择的显示每一张图片的描述文字</li>    </ul>    <p>清楚了这些可能会实现的功能,接下来就从简单的开始分布实现</p>    <p>首先处理每一张图片共有的功能, 比如单击和双击以及缩放, 在本篇中,我们主要是来实现每一张图片拥有的一些功能</p>    <p>一. 提到单击和双击,大家可能就直接想到了直接在相应的ImageView上添加两个UITap手势即可完成, 没错可能要实现手势的响应确实是这么简单, 但是想想我们是要在双击的时候处理缩放, 那么一个难点就是怎样实现图片的缩放</p>    <ul>     <li>我们可以在双击的时候直接处理ImageView的transform来实现缩放, 而且还可以加上一点动画让这个过程更自然, 看上去这还是个可操作易行的方法</li>     <li>我们知道UIScrollView本身帮我们处理好了缩放的功能, 使用它来响应双击手势也许也是可行的, 不过现在我还么没有完全确定, 就是要使用UIScrollView, 因为还有其他的功能没有分析怎么实现</li>    </ul>    <p>二. 支持图片的捏合实现缩放, 要实现这个功能, 看到捏合两个字, 我们的第一反应,一定是在ImageVIew上添加一个UIPinchGestureRecognizer手势来处理, 没错添加pinch这个手势确实可以考虑</p>    <ul>     <li>添加UIPinchGestureRecognizer到ImageVIew上, 然后在手势的响应中对应的处理ImageView的transform实现捏合, 不过,要怎么来处理这个过程中的transform确实还是很麻烦的</li>     <li>我们知道UIScrollView本身帮我们处理好了缩放的功能,只需要很简单的配置就能事项捏合的功能</li>    </ul>    <p>清楚了上面两点,选择UIScrollView来实现图片的各种手势效果无疑是简单的, 所以就选择了UIScrollView, 所以这样很自然的就想到了每一张图片的View层次, 最底层是一个UIView做容器,同时用来成为UIScrollView的代理, 然后在上面添加UIScrollView来响应手势和缩放, 最后在上面添加ImageView来添加图片</p>    <blockquote>     <h3>实现部分</h3>    </blockquote>    <ol>     <li>新建一个PhotoView类继承自UIView</li>     <li>在PhotoView中依次添加UIScrollView和UIImageView<br> <img src="https://simg.open-open.com/show/c6d98dc21111dbe49efe1c61c5d54484.png" alt="iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一) " width="579" height="98"> <p>懒加载子控件.png</p> </li>     <li>设置UIScrollView和UIImageView的frame      <blockquote>       <p>这里需要注意的是设置UIImageView的frame的时候, 如果你是设置他的frame为UIScrollView的bounds, 那么会出现的情况是进行图片放大的时候, 因为是对ImageView整体放大, 所以图片以外的区域也被放大,浏览的效果就是这样的, 不得不吐槽有些app就是这么简单的处理的</p>       <img src="https://simg.open-open.com/show/3a6e168aacd00005fa91acb3522a77a9.gif" alt="iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一) " width="318" height="581">       <p>空白区域被放大.gif</p>       <p><br> 而我们希望的imageView的宽高是和设置的图片宽高相同或者成比例的缩放, 就是说图片会全部充满imageView, 效果就是这样的</p>       <img src="https://simg.open-open.com/show/ce61cdcec73a57815a6fc26138f5125c.gif" alt="iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一) " width="318" height="581">       <p>空白区域不放大.gif</p>      </blockquote> </li>     <li> <p>处理UIScrollView实现缩放</p> <pre>  <code class="language-objectivec"> 要实现缩放, 只需要设置三个地方     1. 设置scrollView的最大最小缩放倍数, 注意最大倍数必须要大于最小倍数(注意不是大于等于)   scrollView.maximumZoomScale = 2.0   scrollView.minimumZoomScale = 1.0   2. 设置代理   scrollView.delegate = self   3. 实现缩放的代理方法, 在这个代理方法中返回要缩放的对象, 当然在我们这里就是imageView, 完成这三步后就可以实现捏合的缩放了         func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {       return imageView   }</code></pre> <h3>恩好完成上面几步后, 我们就已经完成了图片的捏合缩放功能, 不过这里有个问题, 在缩小的时候, 我发现图片没有居中, 我们肯定希望图片居中缩放</h3> <img src="https://simg.open-open.com/show/8c4e9249e4f213ec828b93ab61e8f902.gif" alt="iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一) " width="318" height="557"> <p>不能居中缩放.gif</p> <p><br> 于是在UIScrollView的这个代理方法允许我们监控缩放的过程, 所以我们可以处理imageView的位置</p> <pre>  <code class="language-objectivec"> func scrollViewDidZoom(scrollView: UIScrollView) {       // 居中显示图片, 大家可以使用if语句来理解, 不过我发现使用if后居然效果不对(##<>##), 没有找到原因       let offsetX = (scrollView.zj_width > scrollView.contentSize.width) ? (scrollView.zj_width - scrollView.contentSize.width)*0.5 : 0.0       let offsetY = (scrollView.zj_height > scrollView.contentSize.height) ? (scrollView.zj_height - scrollView.contentSize.height)*0.5 : 0.0         imageView.center = CGPoint(x: scrollView.contentSize.width * 0.5 + offsetX, y: scrollView.contentSize.height * 0.5 + offsetY)   }</code></pre> <p>添加完这一小段代码后效果是这样的,还是很满意</p> <img src="https://simg.open-open.com/show/a95318472ff3dfb72f8dd4a6fbb98fbd.gif" alt="iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一) " width="318" height="557"> <p>居中缩放.gif</p> </li>     <li> <p>添加单击和双击手势, 注意需要解决单击和双击手势的冲突</p> <pre>  <code class="language-objectivec">     let singleTap = UITapGestureRecognizer(target: self, action: #selector(self.handleSingleTap(_:)))       singleTap.numberOfTapsRequired = 1       singleTap.numberOfTouchesRequired = 1         let doubleTap = UITapGestureRecognizer(target: self, action: #selector(self.handleDoubleTap(_:)))       doubleTap.numberOfTapsRequired = 2       doubleTap.numberOfTouchesRequired = 1         // 允许优先执行doubleTap, 在doubleTap执行失败的时候执行singleTap       // 如果没有设置这个, 那么将只会执行singleTap 不会执行doubleTap       singleTap.requireGestureRecognizerToFail(doubleTap)         addGestureRecognizer(singleTap)       addGestureRecognizer(doubleTap)</code></pre> <p>单击手势的响应, 每一张图片自身不处理单击手势, 我们希望由之后的PhotoBrowser来处理, 所以这里使用了Closure</p> <pre>  <code class="language-objectivec"> // 单击手势, 给外界处理   func handleSingleTap(ges: UITapGestureRecognizer) {         singleTapAction?(gesture: ges)   }</code></pre> <p>双击手势处理, 放大或者缩小</p> <pre>  <code class="language-objectivec">     if scrollView.zoomScale <= scrollView.minimumZoomScale { // 放大           scrollView.setZoomScale(scrollView.maximumZoomScale, animated: true)       } else {// 缩小很简单, 直接设置缩小到的倍数, 这里我希望缩放到最小           scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)         }</code></pre> <h3>这样就处理好了双击的放大和缩小, 但是目前的放大到最大区域的时候, 并不是我们想要的放大点击区域的效果, 所以放大肯定还需要单独的处理</h3> <img src="https://simg.open-open.com/show/538d9788e244187cac584684006ae683.gif" alt="iOS详细写一个功能完善的PhotoBrowser同时支持GIF(一) " width="318" height="557"> <p>固定放大.gif</p> </li>    </ol>    <p>经过一番的研究, 如下处理效果还比较满意</p>    <pre>  <code class="language-objectivec">        if scrollView.zoomScale <= scrollView.minimumZoomScale { // 放大                let location = ges.locationInView(scrollView)              // 放大scrollView.maximumZoomScale倍, 将它的宽高缩小这么多倍              let width = scrollView.zj_width/scrollView.maximumZoomScale              let height = scrollView.zj_height/scrollView.maximumZoomScale              // 这里需要进行一点的数学换算得来              let rect = CGRect(x: location.x * (1 - 1/scrollView.maximumZoomScale), y: location.y * (1 - 1/scrollView.maximumZoomScale), width: width, height: height)              // 这个方法会根据提供的rect来缩放, 如果给的宽高小余scrollView的宽高, 将进行相应的倍数放大的操作, 如果大于, 就会进行缩小到最小操作              scrollView.zoomToRect(rect, animated: true)            } else {// 缩小              scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)            }</code></pre>    <blockquote>     <p>到目前为止, 单张图片的处理基本就完整了,运行的效果就和最初给的单张相似 如果你只是需要显示一张图片, 那么这个图片浏览器就已经很完善了, 当然我相信,你肯定也希望能处理多张图片, 具体实现将会在下一篇介绍, 欢迎关注</p>    </blockquote>    <p>文/<a href="/misc/goto?guid=4959673422742695756">ZeroJ</a>(简书)<br>  </p>    <p> </p>