美文网首页
swift之图片浏览器

swift之图片浏览器

作者: 凡尘一笑 | 来源:发表于2019-07-10 11:16 被阅读0次
    start.gif

    1.通过collectionView设置


    image.png

    在ViewController这个类里面展示九宫格

    创建UICollectionView,UICollectionViewCell

    import UIKit
    //标示�ID
    private let cellID = "cellID"
    //间隔
    private let margin : CGFloat = 10
    
    class ViewController: UIViewController {
        //collecitonView
        lazy var collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: PhotoCellFlowLayout())
        //存放图片url的数组
        var urlArr : [URL] = [URL]()
    
    
    extension ViewController {
        private func setupUI() {
            view.addSubview(collectionView)
            collectionView.frame = view.bounds
            collectionView.dataSource = self
            collectionView.delegate = self
            collectionView.backgroundColor = UIColor.white
            collectionView.register(PhotoCell.self, forCellWithReuseIdentifier: cellID)
        }
    }
    
    
    extension ViewController : UICollectionViewDataSource{
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return urlArr.count
        }
        
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            
            let cell  = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! PhotoCell
    
            cell.url = urlArr[indexPath.item]
        
            return cell
        }
    }
    
    //在同一个类里面写个UICollectionViewCell类
    //自定义cell
    class PhotoCell: UICollectionViewCell {
        //设置cell的属性为url
        @objc var url : URL?{
            didSet{
               guard let picUrl = url else { return }
                //下载图片,并设置图片
                pictureImageView.sd_setImage(with: picUrl, placeholderImage: UIImage(named: ""), options: [], completed: nil)
                
            }
        }
        //图片属性
        var pictureImageView : UIImageView = UIImageView()
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupUI()
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        func setupUI() {
            pictureImageView.frame = contentView.bounds
            contentView.addSubview(pictureImageView)
            pictureImageView.contentMode = .scaleAspectFill
            pictureImageView.clipsToBounds = true
        }
    }
    
    
    //在同一个类里面写个UICollectionViewFlowLayout类
    //自定义布局
    class PhotoCellFlowLayout : UICollectionViewFlowLayout{
        override func prepare() {
            super.prepare()
            //三个item
            let width = (UIScreen.main.bounds.width - 4 * margin)/3
            let heigth = width
            //item的大小
            itemSize = CGSize(width: width, height: heigth)
            //行间距
            minimumLineSpacing = margin
            //竖间距
            minimumInteritemSpacing = margin
            //上下左右间距
            sectionInset = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin)
            //竖滚动条
            collectionView?.showsVerticalScrollIndicator = false
            //横滚动条
            collectionView?.showsHorizontalScrollIndicator = false
        
        }
    }
    
    

    展示大图的控制器PhotoBrowerViewController

    • 也使用UICollectionView来展示
    • 同时也在这个类里面,写UICollectionViewFlowLayout
    • 自定义UICollectionViewCell
    • 自定义cell里面设置一个UIScrollView,UIScrollView里面设置一个UIImageView比如长图需要滚动,同时给图片增加手势,让cell拥有一个代理对象,然后UICollectionView实现代理,即使执行关闭按钮点击事件
    
    import UIKit
    import SnapKit
    import SVProgressHUD
    
    private let photoBrowerCellID = "photoBrowerCellID"
    class PhotoBrowerViewController: UIViewController {
    
        lazy var collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: PhotoBrowerCellFlowLayout())
        lazy var saveBtn = UIButton(bgColor: UIColor.lightGray, font: 14, title: "保存")
        lazy var closeBtn = UIButton(bgColor: UIColor.lightGray, font: 14, title: "关闭")
        var urlArr : [URL] = [URL]()
        var indexpath : IndexPath
        
        //构造函数,传进图片数组和下标
        init(indexPath:IndexPath,urlArr:[URL]) {
            
            self.urlArr = urlArr
            self.indexpath = indexPath
            //控制器的构造函数要重写父类的这个方法
            super.init(nibName: nil, bundle: nil)
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        override func loadView() {
            super.loadView()
            //屏幕尺寸增加20的宽度。这里是为了item的间距,到时候cell的srollview还要减去20
            view.frame.size.width += 20
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            setupUI()
            //滚动到对应cell
            collectionView.scrollToItem(at:indexpath as IndexPath, at: .left, animated: false)
    
        }
        
    }
    
    extension PhotoBrowerViewController{
        func setupUI() {
            
            view.addSubview(collectionView)
            view.addSubview(saveBtn)
            view.addSubview(closeBtn)
            //保存按钮的约束
            saveBtn.snp.makeConstraints { (make) in
                make.right.equalTo(-40)
                make.bottom.equalTo(-20)
                make.size.equalTo(CGSize(width: 90, height: 35))
            }
            //关闭按钮的约束
            closeBtn.snp.makeConstraints { (make) in
                make.left.equalTo(20)
                make.bottom.equalTo(-20)
                make.size.equalTo(CGSize(width: 90, height: 35))
            }
        
            collectionView.frame = view.bounds
            //给保存按钮增加点击事件
            collectionView.dataSource = self
            collectionView.delegate = self
            //注册cell
            collectionView.register(PhotoBrowerCollectionViewCell.self, forCellWithReuseIdentifier: photoBrowerCellID)
            //给关闭按钮增加点击事件
            closeBtn.addTarget(self, action: #selector(PhotoBrowerViewController.closeBtnClick), for: .touchUpInside)
            //给保存按钮增加点击事件
            saveBtn.addTarget(self, action: #selector(PhotoBrowerViewController.saveBtnClick), for: .touchUpInside)
        }
    }
    
    extension PhotoBrowerViewController {
        //关闭按钮点击事件
        @objc func closeBtnClick() {
            dismiss(animated: true, completion: nil)
        }
        
        //保存图片按钮点击事件
        @objc func saveBtnClick() {
            //获取展示的cell
            let cell = collectionView.visibleCells.first as! PhotoBrowerCollectionViewCell
            //获取cell中的图片
            let picture = cell.pictureImageView.image
            //校验
            guard let pictureImage = picture else { return }
            //保存到相册
            UIImageWriteToSavedPhotosAlbum(pictureImage, self, #selector(image(image:didFinishSavingWithError:contextInfo:)), nil)
        }
        
        @objc private func image(image : UIImage, didFinishSavingWithError error : NSError?, contextInfo context : AnyObject) {
            // 1.判断是否有错误
            let message = error == nil ? "保存成功" : "保存失败"
            // 2.显示保存结果
            SVProgressHUD.showInfo(withStatus: message)
        }
    }
    
    extension PhotoBrowerViewController : UICollectionViewDataSource,UICollectionViewDelegate{
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            print(urlArr.count)
            return urlArr.count
        }
        
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            
            let  cell  = collectionView.dequeueReusableCell(withReuseIdentifier: photoBrowerCellID, for: indexPath) as! PhotoBrowerCollectionViewCell
            //图片的url
            cell.url = urlArr[indexPath.item]
            //设置代理 手势代理
            cell.delegate = self as PhotoBrowerCollectionViewCellDelegate
            
            return cell
        }
    }
    
    //cell 手势图片点击的代理
    extension PhotoBrowerViewController : PhotoBrowerCollectionViewCellDelegate {
        func pictureClick() {
            //关闭按钮点击
            closeBtnClick()
        }
    }
    
    //在同一个类里面写个UICollectionViewFlowLayout类
    //布局
    class PhotoBrowerCellFlowLayout: UICollectionViewFlowLayout {
    
        override func prepare() {
            super.prepare()
            //设置item的大小
            itemSize = (collectionView?.frame.size)!
            //行间距
            minimumLineSpacing = 0
            //列间距
            minimumInteritemSpacing = 0
            //滚动方向
            scrollDirection = .horizontal
            //竖滚动条
            collectionView?.showsVerticalScrollIndicator = false
            //横滚动条
            collectionView?.showsHorizontalScrollIndicator = false
            //分页
            collectionView?.isPagingEnabled = true
        }
    }
    
    

    cell的自定义

    
    import UIKit
    import SDWebImage
    
    protocol PhotoBrowerCollectionViewCellDelegate : NSObjectProtocol {
        func pictureClick()
    }
    
    class PhotoBrowerCollectionViewCell: UICollectionViewCell {
        
            @objc var url : URL? {
                didSet{
                    guard let picUrl = url else { return }
                    
                    let picture = SDWebImageManager.shared().imageCache?.imageFromCache(forKey:picUrl.absoluteString)
                    
                    guard let pictureImage = picture else { return }
                    // 3.计算imageView的位置和尺寸
                    calculateImageFrame(image: pictureImage)
    
                    pictureImageView.sd_setImage(with: url, placeholderImage: UIImage(named: ""), options: [], progress: { (current, total, _) in
                        
                    }) { (image, _, _, _) in
                        if image != nil {
                            self.calculateImageFrame(image:image!)
                            self.pictureImageView.image = image
                        }
                    }
                    
                }
            }
        
            /// 计算imageView的frame和显示位置
            private func calculateImageFrame(image : UIImage) {
                // 1.计算位置
                let imageWidth = UIScreen.main.bounds.width
                let imageHeight = image.size.height / image.size.width * imageWidth
                
                // 2.设置frame
                pictureImageView.frame = CGRect(x: 0, y: 0, width: imageWidth, height: imageHeight)
                // 3.设置contentSize
                scrollView.contentSize = CGSize(width: imageWidth, height: imageHeight)
                
                // 4.判断是长图还是短图
                if imageHeight < UIScreen.main.bounds.height { // 短图
                    // 设置偏移量
                    let topInset = (UIScreen.main.bounds.height - imageHeight) * 0.5
                    scrollView.contentInset = UIEdgeInsets(top: topInset, left: 0, bottom: 0, right: 0)
                } else { // 长图
                    scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
                }
            }
        
            //scrollView
            var scrollView = UIScrollView()
            //图片
            var pictureImageView = UIImageView()
            //代理
            var delegate : PhotoBrowerCollectionViewCellDelegate?
        
            override init(frame: CGRect) {
                super.init(frame: frame)
                setupUI()
            }
            
            required init?(coder aDecoder: NSCoder) {
                fatalError("init(coder:) has not been implemented")
            }
            
            func setupUI() {
                contentView.addSubview(scrollView)
                scrollView.addSubview(pictureImageView)
                scrollView.frame = bounds
                scrollView.frame.size.width -= 20
               
                //给图片添加手势
                pictureImageView.isUserInteractionEnabled = true
                let tap = UITapGestureRecognizer(target: self, action: #selector(pictureClick))
                pictureImageView.addGestureRecognizer(tap)
            }
        
            @objc func pictureClick() {
                delegate?.pictureClick()
            }
    }
    
    

    PhotoBrowerAnmation这个类来执行动画

    • 注意,这里只是拿对应图片做动画,动画结束后要把做动画的图片移除

    • 申明两个协议。弹出动画协议,消失动画协议

    • 弹出动画的协议为了拿到动画开始的起始位置,动画结束的结束位置,哪个图片做动画

    • 消失动画的协议为了拿到做动画的图片,拿到做动画图片的下标

    • 最后去到对应控制器去拿到这两个动画协议里面需的东西

    import UIKit
    
    class PhotoBrowerAnmation: NSObject {
        
        var isPresented : Bool = false
        
        // 定义indexPath和presentedDelegate属性
        var indexPath : NSIndexPath?
        // 定义弹出的presentedDelegate
        var presentedDelegate : PhotoBrowserPresentedDelegate?
        // 定义消失的DismissDelegate
        var dismissDelegate : PhotoBrowserDismissDelegate?
    
    }
    
    protocol PhotoBrowserPresentedDelegate : NSObjectProtocol {
        // 1.提供弹出的imageView
        func imageForPresent(indexPath : NSIndexPath) -> UIImageView
        
        // 2.提供弹出的imageView的frame
        func startRectForPresent(indexPath : NSIndexPath) -> CGRect
        
        // 3.提供弹出后imageView的frame
        func endRectForPresent(indexPath : NSIndexPath) -> CGRect
    }
    
    protocol PhotoBrowserDismissDelegate : NSObjectProtocol {
        // 1.提供退出的imageView
        func imageViewForDismiss() -> UIImageView
        
        // 2.提供退出的indexPath
        func indexPathForDismiss() -> NSIndexPath
    }
    
    
    
    
    extension PhotoBrowerAnmation : UIViewControllerTransitioningDelegate {
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            isPresented = true
            return self
        }
        
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            isPresented = false
            return self
        }
    }
    
    extension PhotoBrowerAnmation : UIViewControllerAnimatedTransitioning {
    
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 1
        }
    
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            
            isPresented ? presentedView(using: transitionContext) : dissmissedView(using: transitionContext)
        }
        
        
        func presentedView(using transitionContext: UIViewControllerContextTransitioning) {
            
            guard let presentedDelegate = presentedDelegate, let indexPath = indexPath else {
                return
            }
            
            // 1.取出弹出的View
            let presentedView = transitionContext.view(forKey: .to)
            transitionContext.containerView.addSubview(presentedView!)
            //取图片做动画
            let tempImageView = presentedDelegate.imageForPresent(indexPath: indexPath)
            transitionContext.containerView.addSubview(tempImageView)
            
            tempImageView.frame = presentedDelegate.startRectForPresent(indexPath: indexPath)
            
            // 3.执行动画
            presentedView!.alpha = 0.0
            transitionContext.containerView.backgroundColor = UIColor.black
            UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
                tempImageView.frame = presentedDelegate.endRectForPresent(indexPath: indexPath)
            }) { (_) in
                transitionContext.containerView.backgroundColor = UIColor.clear
                transitionContext.completeTransition(true)
                tempImageView.removeFromSuperview()
                presentedView!.alpha = 1.0
            }
    
        }
        
        
        func dissmissedView(using transitionContext: UIViewControllerContextTransitioning) {
            
            guard let dismissDelegate = dismissDelegate, let presentedDelegate = presentedDelegate else {
                return
            }
            
            // 1.取出消失的View
            let dismissView = transitionContext.view(forKey: .from)
            dismissView?.alpha = 0
            //取出图片做动画
            let tempImageView = dismissDelegate.imageViewForDismiss()
            transitionContext.containerView.addSubview(tempImageView)
            
            // 2.执行动画
            UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
                tempImageView.frame = presentedDelegate.startRectForPresent(indexPath: dismissDelegate.indexPathForDismiss())
    
            }) { (_) in
                tempImageView.removeFromSuperview()
                dismissView?.removeFromSuperview()
                transitionContext.completeTransition(true)
            }
    
        }
    }
    
    
    • 弹出动画的执行代理是viewController,就是那个九宫格的那个控制器,因为它可以拿到图片,拿到下标,拿到起始动画开始的起始位置

    
    extension ViewController : UICollectionViewDelegate {
        
        func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
            
            let photoVC = PhotoBrowerViewController(indexPath: indexPath, urlArr: urlArr)
            //自定义动画
            photoVC.modalPresentationStyle = .custom
            //设置谁负责动画
            photoVC.transitioningDelegate = animation
            // 4.设置photoBrowserAnimator的相关属性
            animation.indexPath = indexPath as NSIndexPath
            //设置弹出动画的代理
            animation.presentedDelegate = self
            //设置消失动画的代理
            animation.dismissDelegate = (photoVC as PhotoBrowserDismissDelegate)
            //弹出控制器
            present(photoVC, animated: true, completion: nil)
        }
        
    }
    
    
    // MARK:- 用于提供动画的内容
    extension ViewController : PhotoBrowserPresentedDelegate {
        func imageForPresent(indexPath: NSIndexPath) -> UIImageView {
            // 1.创建用于做动画的UIImageView
            let imageView = UIImageView()
            
            // 2.设置imageView属性
            imageView.contentMode = .scaleAspectFill
            imageView.clipsToBounds = true
            
            // 3.设置图片
            imageView.sd_setImage(with: urlArr[indexPath.item], placeholderImage: UIImage(named: "empty_picture"))
            
            return imageView
        }
        
        func startRectForPresent(indexPath: NSIndexPath) -> CGRect {
            // 1.取出cell
            guard let cell = collectionView.cellForItem(at: indexPath as IndexPath) else {
                return CGRect(x: collectionView.bounds.width * 0.5, y: UIScreen.main.bounds.height + 50, width: 0, height: 0)
            }
            
            // 2.计算转化为UIWindow上时的frame
            let startRect = collectionView.convert(cell.frame, to: UIApplication.shared.keyWindow)
            
            
            return startRect
        }
        
        func endRectForPresent(indexPath: NSIndexPath) -> CGRect {
            // 1.获取indexPath对应的URL
            let url = urlArr[indexPath.item]
            
            // 2.取出对应的image
            var image = SDWebImageManager.shared().imageCache!.imageFromDiskCache(forKey: url.absoluteString)
            
            if image == nil {
                image = UIImage(named: "empty_picture")
            }
            
            // 3.根据image计算位置
            let screenW = UIScreen.main.bounds.width
            let screenH = UIScreen.main.bounds.height
            let imageH = screenW / image!.size.width * image!.size.height
            var y : CGFloat = 0
            if imageH < screenH {
                y = (screenH - imageH) * 0.5
            } else {
                y = 0
            }
            
            return CGRect(x: 0, y: y, width: screenW, height: imageH)
        }
    }
    
    
    • 消失动画的执行代理是PhotoBrowerViewController,就展示大图的控制器

    extension PhotoBrowserController : PhotoBrowserDismissDelegate {
        func imageViewForDismiss() -> UIImageView {
            // 1.创建UIImageView对象
            let tempImageView = UIImageView()
            
            // 2.设置属性
            tempImageView.contentMode = .ScaleAspectFill
            tempImageView.clipsToBounds = true
            
            // 3.设置图片
            let cell = collectionView.visibleCells()[0] as! PhotoBrowserCell
            tempImageView.image = cell.imageView.image
            tempImageView.frame = cell.scrollView.convertRect(cell.imageView.frame, toCoordinateSpace: UIApplication.sharedApplication().keyWindow!)
            
            return tempImageView
        }
        
        func indexPathForDismiss() -> NSIndexPath {
            return collectionView.indexPathsForVisibleItems()[0]
        }
    }
    
    

    代码留给你

    https://gitee.com/lanyingwei/codes/rj1w83inc6h74oydmsugz34

    • 备注:

    如果有不足或者错误的地方还望各位读者批评指正,可以评论留言,笔者收到后第一时间回复。

    QQ/微信:2366889552 /lan2018yingwei。

    简书号:凡尘一笑:[简书]

    感谢各位观众老爷的阅读,如果觉得笔者写的还凑合,可以关注或收藏一下,不定期分享一些好玩的实用的demo给大家。

    文/凡尘一笑(简书作者)

    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”

    相关文章

      网友评论

          本文标题:swift之图片浏览器

          本文链接:https://www.haomeiwen.com/subject/ptkckctx.html