179 lines
6.5 KiB
Swift
179 lines
6.5 KiB
Swift
|
|
//
|
||
|
|
// 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
|
||
|
|
}
|
||
|
|
}
|