// // CircleCropViewController.swift // CIrcleCropView // // Created by Bhavesh Chaudhari on 08/05/20. // Copyright © 2020 Bhavesh. All rights reserved. // import UIKit public class CropViewController: UIViewController { var image: UIImage let imageView: UIImageView let scrollView: UIScrollView let completion: (UIImage?) -> Void private var circleView: CircleCropView? var backButton: StyleButton = { let button = StyleButton() button.tertiary(size: .large) button.translatesAutoresizingMaskIntoConstraints = false button.setTitle("Cancel", for: .normal) return button }() var okButton: StyleButton = { let button = StyleButton() button.primary(size: .large) button.setTitle("Confirm", for: .normal) button.translatesAutoresizingMaskIntoConstraints = false return button }() public init(image: UIImage, completion: @escaping (UIImage?) -> Void) { self.image = image self.completion = completion imageView = UIImageView(image: image) scrollView = UIScrollView() scrollView.contentInsetAdjustmentBehavior = .never super.init(nibName: nil, bundle: nil) } override public func viewDidLoad() { super.viewDidLoad() circleView = CircleCropView(frame: view.bounds) view.addSubview(scrollView) view.addSubview(circleView!) view.addSubview(okButton) view.addSubview(backButton) scrollView.addSubview(imageView) scrollView.contentSize = image.size scrollView.delegate = self view.backgroundColor = UIColor(red: 33/255.0, green: 26/255.0, blue: 43/255.0, alpha: 1) scrollView.frame = view.frame//.inset(by: view.safeAreaInsets) circleView?.frame = scrollView.frame//.inset(by: view.safeAreaInsets) backButton.addTarget(self, action: #selector(backClick), for: .touchUpInside) okButton.addTarget(self, action: #selector(okClick), for: .touchUpInside) modalPresentationStyle = .fullScreen addConstraint() } func addConstraint() { backButton.snp.makeConstraints { make in make.leading.equalToSuperview().offset(24) make.bottom.equalToSuperview().offset(-16-UIWindow.safeAreaBottom*0.5) } okButton.snp.makeConstraints { make in make.trailing.equalToSuperview().offset(-24) make.bottom.equalToSuperview().offset(-16-UIWindow.safeAreaBottom*0.5) } } override public var preferredStatusBarStyle: UIStatusBarStyle { return .default } override public func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.setNavigationBarHidden(true, animated: animated) } override public func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let scrollFrame = scrollView.frame let imSize = image.size guard let hole = circleView?.circleInset, hole.width > 0 else { return } let verticalRatio = hole.height / imSize.height let horizontalRatio = hole.width / imSize.width let maxRatio = max(horizontalRatio, verticalRatio) if(maxRatio > 1){ // 图比裁剪区域小 scrollView.minimumZoomScale = maxRatio scrollView.maximumZoomScale = maxRatio * 2 scrollView.zoomScale = maxRatio }else{ scrollView.minimumZoomScale = maxRatio scrollView.maximumZoomScale = 1 scrollView.zoomScale = scrollView.minimumZoomScale } let insetHeight = (scrollFrame.height - hole.height) / 2 let insetWidth = (scrollFrame.width - hole.width) / 2 scrollView.contentInset = UIEdgeInsets(top: insetHeight, left: insetWidth, bottom: insetHeight, right: insetWidth) okButton.clipsToBounds = true } override public func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.setNavigationBarHidden(false, animated: animated) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc func backClick(sender: UIButton) { dismiss(animated: true, completion: nil) } @objc func okClick(sender: UIButton) { cropImage() } private func cropImage() { guard let rect = circleView?.circleInset else { return } let shift = rect.applying(CGAffineTransform(translationX: scrollView.contentOffset.x, y: scrollView.contentOffset.y)) let scaled = shift.applying(CGAffineTransform(scaleX: 1.0 / scrollView.zoomScale, y: 1.0 / scrollView.zoomScale)) let newImage = image.imageCropped(toRect: scaled) completion(newImage) dismiss(animated: true, completion: nil) } } extension CropViewController: UIScrollViewDelegate { func zoomOut() { let newScale = scrollView.zoomScale == scrollView.minimumZoomScale ? 0.5 : scrollView.minimumZoomScale scrollView.setZoomScale(newScale, animated: true) } public func viewForZooming(in scrollView: UIScrollView) -> UIView? { return imageView } public func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { // need empty implementation for zooming } } extension UIImage { func imageCropped(toRect rect: CGRect) -> UIImage { let rad: (Double) -> CGFloat = { deg in CGFloat(deg / 180.0 * .pi) } var rectTransform: CGAffineTransform switch imageOrientation { case .left: let rotation = CGAffineTransform(rotationAngle: rad(90)) rectTransform = rotation.translatedBy(x: 0, y: -size.height) case .right: let rotation = CGAffineTransform(rotationAngle: rad(-90)) rectTransform = rotation.translatedBy(x: -size.width, y: 0) case .down: let rotation = CGAffineTransform(rotationAngle: rad(-180)) rectTransform = rotation.translatedBy(x: -size.width, y: -size.height) default: rectTransform = .identity } rectTransform = rectTransform.scaledBy(x: scale, y: scale) let transformedRect = rect.applying(rectTransform) let imageRef = cgImage!.cropping(to: transformedRect)! let result = UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation) print("croped Image width and height = \(result.size)") return result } }