iOS 自定义相机,拍照旋转

2,712 阅读2分钟

1 , 自定义相机,拿到照片,校正方向

AVCapturePhotoCaptureDelegate 的这个代理方法

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) 

拿照片,


extension ZLCustomCamera: AVCapturePhotoCaptureDelegate {
    
    
    public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        if error != nil {
            return
        }
        
        if let imgData = photo.fileDataRepresentation(){
            self.session.stopRunning()
            if let img = UIImage(data: imgData){
                self.takedImage = img.fixOrientation()
            }
            self.takedImageView.image = self.takedImage
            self.takedImageView.isHidden = false
            self.resetSubViewStatus()
            
            
        }
    }
    
    
}

拿到照片后,让照片的头部朝上

extension UIImage {
    
    // 修复转向
    func fixOrientation() -> UIImage {
        if self.imageOrientation == .up {
            return self
        }
        
        var transform = CGAffineTransform.identity
        
        switch self.imageOrientation {
        case .down, .downMirrored:
            transform = CGAffineTransform(translationX: self.size.width, y: self.size.height)
            transform = transform.rotated(by: .pi)
        
        case .left, .leftMirrored:
            transform = CGAffineTransform(translationX: self.size.width, y: 0)
            transform = transform.rotated(by: CGFloat.pi / 2)
            
        case .right, .rightMirrored:
            transform = CGAffineTransform(translationX: 0, y: self.size.height)
            transform = transform.rotated(by: -CGFloat.pi / 2)
            
        default:
            break
        }
        
        switch self.imageOrientation {
        case .upMirrored, .downMirrored:
            transform = transform.translatedBy(x: self.size.width, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)
            
        case .leftMirrored, .rightMirrored:
            transform = transform.translatedBy(x: self.size.height, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)
        
        default:
            break
        }
        
        guard let ci = self.cgImage, let colorSpace = ci.colorSpace else {
            return self
        }
        let context = CGContext(data: nil, width: Int(self.size.width), height: Int(self.size.height), bitsPerComponent: ci.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: ci.bitmapInfo.rawValue)
        context?.concatenate(transform)
        switch self.imageOrientation {
        case .left, .leftMirrored, .right, .rightMirrored:
            context?.draw(ci, in: CGRect(x: 0, y: 0, width: self.size.height, height: self.size.width))
        default:
            context?.draw(ci, in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
        }
        
        guard let newCgimg = context?.makeImage() else {
            return self
        }
        return UIImage(cgImage: newCgimg)
    }
}
   

照片朝上,不用管,

朝左、朝右、朝下,都有一个旋转,

旋转后,相应的翻转坐标系,

然后从绘图上下文中,取出照片

2 , 旋转照片


var angleX: CGFloat = 0

func rotateRhs() {
        guard let img = takedImage else {
            return
        }
        let radian = CGFloat.pi * 0.5
        angleX += 1
        
        takedImageView.transform = CGAffineTransform(rotationAngle: radian * angleX)
        if Int(angleX) % 2 == 1{
            takedImageView.frame = view.bounds
            
        }
        else{
            let ratio = img.size.height / img.size.width
            let w = UI.std.width
            let h = w * ratio
            takedImageView.frame = CGRect(x: 0, y: 0, width: w, height: h)
            
        }
        takedImageView.center = CGPoint(x: UI.std.width * 0.5, y: UI.std.height * 0.5)
    }
弄一个属性,记录已经旋转了多少个 90 度,

旋转图片视图 takedImageView,

限定图片视图的 frame,

图片视图的 frame, 要么跟屏幕一致,要么根据分辨率

根据分辨率:
  • 此时, image.height > image.width

视图的 width = 屏幕 width,

然后等图片比例放大,可接受

就算 image.height > 屏幕 height,

也 OK

  • 此时, image.height <= image.width

视图的 width = 屏幕 width,

等图片比例放大,可接受

然后指定中心点

3, 重拍

    @objc
    func retakeBtnClick(){
        angleX = 0
        
        takedImageView.transform = .identity
        takedImageView.frame = view.bounds
        
        
        session.startRunning()
        resetSubViewStatus()
        takedImage = nil
        
    }

恢复,记录的旋转角度

takedImageView 的 frame 和 transform 都重置

4, 结束旋转

@objc func doneBtnClick() {
        recordVideoPlayerLayer?.player?.pause()
        recordVideoPlayerLayer?.player = nil
        dismiss(animated: true) {
            self.takeDoneBlock?(self.takedImage?.image(rotated: Int(self.angleX)))
        }
    }

根据旋转的角度,拿原图绘制,新图



extension UIImage{
    
    
    func image(rotated time: Int) -> UIImage{
        guard time != 0 else {
            return self
        }
        
        let radian = CGFloat(time) * CGFloat.pi / 2
        let rotatedSize = CGRect(origin: .zero, size: size)
            .applying(CGAffineTransform(rotationAngle: radian))
            .integral.size
        UIGraphicsBeginImageContext(rotatedSize)
        if let context = UIGraphicsGetCurrentContext() {
            let origin = CGPoint(x: rotatedSize.width / 2.0,
                                 y: rotatedSize.height / 2.0)
            context.translateBy(x: origin.x, y: origin.y)
            context.rotate(by: radian)
            draw(in: CGRect(x: -origin.y, y: -origin.x,
                            width: size.width, height: size.height))
            let rotatedImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()

            return rotatedImage ?? self
        }

        return self

    }
    
    
    
}

github 链接 _