教您制作风度翩翩款性子化图片浏览器,swift达成图片浏览器

Posted by

swift实现图片浏览器

  • 1.配置环境
  • 2.搭建首页home界面
  • 3.自定义流水布局注意重写prepareLayout()方法,必须调用父类的super.prepareLayout()
  • 4.加载AFN
  • 5.分装AFN网络请求类
    -1.
    提供接口,将请求到的数据或者错误信息通过闭包回调给外界2.,提供一个单列对象,

class NetWorkTool: AFHTTPSessionManager {
//提供一个单列对象
static let shareInstance : NetWorkTool = {
let shareInstance = NetWorkTool()
shareInstance.responseSerializer.acceptableContentTypes?.insert(“text/html”)
return shareInstance
}()
//提供结构,将请求到的数据通过闭包传递
func loadData(offset: Int,finishCallBack: (data:[[String:
NSObject]]?, error: NSError? ) ->()){
let url =
“http://mobapi.meilishuo.com/2.0/twitter/popular.json?offset=(offset)&limit=30&access\_token=b92e0c6fd3ca919d3e7547d446d9a8c2&limit=30&access_token=b92e0c6fd3ca919d3e7547d446d9a8c2)”
GET(url, parameters: nil, progress: nil, success: { (, result) ->
Void in
guard let dic = result as? [String: NSObject] else{
return
}
guard let dicArray = dic[“data”] as? [[String: NSObject]] else{
return
}
finishCallBack(data: dicArray, error: nil)
}) { (
, error) -> Void in

            finishCallBack(data: nil, error: error)
    }
}

}
“`

  • 5.根据接收到的数据,来设置模型
    -6.自定义collectionViewCell
  • 7.加载跟多的数据,
    -方式一: 通过第三方的上下拉刷新框架

    • 方式二:当最后一个cell出现的时候,说明已经滑到了底部,通过索引来判断是否是最后一个cell.当最后一个cell出现,就发送请求获取数据
    abc.gif
  • 8.点击cell ,弹出图片浏览器
    • 搭建图片浏览器界面,底部两个按钮,和底层是collectionview
    • 自定义流水布局
    • 自定义Cell,
      注意通过案列发现,当点击Cell展出图片浏览器,滑动图片浏览器,当点击退出图片浏览器的时候,发现会产生动画回到当前的图片对应的小图片出,所以这里弹出的图片浏览器,不能够传递指定的图片,而是需要将模型数组和索引传递给图片浏览器

      • 1.设置cell之间的间距问题,如果是通过流水布局来设置最小间距,那么当collection滑动,由于collection的分页效果,collection的尺寸与cell的尺寸不匹配,会出现位置位置错乱,达不到分页的效果,这里可以设置view的宽度在原有的基础上+
        10,
        由于collectionView的尺寸基于view,流水布局设置的尺寸也是基于collection,所以这3个view的尺寸是一样的,而cell的图片的尺寸是根据屏幕尺寸来计算的,而collectionView的分页效果是和collection有关,这里讲view的宽度+
        10.这样就实现了间隔
        Snip20160523\_2.png

    -   2.当图片浏览器加载完毕,那么我可以根据索引,让图片浏览器滚动到对应的位置处
  • 9.设置转场动画,(在主页model出图片浏览器的过程中)
    • 给model出的控制器设置转场动画样式modalTransitionStyle
  • 10.自定义转场动画
    -1.设置转场动画代理为当前的控制器transitioningDelegate
    -2.通过代理方法,告诉系统弹出的view的动画交给谁去管理/告诉系统消失控制器的view的转场动画交给谁去管理

    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        //告诉系统,让谁来管理present的转场动画
        isPresent = true
        return self
    }
    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        isPresent = false
        return self
    }
    

    -3.实现转场代理方法
    -4.在设置动画方法中

    • 1.通过转场上下文,来获取presentd即将弹出的view
    • 2.将弹出的view添加到containerView上面
    • 注意:为什么需要添加到containerview上面,

      1.通过model的方式,来展示控制器的view,原理:是现将当前显示的view移除,再讲需要显示的控制器的view添加到containerView上面.

      2.因为我们需要自己来实现model的转场动画,所以系统不会主动的将presentedView弹出的view来添加到containverView上面,需要我们手动来添加
      -3.
      分析,弹入弹出动画,就是将一张图片,移动小图的位置到放大到图片浏览器中
      大图的位置

    • 弹出动画需要满足3各条件

      • 1.需要获取小图的图片
      • 2.需要知道小图的坐标(由于小图在collectionview)中,所以这里需要坐标的转换
      • 3.需要知道大图的位置
    • 当点击cell -> 先创建一个和点击Cell上图片一样大小的图片 ->
      在开始动画之前,将小图片containerView上面,位置需要转换 ->
      通过UIView的动画,动画的过程就是讲图片从点击cell出的位置放大到图片浏览器中图片的位置
    • 但是我们封装的工具类,无法获取到小图片,小图的位置,换算尺寸都大图的位置,因为是点击cell之后,需要来展示动画,我们可以通过cell的点击方法,将对应点击的索引传递给封装的动画类
    • 面向协议开发,封装的工具类无法自己获取必须的条件,可以通过代理来获取,在homeVc
      我们可以获取我们想要的条件,
    • 如何设置消失动画的代理?这里需要设置图片浏览器作为消失动画的代理
      -12.自定义消失动画
    • 消失动画需要满足3各条件
      • 1.获取当前显示的大图
      • 2.需要知道大图的位置
      • 3.需要知道结束小图的位置
    • 如果当前是消失动画,那么可以根据当前在图片浏览控制器中点击的cell的索引获取到1.当前显示的大图,就是间接的获取到了大图的位置2.通过索引我们可以去home控制器中获取到小图.

extension PhotoBrowserAnimation:UIViewControllerAnimatedTransitioning{

    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        //设置转场动画的时间
        return 1.0
    }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        if isPresent {
            guard let indexpath = indexpath ,presentDelegate = presentDelegate else{
                return
            }
            //设置转场动画
            //1.获取弹出的view
            let presentView = transitionContext.viewForKey(UITransitionContextToViewKey)
            //2.model的原理:将当前显示的view移除,将需要展示的View添加到containerView上面,由于我们是自定义的转场动画,需要自己来讲view添加到containerView上面
            //获取动画的图片
            let imageview = presentDelegate.presentImage(indexpath)
            //设置图片的尺寸和位置
            imageview.frame = presentDelegate.presentStartRect(indexpath)
            //展示图片
            transitionContext.containerView()?.addSubview(imageview)
            transitionContext.containerView()?.backgroundColor = UIColor.blackColor()
            //3.设置动画
            UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
                imageview.frame = presentDelegate.presentEndRect(indexpath)
                }) { (_) -> Void in
                    //需要告诉系统转动动画结束
                    transitionContext.containerView()?.backgroundColor = UIColor.clearColor()
                    transitionContext.containerView()?.addSubview(presentView!)
                    transitionContext.completeTransition(true)
                    imageview.removeFromSuperview()
            }
        }else{
            guard let indexpath = indexpath , dismissDelegate = dismissDelegate else{
                return
            }
            //获取消失的view
            let dismissView = transitionContext.viewForKey(UITransitionContextFromViewKey)
            //获取结束动画的图片
            let imageView = dismissDelegate.dismissImage(indexpath)
            //将图片添加到contrainerView
            transitionContext.containerView()?.addSubview(imageView)
            let srartIndepath = dismissDelegate.dismissEndRect()
            //获取起始小图的位置
            let endRect = presentDelegate!.presentStartRect(srartIndepath)
            dismissView?.alpha = endRect == CGRectZero ? 1.0 : 0.0
            UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
                if endRect == CGRectZero{
                    dismissView?.alpha = 0.0
                    imageView.alpha = 0.0
                }else{
                    imageView.frame = endRect
                }
                }, completion: { (_) -> Void in
                    dismissView?.removeFromSuperview()
                    transitionContext.completeTransition(true)
            })
        }
    }
}

111.gif

项目需求:做一个图片浏览器,点击图片查看大图,大图模式下,左右滚动能查看不同的图片.

项目的主要核心技术:图片的弹出和消失动画

 

图片 1

 

项目源代码: Photo-Browser

 

一.对代码进行重构

1.对代码进行抽取划分

     1.1 为什么要对代码进行抽取?

          swift中,代码全部写在一起,阅读性极差

     

2.如何对代码进行抽取?

     2.1在oc中,可以把功能模块抽取一个个方法

 

     2.2swift中,专门提供 extension ,可以对原有的类进行扩展

 

3.怎么使用extension 抽取代码?

    3.1
把一些方法写在extension(扩展)里面,这样能减少viewDidLoad里面的代码

 

     3.2 extension可以写多个,这样就可以把不同的功能模块
,写在不同的扩展里面

 

图片 2

 

二.项目基本设置

 

1.修改bundleID

2.部署版本

3.设置项目图片,启动图片

4.对文件夹目录进行划分

 

三.首页布局

 

1.让首页为UICollectionViewController

2.设置数据源

 

3.自定义布局

     3.1 创建一个源文件,继承自UICollectionViewFlowLayout

     3.2 重写 prepareLayout

     3.3 设置布局的相关属性

 

4.如何设置StoryBoard中的UICollectionViewController的布局

     4.1 在StoryBoard中选中collectionView 

     4.2 在属性里找到 layout 设置为自定义  custom

     4.3 在下面的class里面 把自定义布局的类名写进去即可

 

图片 3

 

四.网络工具类的封装

 

1.集成CocoaPods, 并导入AFNetworking框架

     1.1 打开终端,进入项目路径下  cd  路径

     1.2 创建PodFile文件  pod init

     1.3 配置PodFile文件 ,写入要导入的框架

 图片 4 

     

     1.4 导入框架 pod install —no-repo-update / 或 pod intall

          1.41 pod install 会更新本地库(本地已有的框架也会更新)
速度相对较慢

          1.42 pod install —no-repo-update
不会更新本地库,速度相对来说快点

 

2.封装工具类

     2.1 将工具类设计成单例对象

          防止别人修改

          防止多线程访问,创建多个对象

 

     2.2 swift中单例的设置方式

          static let shareInstance : NetworkTools = NetworkTools()

 

     2.3 可以让工具类,直接继承自用到框架的一个类

         
好处:自己就是这个类的子类,拥有这个类的所有方法和属性,用的时候直接自己就能调用

 

图片 5

 

3.封装网络请求方法

1 func requestData (type : Int , urlString : String , parameters : [ String : NSObject] , callBack : (result : AnyObject? , error : NSErroe?) -> ()   )
2 func reqeustData(type : RequestType, urlString : String, parameters : [String : NSObject], finishedCallback : (result : AnyObject?, error : NSError?) -> ()) {   } 

 

4.把方法里面的闭包抽取出来

     4.1 为什么要抽取?

          方法里面闭包很长,代码很乱,造成阅读性差

 

     4.2 怎么抽取?

          定义一个成员属性  为闭包类型   把方法里面的闭包,用属性名 替换

 

图片 6

 

五.项目集成工具类

 

     把封装好的工具类,直接拖到项目文件中

 

六.请求网络数据

 

1.在控制器中调用工具类封装好的网络请求方法

 

2.解析数据

     要对获取到的数据进行类型转换,应为从网络加载的数据类型为AnyObject

      guard let resultDict = result as? [String : NSObject] else {
                return
            }

            guard let dataArray = resultDict["data"] as? [[String : NSObject]] else {
                return
            }

3.字典转模型

     3.1 创建模型

     3.2 通过kvc手动转模型 , 要重写 override func setValue(value:
AnyObject?, forUndefinedKey key: String) {}

     3.3 注意: 在闭包中  self. 也不可以省略     

 

七,自定义cell,展示数据

 

1.创建cell继承自UICollectionViewCell 

2.在cell里面定义模型属性

3.监听属性改变(相当于oc的重写set方法)

     在属性监听器(willSet, didSet)  
这里用didSet方法里面给模型里面的属性赋值

 

八.加载更多数据

 

1.什么时候加载更多的数据?

     当最后一个cell出现的时候

 

2.怎么监听最后一个cell是否出现在屏幕上

     通过cell(item)的下标值(从0开始)是否等于数组长度 – 1   

   

   // 最后一个cell已经出现
        if indexPath.item == shops.count - 1 {  
           indexPath.item 相当于 tableView 的  indexPath.row 
            loadHomeData(shops.count)
        }

 

3.怎么加载更多数据

     和加载数据一样,只不过多传一个参数offset

 

九.弹出图片浏览器

 

1.创建图片浏览器的控制器对象UIViewController

2.弹出控制器

     2.1 监听cell的点击

     2.2 创建图片浏览器控制器对象

     2.3 设置图片浏览器控制器对象的弹出样式

     photoBrowserVc.modalTransitionStyle = .FlipHorizontal

     2.4 把控制器modal出来

 

十.布局图片浏览器

 

1.布局UICollectionView

     1.1 创建UICollectionView

     1.2 把UICollectionView添加到控制器的View上

     1.3 设置数据源

     1.4 自定义布局

 

2.布局两个按钮

     2.1 创建两个按钮

     2.2 设置按钮的frame 

 

     2.3 对UIButton进行extension(扩展)

          2.31 为什么要进行扩展

             
 创建出来的按钮,要设置图片,字体,和文字,一个个设置太麻烦,想让按钮创建出来就有这些属性

          2.32 怎么进行扩展?

               对UIButton进行extension(扩展)
扩充一个类型方法,在类方法里面封装好这些属性

   class func createBtn(title : String, bgColor : UIColor, fontSize : CGFloat) -> UIButton {
        let btn = UIButton()

        btn.backgroundColor = bgColor
        btn.setTitle(title, forState: .Normal)
        btn.titleLabel?.font = UIFont.systemFontOfSize(fontSize)

        return btn
    }

     

     2.4
这样创建还不是很方便,我们可以给UIbutton扩展构造函数,创建的时候直接设置这些属性

 

          2.41 注意:在extension中扩充构造函数,只能扩充便利构造函数     

     

          2.42 什么是便利构造函数?

               1.必须在init前面加上convenience

               2.必须在init方法中 调用self.init()

1     convenience init(title : String, bgColor : UIColor, fontSize : CGFloat) {
2         self.init()
3        
4         setTitle(title, forState: .Normal)
5         backgroundColor = bgColor
6         titleLabel?.font = UIFont.systemFontOfSize(fontSize)
7     }

 

3.监听按钮的点击

     3.1 xcode7.2 和xcode7.3中监听方法的写法不太一样

          Xcode7.2 –> 1> Selector(“方法的名称”) 2> “”

          Xcode7.3 –> #selector(类.方法名称)     

 

     3.2 如果点击按钮调用的方法前面加上private 调用会报错

          3.21 为什么会报错

               找不到方法

          

          3.22 监听事件实质就是发送一条消息 

               

          3.23 发送消息的过程是:

               1.将消息包装成@SEL
 2.通过@SEL去类中的方法列表中找对相应的方法(函数)

               

          3.34
在swift中,如果一个函数前面加上private,那么该函数就不会被添加到消息(映射)列表中

          

          3.35 如果在private前面加上@objc ,就会保留oc的特性,
该方法依然会添加到消息列表中

 

     3.3 解决问题的方法就是 在private前面加上@objc   或者不写private

     

 

十一.传递数据

 

1.传递什么数据?

     要在PhotoBrowserVc中查看大图,首先要拿到图片数据

 

2.怎么传递数据?直接传递图片?

     直接传递图片url也可以,不过要从模型数组中抽离出来,不太好

     最好的做法是:直接把模型数组传递给PhotoBrowserVc

     

十二.自定义PhotoBrowserCell,用于展示数据

 

1.PhotoBrowserVc是UICollectionViewController,要想展示图片,需要在cell上添加UIImageView

   
 注意:如果一个构造函数前有required,那么重写了其他构造函数时,那么该构造函数也必须被重写

 1     // MARK:- 重写构造函数
 2     override init(frame: CGRect) {
 3         super.init(frame: frame)
 4        
 5         setupUI()
 6     }
 7     // required : 如果一个构造函数前有required,那么重写了其他构造函数时,那么该构造函数也必须被重写
 8     required init?(coder aDecoder: NSCoder) {
 9         fatalError("init(coder:) has not been implemented")
10     }

 

2.要想展示图片,需要设置什么?

     2.1 设置UIImageView的image

          直接从 SDWebImage缓存中取出原来cell的图片(小图)

          注意:取出的图片类型是可选类型,要先进行判断再使用

 

     2.2 设置UIImageView的frame

          2.21
根据取出的图片的尺寸,计算图片的frame(设置UIImageView的宽度等于屏幕宽度)

          2.22 让图片的宽度等于UIImageView的宽度

          2.23 UIImageView的高度,就等于  图片高度 * UIImageView的宽度 /
图片宽度 (让图片等宽高比拉伸)

 

3.加载高清图片

     3.1 为什么要加载高清图片?

          上面取出的图片是小图,不清晰. 查看大图的时候,要换成高清图片

     

     3.2 怎么设置?

          用SDWebImage加载大图,把小图设置为占位图片

         
占位图片:图片还没加载的时候,先用内存中的一张图片显示到屏幕上,加载好图片,
就显示加载的图片

 

4.设置完成后,查看大图,发现滚动到后面,发现图片被压缩了,为什么?

     4.1 因为在MainVc(首页)展示小图的时候,给小图也设置了占位图片

     4.2
在PhotoBrowserVc中查看大图,滚动到后面的时候,MainVc中的cell还没显示,小图就不会被加载,就把占位图片赋值给小图 
     

1    // 2.获取小图片
2             var smallImage = SDWebImageManager.sharedManager().imageCache.imageFromDiskCacheForKey(shop.q_pic_url)
3             if smallImage == nil {
4                 smallImage = UIImage(named: "empty_picture")
5             }

   

   4.3
这是UIImageView的尺寸就是根据占位图片的尺寸计算出来的,跟实际图片的尺寸会有差别,实际显示的图片就可能被压缩

 

5.怎么解决图片压缩为题?

     大图请求成功时,重新计算UIImageView的尺寸就可以了

 

十三.把collectionView滚动到正确的位置

 

1.为什么要滚动collectionView?

     PhotoBrowserVc的cell是从第0个cell开始显示的,
所以每次点击查看大图都是从第0张图片开始显示

     当点击MainVc(首页)cell的时候,要显示对应的大图,不一定是第0张图片

 

2.怎么滚动?

     2.1 在PhotoBrowserVc中定义indexPath属性,
 在MainVc中拿到cell的indexPath,对 PhotoBrowserVc的indexPath赋值

     2.2 滚动到对应的位置(用下面这个方法)        

collectionView.scrollToItemAtIndexPath(indexPath!, atScrollPosition: .CenteredHorizontally, animated: false)

 

     2.3 滚动代码应该写到哪里?

    
     点击MainVc的cell,就弹出查看大图控制器(PhotoBrowserVc),就要滚动要对应的位置

          所以,代码可以写到viewDidLoad里面

 

  1.  ??的使用

      

1 // ?? : 先判断前面的可选链是否有值, 如果有值,解包并且获取对应类型的值. 如果没有值直接取后面的值
2         return shops?.count ?? 0

 

 

十四.设置大图之间的间距

 

1.怎么设置大图之间的间距?用 minimumLineSpacing?

     不可以,虽然能让大图之间有间距,但是会把后面的cell往后移
,后面的cell就不能完全显示在屏幕上

 

2.思考:可以collectionView的cell的宽度比屏幕宽度大一点,多出来的宽度就当做间距

   
 不可行,collectionView(scrollView)的分页效果,会让用户看到多出来的那部分

     scrollView分页效果的滚动距离  是由scrollView的宽度来决定的

 

3.最终解决方案

     3.1 只用一句代码就可以搞定,把控制器的view的宽度增大一点就可以了

          view.frame.size.with += 15

 

     3.2 注意collectionView的宽度和 cell的宽度
要等于控制器的view的宽度才可以

 

4.全局函数的定义

     4.1 什么是全局函数?

          就是在工程目录下的任何地方都能使用的函数

 

     4.2 怎么定义全局函数?

          只要把函数定义到AppDelegate里面就可以了

          

十五.保存图片

 

1.先要拿到对应的图片,根据indexPath拿?

     点击查看大图后可能会被滚动,所以不能根据indexPath拿

 

2.怎么拿到正在显示的图片

     2.1 先拿到正在显示的cell

     2.2 cell里面保存的就有image

 

3.怎么拿到cell

     可以通过苹果自带的api拿到正在显示的cell

     

1  // 1.1.拿到正在显示的Cell
2         // visibleCells 返回所有在屏幕中显示的Cell
3         let cell = collectionView.visibleCells()[0] as! PhotoBrowserViewCell
4         guard let image = cell.imageView.image else {
5             return
6         }

 

4.保存图片到相册

     苹果自带api保存到相册

UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)

 

十六.点击大图关闭控制器

 

1.需求:点击大图或关闭按钮,把控制器dismiss掉

 

2.点击按钮关闭

     给按钮设置点击方法就可以了  addTarget

 

3.点击大图关闭怎么实现?

     点击大图相当于点击了cell,在cell代理方法里面dismiss即可

 

十七.自定义转场(淡入淡出)

 

1.怎么自定义转场动画?

   
 遵守转场的代理协议UIViewControllerTransitioningDelegate,实现代理方法

 

2.实现了代理方法,发现程序还是报错,为什么?

   
 代理方法都有一个返回值,返回值要遵守一个协议UIViewControllerContextTransitioning才能作为返回值

 

3.在UIViewControllerContextTransitioning代理方法里面设置动画(具体看代码)

 

4.设置消失动画

     4.1 设置完显示动画后,消失的时候也自动会有一个动画效果,为什么?

          因为,大图view消失的时候,也是主控制器的view显示的时候

          看到的消失动画,实际上是主控制器的view的显示动画

     

     4.2 怎么判断是显示,还是消失?

          定义一个属性记录即可 
在UIViewControllerTransitioningDelegate代理方法中记录

 

 1 // MARK:- 遵守转场的代理协议,和实现对应的方法
 2 extension HomeViewController : UIViewControllerTransitioningDelegate {
 3     // 为弹出控制器做一个动画
 4     func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
 5      //记录当前为显示阶段
 6      isPresented = true
 7         return self
 8     }
 9   
10     // 为消失控制器做一个动画
11     func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
12       //记录当前为消失阶段
13       isPresented = false
14         return self
15     }
16 }
17  
18 extension HomeViewController : UIViewControllerAnimatedTransitioning {
19     // 返回动画执行的时间
20     func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
21         return 3
22     }
23    
24     // transitionContext : 转场上下文
25     // 作用 : 可以通过上下文获取到弹出的View和消失的View
26     // UITransitionContextFromViewKey : 获取消失的View
27     // UITransitionContextToViewKey : 获取弹出的View
28     func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
29         if isPresented {
30             // 获取弹出的View
31             let presentedView = transitionContext.viewForKey(UITransitionContextToViewKey)!
32            //需要把view添加到父控件上,才能有动画效果  
33           //父控件就是widow的containerView,    通过transitionContext.containerView()拿到
34             transitionContext.containerView()?.addSubview(presentedView)
35            
36             // 修改View alpha值
37             presentedView.alpha = 0.0
38            
39             // 执行动画
40             UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
41                 presentedView.alpha = 1.0
42             }) { (isFinished : Bool) in
43                //告诉控制器,转场动画完成
44                 transitionContext.completeTransition(isFinished)
45             }
46         } else {
47             // 1.获取消失的View
48             let dismissedView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
49            
50             // 2.执行动画
51             UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
52                 dismissedView.alpha = 0.0
53                 }, completion: { (isFinished : Bool) in
54                     //移除view,显示主控制器的view
55                     dismissedView.removeFromSuperview()
56                     transitionContext.completeTransition(isFinished)
57             })
58         }
59     }
60 }

* *

5.性能优化,代码抽取

     5.1
把转场动画的代理设置为主控制器,代理要全部写在主控制器中,代码臃肿,阅读性差

     5.2 怎么优化?

          把代理设置为其它对象,让其它对象实现代理方法即可

 

     5.3 具体实现步骤

          5.31 创建一个对象(任何对象)

          5.32 设置这个对象为转场动画的代理

          5.33 在对象中实现代理方法即可

          5.34 注意:代理属性为弱引用,要让一个强引用指向它

 

十八.最终动画效果

 

1.想要做动画,必须要拿到三个元素

     1.1 图片的起始位置(相对于控制器view的坐标系)

     1.2 图片的终点位置(相对于控制器view的坐标系)

     1.3 转场图片的父控件UIImageView

 

2.弹出动画

     2.1 获取动画的三个元素

          图片的起始位置,终点位置和UIImageView
只有主控制器(mainVc)最清楚,可以定义代理

 

     2.2 mainVc成为动画代理对象的代理,提供三个元素

 

3.消失动画

     3.1 消失的时候,图片的终点位置有可能发生变化,需要重新计算

 

     3.2 怎么计算消失的图片的终点位置?

          只要拿到对应cell的indexPath就可以计算位置

 

     3.3 怎么拿到indexPath?

         
cell的indexPath只有PhotoBrowserVc最清楚,可以设置代理,让PhotoBrowserVc提供indexPath

          indexPath就是最后显示在屏幕上cell的indexPath

      // 1.获取在屏幕中显示的cell

        let cell = collectionView.visibleCells()[0]  

        // 2.获取cell对应的indexPath

        let indexPath = collectionView.indexPathForCell(cell)!

 

     3.4 根据indexPath计算终点位置,完成动画

 

4.性能优化

     4.1
当查看大图滚动的时候,indexPath会大于屏幕上显示的indexPath最大值,这个时候,就获取不到终点位置,就会没有消失动画(直接消失)

          不滚动的时候消失的时候,图片是不清晰的图片          

 

     4.2 为什么直接消失?

          不滚动的时候,设置的图片是小图,所以不清晰

          返回的时候获取不到cell,获取不到cell就直接返回空的ImageView   

          ImageView还没有设置图片就直接返回了

     

     4.3 怎么解决?

          在PhotoBrowserVc中可以拿到高清图片

          设置代理拿到Image,消失动画的时候,直接显示高清图片

 

     4.4 消失的时候,发现还是直接消失为什么?

          因为滚动到后面,mainVc的cell不在屏幕上,就获取不到cell,
所以消失时获取的startRect = CGRectZero  

          从0消失到0 所以没有动画

 

      4.5
点击mainVc最后一个cell,看看大图,往后滚,返回的时候,发现消失动画最终消失到左上角为什么?

          因为,后面的cell还没出现,就获取不到最终位置,系统默认在左上角

   

     4.6 怎么解决?

          方法一: 当超出的时候,给定一个终点位置,让它在指定的位置消失

                    效果可以,但是满足不了需求

     

     4.7 最终方案(参考微信的解决方案)

          当获取不到终点位置的时候,让图片消失的动画 设置为渐变消失动画

 

十九.版本适配bug的解决

 

1.当项目运行到6s Plus上的时候,collectionView只能显示两列(需求是三列)

     产生bug的原因是苹果对临界值得处理不太好

   
 具体来说就是,屏幕宽度三等分,得到的数值是无限循环小数,苹果会根据数据类型对小数向前进一位

   
 这时,屏幕的宽度就不足以放三个cell,就会把第三个cell挤到下一行显示,就变成了两列

 

2.bug解决

     让得到的cell的宽度减去一个临界值小数即可(0.000001)  这个数值随便写

 

 

 项目源代码: Photo-Browser

 

 

相关文章

Leave a Reply

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