1.通过collectionView设置
在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给大家。
文/凡尘一笑(简书作者)
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”