首页 > 解决方案 > 在 Swift 中裁剪肖像照片

问题描述

我试图快速将照片裁剪到特定尺寸。目前,我目前的裁剪器对于水平照片的效果非常好,但是垂直拍摄的照片(人像模式)会被拉伸并且裁剪不当。是否有人对此有解决方案或我可以实施的简单裁剪器?任何建议或帮助都会很棒!我意识到下面有很多代码,所以如果有人有任何更短的解决方案,那就太好了。到目前为止,这是我的代码:

cropImage()在结构中的函数是我用来裁剪图像的。

该操作cropPressed()是我调用按钮操作的地方,并创建了一个 CGRect 以将裁剪格式化为

该函数getCGImageWithCorrectOrientation()通过将原始图像翻转到正确的方向来校正原始图像(我在裁剪后旋转图像的手机方向有一些问题,所以这解决了它)

最后imageFrame() -> CGRect,在 imageview 扩展中有助于找到实际的图像帧。

我的整个代码:

    import UIKit

struct imageCropHandler {
    static let sharedInstance = imageCropHandler()
    
    func cropImage(_ inputImage: UIImage, toRect cropRect: CGRect, viewWidth: CGFloat, viewHeight: CGFloat) -> UIImage?
    {
        let imageViewScale = max(inputImage.size.width / viewWidth,
                                 inputImage.size.height / viewHeight)

        // Scale cropRect to handle images larger than shown-on-screen size
        let cropZone = CGRect(x:cropRect.minX * imageViewScale,
                              y:cropRect.minY * imageViewScale,
                              width:cropRect.size.width * imageViewScale,
                              height:cropRect.size.height * imageViewScale)

        // Perform cropping in Core Graphics
        guard let cutImageRef: CGImage = inputImage.cgImage?.cropping(to:cropZone)
        else {
            return nil
        }

        // Return image to UIImage
        let croppedImage: UIImage = UIImage(cgImage: cutImageRef)
        return croppedImage
    }
}


class cropperViewController: UIViewController, UIScrollViewDelegate,UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    @IBOutlet var scrollView: UIScrollView! {
        didSet{
            scrollView.delegate = self
            scrollView.minimumZoomScale = 1.0
            scrollView.maximumZoomScale = 4.0
        }
    }
    @IBOutlet var imageView: UIImageView!
    @IBOutlet var cropView: UIView!
    @IBOutlet var newPhotoButton: UIButton!
    @IBOutlet var cropButton: UIButton!
    @IBOutlet var undoButton: UIButton!
    @IBOutlet var doneButton: UIButton!
    
    var previousImage:[UIImage] = []
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        showPicker()
        imageView.isHidden = true
        cropView.isHidden = true
        doneButton.isHidden = true
        self.navigationController?.setNavigationBarHidden(false, animated: animated)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.navigationController?.setNavigationBarHidden(true, animated: animated)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        cropView.layer.borderWidth = 2.0
        cropView.layer.borderColor = UIColor.label.cgColor
        let buttons = [newPhotoButton, cropButton, undoButton]
        for button in buttons {
            button!.layer.masksToBounds = true
            button!.layer.cornerRadius = button!.bounds.height/2
            button!.titleLabel!.textColor = .systemBlue
            button?.layer.borderWidth = 1
            button?.layer.borderColor = UIColor.systemBlue.cgColor
        }
        doneButton.layer.masksToBounds = true
        doneButton.layer.cornerRadius = doneButton.bounds.height/2
    }
    
    func showPicker(){
        let imagePickerController = UIImagePickerController()
        imagePickerController.delegate = self
        imagePickerController.allowsEditing = false
        present(imagePickerController,animated: true, completion: nil)
    }
    
    func  imagePickerController(_ picker : UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]){
        dismiss(animated: true, completion: nil)
        if let originalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            if originalImage.size.width > originalImage.size.height {
                if let lastImage = imageView.image {
                    previousImage.append(lastImage)
                }
                imageView.image = originalImage
                imageView.isHidden = false
                cropView.isHidden = false
                doneButton.isHidden = true
                print("imageview width: \(imageView.frame.width)")
                print("image width: \(imageView.image!.size.width)")
                print("imageview height: \(imageView.frame.height)")
            } else {
                let ac = UIAlertController(title: "Select Different Image", message: "Please select an image that was taken in landscape (horizontal) position!", preferredStyle: .alert)
                ac.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
                present(ac, animated: true, completion: nil)
            }
        }
    }
    
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
         return imageView
    }
    
    @IBAction func cropPressed(_ sender: Any) {
        guard let image = self.imageView.image else {
                return
            }
        

        let cgImage = self.getCGImageWithCorrectOrientation(image)
        let croppableImage = UIImage(cgImage: cgImage, scale: image.scale, orientation: .up)
        let cropRect = CGRect(x: cropView.frame.minX - imageView.imageFrame().minX, y: cropView.frame.minY - imageView.imageFrame().minY, width: cropView.frame.width, height: cropView.frame.height)
        let croppedImage = imageCropHandler.sharedInstance.cropImage(croppableImage, toRect: cropRect, viewWidth: imageView.frame.width, viewHeight: imageView.frame.height)
        if let lastImage = imageView.image {
            previousImage.append(lastImage)
        }
        
        imageView.image = croppedImage
        scrollView.zoomScale = 1
        doneButton.isHidden = false
    }

    
    func getCGImageWithCorrectOrientation(_ image : UIImage) -> CGImage {
        if (image.imageOrientation == UIImage.Orientation.up) {
            return image.cgImage!;
        }

        var transform : CGAffineTransform = CGAffineTransform.identity;
        switch (image.imageOrientation) {
        case UIImage.Orientation.right, UIImage.Orientation.rightMirrored:
            transform = transform.translatedBy(x: 0, y: image.size.height);
            transform = transform.rotated(by: CGFloat(-1.0 * .pi/2));
            break;
        case UIImage.Orientation.left, UIImage.Orientation.leftMirrored:
            transform = transform.translatedBy(x: image.size.width, y: 0);
            transform = transform.rotated(by: .pi/2);
            break;
        case UIImage.Orientation.down, UIImage.Orientation.downMirrored:
            transform = transform.translatedBy(x: image.size.width, y: image.size.height);
            transform = transform.rotated(by: .pi);
            break;
        default:
            break;
        }

        switch (image.imageOrientation) {
        case UIImage.Orientation.rightMirrored, UIImage.Orientation.leftMirrored:
            transform = transform.translatedBy(x: image.size.height, y: 0);
            transform = transform.scaledBy(x: -1, y: 1);
            break;
        case UIImage.Orientation.downMirrored, UIImage.Orientation.upMirrored:
            transform = transform.translatedBy(x: image.size.width, y: 0);
            transform = transform.scaledBy(x: -1, y: 1);
            break;
        default:
            break;
        }

        let contextWidth : Int;
        let contextHeight : Int;
        
        switch (image.imageOrientation) {
        case UIImage.Orientation.left, UIImage.Orientation.leftMirrored,
                UIImage.Orientation.right, UIImage.Orientation.rightMirrored:
            contextWidth = (image.cgImage?.height)!;
            contextHeight = (image.cgImage?.width)!;
            break;
        default:
            contextWidth = (image.cgImage?.width)!;
            contextHeight = (image.cgImage?.height)!;
            break;
        }

        let context : CGContext = CGContext(data: nil, width: contextWidth, height: contextHeight, bitsPerComponent: image.cgImage!.bitsPerComponent, bytesPerRow: image.cgImage!.bytesPerRow, space: image.cgImage!.colorSpace!, bitmapInfo: image.cgImage!.bitmapInfo.rawValue)!;

        context.concatenate(transform);
        context.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: CGFloat(contextWidth), height: CGFloat(contextHeight)));
        
        let cgImage = context.makeImage();

        return cgImage!;
    }
    
    @IBAction func newPhotoPressed(_ sender: Any) {
        let ac = UIAlertController(title: "Choose Event Picture", message: "Please choose a photo from your device!", preferredStyle: .actionSheet)
        ac.addAction(UIAlertAction(title: "Choose Image from Library", style: .default, handler: { (action) in
            self.showPicker()
        }))
        ac.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
        present(ac, animated: true)
    }
    
    @IBAction func undoPressed(_ sender: Any) {
        if previousImage.count == 0 {
            let ac = UIAlertController(title: "No Previous Actions", message: "There are no actions to undo.", preferredStyle: .alert)
            ac.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            present(ac, animated: true, completion: nil)
        } else {
            imageView.image = previousImage.last
            let imageRatio = (imageView.image!.size.height) / (imageView.image!.size.width)
            if imageRatio == 9/23 {
                doneButton.isHidden = false
            } else {
                doneButton.isHidden = true
            }
            let imageIndex = previousImage.count - 1
            previousImage.remove(at: imageIndex)
        }
    }
    
    @IBAction func donePressed(_ sender: Any) {
        NotificationCenter.default.post(Notification(name: Notification.Name("Event Image Chosen"), object: nil, userInfo: ["Chosen Image" : imageView.image!]))
    }
    
}

//extending the UIImageView
extension UIImageView {
    
    // MARK: - Methods
    func imageFrame() -> CGRect {
        let imageViewSize = self.frame.size
            let imageSize = self.image!.size

            let scaleW = imageViewSize.width / imageSize.width
            let scaleH = imageViewSize.height / imageSize.height
            let aspect = min(scaleW, scaleH)

            var imageRect = CGRect(x: 0, y: 0, width: (imageSize.width * aspect), height: (imageSize.height * aspect))

            imageRect.origin.x = (imageViewSize.width - imageRect.size.width) / 2
            imageRect.origin.y = (imageViewSize.height - imageRect.size.height) / 2

        imageRect.origin.x += self.frame.origin.x
            imageRect.origin.y += self.frame.origin.y

            return imageRect;
        }

}

//makes the cropper unclickable
class CropAreaView: UIView {
     override func point(inside point: CGPoint, with event:   UIEvent?) -> Bool {
          return false
     }
}

标签: swiftxcodeimageuiimageviewcrop

解决方案


推荐阅读