ZLPhotoBrowser 源码分析: 图片旋转

1,486 阅读2分钟

本文简单分析下 ZLPhotoBrowser 的源代码,

其中的图片旋转,使用的是 ZLClipImageViewController

ZLPhotoBrowser 代码比较绕,很有意思

特色:

大部分封装的功能控制器,使用的入口是 ZLPhotoPreviewSheet

例外:

图片预览控制器, 使用 ZLImagePreviewController,可方便地直接调用

照相机,使用 ZLCustomCamera,可方便地直接调用

1.1 , 图片旋转控制器的官方调用流程

先进入预览界面

ZLPhotoPreviewSheet 类里面,

func showPreviewController(_ models: [ZLPhotoModel], index: Int) {
        let vc = ZLPhotoPreviewController(photos: models, index: index)
        let nav = self.getImageNav(rootViewController: vc)
        // ...
        self.sender?.showDetailViewController(nav, sender: nil)
    }

预览界面,进入编辑界面, ZLEditImageViewController


func showEditImageVC(image: UIImage) {
        let model = self.arrDataSources[self.currentIndex]
        let nav = self.navigationController as! ZLImageNavController
        ZLEditImageViewController.showEditImageVC(parentVC: self, image: image, editModel: model.editImageModel) { [weak self, weak nav] (ei, editImageModel) in
            // ...
        }
    }

编辑界面, 进入旋转界面 ZLClipImageViewController

1.1.1 , 预览界面,进入旋转界面, ZLClipImageViewController

简单调整下,官方的代码

ZLEditImageViewController 类里面,

 @objc public class func showEditImageVC(parentVC: UIViewController?, animate: Bool = false, image: UIImage, editModel: ZLEditImageModel? = nil, completionX: ( (UIImage, ZLEditImageModel?) -> Void )? ) {
    
            let vc = ZLClipImageViewController(image: image, editRect: editModel?.editRect, angle: editModel?.angle ?? 0, selectRatio: editModel?.selectRatio)
            // ...
            vc.animate = animate
            vc.modalPresentationStyle = .fullScreen
            parentVC?.present(vc, animated: animate, completion: nil)
      
    }

ZLPhotoBrowser的这段逻辑,很有特色

ZLEditImageViewController 什么用,都没有

只是简单的中转一下

1.2 , 直接去编辑控制器

ZLPhotoPreviewSheet 类里面,

稍微调整下

点击 demo 的 ViewController 的,中间的列表的 item

@objc public func previewAssets(sender: UIViewController, assets: [PHAsset], index: Int, isOriginal: Bool, showBottomViewAndSelectBtn: Bool = true) {
        let models = assets.removeDuplicate().map { (asset) -> ZLPhotoModel in
            let m = ZLPhotoModel(asset: asset)
            m.isSelected = true
            return m
        }
        self.arrSelectedModels.removeAll()
        self.arrSelectedModels.append(contentsOf: models)
        self.sender = sender
        self.isSelectOriginal = isOriginal
        self.isHidden = true
        self.sender?.view.addSubview(self)
        if arrSelectedModels.count > 0{
            shouldDirectEdit(arrSelectedModels[0])
        }
    }

进入 ZLEditImageViewController

func shouldDirectEdit(_ model: ZLPhotoModel) -> Bool {
        self.showEditImageVC(model: model)
        return false
    }
1.2.1 , 直接去, 图片旋转控制器

稍微调整下,上面的代码

直接去 ZLClipImageViewController


func showEditImageVC(model: ZLPhotoModel) {
   
        
        ZLPhotoManager.fetchImage(for: model.asset, size: model.previewSize) { [weak self] (image, isDegraded) in
            
                if let image = image {
                    let editModel = model.editImageModel
                    let vc = ZLClipImageViewController(image: image, editRect: editModel?.editRect, angle: editModel?.angle ?? 0)
                    vc.clipDoneBlockZz = { [weak self]  (angle) in
                        let ei = image.clipImage(angle) ?? image
                        let editImageModel = ZLEditImageModel(editRect: CGRect.zero, angle: angle)
                        model.isSelected = true
                        model.editImage = ei
                        model.editImageModel = editImageModel
                        self?.arrSelectedModels.append(model)
                        self?.requestSelectPhoto()
                    }
                    vc.modalPresentationStyle = .fullScreen
                    self?.sender?.present(vc, animated: true, completion: nil)
                    
                } 
                // ...
        }
    }

图片旋转分析

ZLClipImageViewController 控制器的

func rotateBtnClick() 方法中,

每一次旋转,都把数据给修改了

self.editImage = self.editImage.rotate(orientation: .left)

// ...
self.imageView.image = self.editImage

这里的处理,比较重,

常用思路,可以记录旋转的角度,设置 imageView 的 transfrom

需要的时候,再修改数据

优点: 伪动画

这里的旋转,是改图片数据,重新布局

还创建一个临时的 imageView ,做动画

        let transform = CGAffineTransform(rotationAngle: -CGFloat.pi/2)
        let animateImageView = UIImageView(image: self.editImage)
        animateImageView.contentMode = .scaleAspectFit
        animateImageView.clipsToBounds = true
        view.addSubview(animateImageView)
        
        
       self.containerView.alpha = 0 
       UIView.animate(withDuration: 0.3, animations: {
            animateImageView.transform = transform
            animateImageView.frame = toFrame
        }) { (_) in
            animateImageView.removeFromSuperview()
            self.containerView.alpha = 1
        }
滚动视图代理方法,用的好

func viewForZooming(in scrollView: UIScrollView) -> UIView?

可优化

各种布局计算,七算八算,绕

没有利用到,AVFoundationAVMakeRect 方法

简化后,去除效果的代码

github repo