swift之图片浏览器

简介: swift之图片浏览器

swift之图片浏览器


ea51e7b9ab84a8f11aa7232b1094aca1.jpg

start.gif

1.通过collectionView设置


725ffbfdf33f22bd57c7dea6d114321c.png

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

相关文章
|
6月前
|
计算机视觉 C++
基于Qt的简易图片浏览器设计与实现
基于Qt的简易图片浏览器设计与实现
289 1
|
JavaScript 对象存储
在阿里云OpenAPI 为什么oss 图片链接, 在浏览器访问直接下载了,不是预览呢?
在阿里云OpenAPI 为什么oss 图片链接, 在浏览器访问直接下载了,不是预览呢?
1308 1
|
3月前
|
XML 缓存 JSON
为什么浏览器中有些图片、PDF等文件点击后有些是预览,有些是下载
为什么浏览器中有些图片、PDF等文件点击后有些是预览,有些是下载
244 0
|
5月前
|
Web App开发 JavaScript 前端开发
使用 JS 实现在浏览器控制台打印图片 console.image()
在前端开发过程中,调试的时候,我们会使用 console.log 等方式查看数据。但对于图片来说,仅靠展示的数据与结构,是无法想象出图片最终呈现的样子的。 虽然我们可以把图片数据通过 img 标签展示到页面上,或将图片下载下来进行预览。但这样的调试过程实在是复杂,何不实现一个 console.image() 呢?
118 1
使用 JS 实现在浏览器控制台打印图片 console.image()
|
4月前
|
Web App开发 前端开发
canvas保存图片时,谷歌浏览器Chrome报错【解决方案】Not allowed to navigate top frame to data URL
canvas保存图片时,谷歌浏览器Chrome报错【解决方案】Not allowed to navigate top frame to data URL
127 0
|
5月前
|
缓存 算法 API
视觉智能开放平台产品使用合集之如何在Web浏览器中查看处理后的图片
视觉智能开放平台是指提供一系列基于视觉识别技术的API和服务的平台,这些服务通常包括图像识别、人脸识别、物体检测、文字识别、场景理解等。企业或开发者可以通过调用这些API,快速将视觉智能功能集成到自己的应用或服务中,而无需从零开始研发相关算法和技术。以下是一些常见的视觉智能开放平台产品及其应用场景的概览。
158 0
|
6月前
|
域名解析 应用服务中间件 对象存储
解决阿里云oss图片浏览器访问直接下载而不是打开
解决阿里云oss图片浏览器访问直接下载而不是打开
3312 0
|
6月前
|
移动开发 JavaScript
微信公众号H5开发,在微信浏览器打开H5,无法一键下载图片
微信公众号H5开发,在微信浏览器打开H5,无法一键下载图片
210 0
|
JavaScript
Qt图片浏览器
可以显示jpg、jpeg、png、bmp。可以从电脑上拖动图到窗口并显示出来或者打开文件选择 重载实现dragEnterEvent(拖拽)、dropEvent(拖拽放下)、resizeEvent(窗口大小改变)
107 0
|
Swift iOS开发
iOS PhotoBrowser 横竖屏图片浏览器
iOS PhotoBrowser 横竖屏图片浏览器
154 0