CropViewController.swift 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. //
  2. // CropViewController.swift
  3. // CropViewController
  4. //
  5. // Created by Guilherme Moura on 2/25/16.
  6. // Copyright © 2016 Reefactor, Inc. All rights reserved.
  7. // Credit https://github.com/sprint84/PhotoCropEditor
  8. import UIKit
  9. public protocol CropViewControllerDelegate: class {
  10. func cropViewController(_ controller: CropViewController, didFinishCroppingImage image: UIImage, transform: CGAffineTransform, cropRect: CGRect)
  11. func cropViewControllerDidCancel(_ controller: CropViewController)
  12. }
  13. open class CropViewController: UIViewController {
  14. open weak var delegate: CropViewControllerDelegate?
  15. open var image: UIImage? {
  16. didSet {
  17. cropView?.image = image
  18. }
  19. }
  20. open var keepAspectRatio = false {
  21. didSet {
  22. cropView?.keepAspectRatio = keepAspectRatio
  23. }
  24. }
  25. open var cropAspectRatio: CGFloat = 0.0 {
  26. didSet {
  27. cropView?.cropAspectRatio = cropAspectRatio
  28. }
  29. }
  30. open var cropRect = CGRect.zero {
  31. didSet {
  32. adjustCropRect()
  33. }
  34. }
  35. open var imageCropRect = CGRect.zero {
  36. didSet {
  37. cropView?.imageCropRect = imageCropRect
  38. }
  39. }
  40. open var toolbarHidden = false
  41. open var rotationEnabled = false {
  42. didSet {
  43. cropView?.rotationGestureRecognizer.isEnabled = rotationEnabled
  44. }
  45. }
  46. open var rotationTransform: CGAffineTransform {
  47. return cropView!.rotation
  48. }
  49. open var zoomedCropRect: CGRect {
  50. return cropView!.zoomedCropRect()
  51. }
  52. fileprivate var cropView: CropView?
  53. public required init?(coder aDecoder: NSCoder) {
  54. super.init(coder: aDecoder)
  55. initialize()
  56. }
  57. public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
  58. super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
  59. initialize()
  60. }
  61. fileprivate func initialize() {
  62. rotationEnabled = true
  63. }
  64. open override func loadView() {
  65. let contentView = UIView()
  66. contentView.autoresizingMask = .flexibleWidth
  67. contentView.backgroundColor = UIColor.black
  68. view = contentView
  69. // Add CropView
  70. cropView = CropView(frame: contentView.bounds)
  71. contentView.addSubview(cropView!)
  72. }
  73. open override func viewDidLoad() {
  74. super.viewDidLoad()
  75. navigationController?.navigationBar.isTranslucent = false
  76. navigationController?.toolbar.isTranslucent = false
  77. navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(CropViewController.cancel(_:)))
  78. navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(CropViewController.done(_:)))
  79. if self.toolbarItems == nil {
  80. let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
  81. let constrainButton = UIBarButtonItem(title: "Constrain", style: .plain, target: self, action: #selector(CropViewController.constrain(_:)))
  82. toolbarItems = [flexibleSpace, constrainButton, flexibleSpace]
  83. }
  84. navigationController?.isToolbarHidden = toolbarHidden
  85. cropView?.image = image
  86. cropView?.rotationGestureRecognizer.isEnabled = rotationEnabled
  87. }
  88. open override func viewDidAppear(_ animated: Bool) {
  89. super.viewDidAppear(animated)
  90. if cropAspectRatio != 0 {
  91. cropView?.cropAspectRatio = cropAspectRatio
  92. }
  93. if !cropRect.equalTo(CGRect.zero) {
  94. adjustCropRect()
  95. }
  96. if !imageCropRect.equalTo(CGRect.zero) {
  97. cropView?.imageCropRect = imageCropRect
  98. }
  99. cropView?.keepAspectRatio = keepAspectRatio
  100. }
  101. open func resetCropRect() {
  102. cropView?.resetCropRect()
  103. }
  104. open func resetCropRectAnimated(_ animated: Bool) {
  105. cropView?.resetCropRectAnimated(animated)
  106. }
  107. @objc func cancel(_ sender: UIBarButtonItem) {
  108. delegate?.cropViewControllerDidCancel(self)
  109. }
  110. @objc func done(_ sender: UIBarButtonItem) {
  111. if let image = cropView?.croppedImage {
  112. guard let rotation = cropView?.rotation else {
  113. return
  114. }
  115. guard let rect = cropView?.zoomedCropRect() else {
  116. return
  117. }
  118. delegate?.cropViewController(self, didFinishCroppingImage: image, transform: rotation, cropRect: rect)
  119. }
  120. }
  121. @objc func constrain(_ sender: UIBarButtonItem) {
  122. let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  123. let original = UIAlertAction(title: "Original", style: .default) { [unowned self] action in
  124. guard let image = self.cropView?.image else {
  125. return
  126. }
  127. guard var cropRect = self.cropView?.cropRect else {
  128. return
  129. }
  130. let width = image.size.width
  131. let height = image.size.height
  132. let ratio: CGFloat
  133. if width < height {
  134. ratio = width / height
  135. cropRect.size = CGSize(width: cropRect.height * ratio, height: cropRect.height)
  136. } else {
  137. ratio = height / width
  138. cropRect.size = CGSize(width: cropRect.width, height: cropRect.width * ratio)
  139. }
  140. self.cropView?.cropRect = cropRect
  141. }
  142. actionSheet.addAction(original)
  143. let square = UIAlertAction(title: "Square", style: .default) { [unowned self] action in
  144. let ratio: CGFloat = 1.0
  145. // self.cropView?.cropAspectRatio = ratio
  146. if var cropRect = self.cropView?.cropRect {
  147. let width = cropRect.width
  148. cropRect.size = CGSize(width: width, height: width * ratio)
  149. self.cropView?.cropRect = cropRect
  150. }
  151. }
  152. actionSheet.addAction(square)
  153. let threeByTwo = UIAlertAction(title: "3 x 2", style: .default) { [unowned self] action in
  154. self.cropView?.cropAspectRatio = 2.0 / 3.0
  155. }
  156. actionSheet.addAction(threeByTwo)
  157. let threeByFive = UIAlertAction(title: "3 x 5", style: .default) { [unowned self] action in
  158. self.cropView?.cropAspectRatio = 3.0 / 5.0
  159. }
  160. actionSheet.addAction(threeByFive)
  161. let fourByThree = UIAlertAction(title: "4 x 3", style: .default) { [unowned self] action in
  162. let ratio: CGFloat = 3.0 / 4.0
  163. if var cropRect = self.cropView?.cropRect {
  164. let width = cropRect.width
  165. cropRect.size = CGSize(width: width, height: width * ratio)
  166. self.cropView?.cropRect = cropRect
  167. }
  168. }
  169. actionSheet.addAction(fourByThree)
  170. let fourBySix = UIAlertAction(title: "4 x 6", style: .default) { [unowned self] action in
  171. self.cropView?.cropAspectRatio = 4.0 / 6.0
  172. }
  173. actionSheet.addAction(fourBySix)
  174. let fiveBySeven = UIAlertAction(title: "5 x 7", style: .default) { [unowned self] action in
  175. self.cropView?.cropAspectRatio = 5.0 / 7.0
  176. }
  177. actionSheet.addAction(fiveBySeven)
  178. let eightByTen = UIAlertAction(title: "8 x 10", style: .default) { [unowned self] action in
  179. self.cropView?.cropAspectRatio = 8.0 / 10.0
  180. }
  181. actionSheet.addAction(eightByTen)
  182. let widescreen = UIAlertAction(title: "16 x 9", style: .default) { [unowned self] action in
  183. let ratio: CGFloat = 9.0 / 16.0
  184. if var cropRect = self.cropView?.cropRect {
  185. let width = cropRect.width
  186. cropRect.size = CGSize(width: width, height: width * ratio)
  187. self.cropView?.cropRect = cropRect
  188. }
  189. }
  190. actionSheet.addAction(widescreen)
  191. let cancel = UIAlertAction(title: "Cancel", style: .default) { [unowned self] action in
  192. self.dismiss(animated: true, completion: nil)
  193. }
  194. actionSheet.addAction(cancel)
  195. if let popoverController = actionSheet.popoverPresentationController {
  196. popoverController.barButtonItem = sender
  197. }
  198. present(actionSheet, animated: true, completion: nil)
  199. }
  200. // MARK: - Private methods
  201. fileprivate func adjustCropRect() {
  202. imageCropRect = CGRect.zero
  203. guard var cropViewCropRect = cropView?.cropRect else {
  204. return
  205. }
  206. cropViewCropRect.origin.x += cropRect.origin.x
  207. cropViewCropRect.origin.y += cropRect.origin.y
  208. let minWidth = min(cropViewCropRect.maxX - cropViewCropRect.minX, cropRect.width)
  209. let minHeight = min(cropViewCropRect.maxY - cropViewCropRect.minY, cropRect.height)
  210. let size = CGSize(width: minWidth, height: minHeight)
  211. cropViewCropRect.size = size
  212. cropView?.cropRect = cropViewCropRect
  213. }
  214. }