CropRectView.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. //
  2. // CropRectView.swift
  3. // CropViewController
  4. //
  5. // Created by Guilherme Moura on 2/26/16.
  6. // Copyright © 2016 Reefactor, Inc. All rights reserved.
  7. // Credit https://github.com/sprint84/PhotoCropEditor
  8. import UIKit
  9. protocol CropRectViewDelegate: class {
  10. func cropRectViewDidBeginEditing(_ view: CropRectView)
  11. func cropRectViewDidChange(_ view: CropRectView)
  12. func cropRectViewDidEndEditing(_ view: CropRectView)
  13. }
  14. class CropRectView: UIView, ResizeControlDelegate {
  15. weak var delegate: CropRectViewDelegate?
  16. var showsGridMajor = true {
  17. didSet {
  18. setNeedsDisplay()
  19. }
  20. }
  21. var showsGridMinor = false {
  22. didSet {
  23. setNeedsDisplay()
  24. }
  25. }
  26. var keepAspectRatio = false {
  27. didSet {
  28. if keepAspectRatio {
  29. let width = bounds.width
  30. let height = bounds.height
  31. fixedAspectRatio = min(width / height, height / width)
  32. }
  33. }
  34. }
  35. fileprivate var resizeImageView: UIImageView!
  36. fileprivate let topLeftCornerView = ResizeControl()
  37. fileprivate let topRightCornerView = ResizeControl()
  38. fileprivate let bottomLeftCornerView = ResizeControl()
  39. fileprivate let bottomRightCornerView = ResizeControl()
  40. fileprivate let topEdgeView = ResizeControl()
  41. fileprivate let leftEdgeView = ResizeControl()
  42. fileprivate let rightEdgeView = ResizeControl()
  43. fileprivate let bottomEdgeView = ResizeControl()
  44. fileprivate var initialRect = CGRect.zero
  45. fileprivate var fixedAspectRatio: CGFloat = 0.0
  46. override init(frame: CGRect) {
  47. super.init(frame: frame)
  48. initialize()
  49. }
  50. required init?(coder aDecoder: NSCoder) {
  51. super.init(coder: aDecoder)
  52. initialize()
  53. }
  54. fileprivate func initialize() {
  55. backgroundColor = UIColor.clear
  56. contentMode = .redraw
  57. resizeImageView = UIImageView(frame: bounds.insetBy(dx: -2.0, dy: -2.0))
  58. resizeImageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  59. let bundle = Bundle(for: type(of: self))
  60. let image = UIImage(named: "PhotoCropEditorBorder", in: bundle, compatibleWith: nil)
  61. resizeImageView.image = image?.resizableImage(withCapInsets: UIEdgeInsets(top: 23.0, left: 23.0, bottom: 23.0, right: 23.0))
  62. addSubview(resizeImageView)
  63. topEdgeView.delegate = self
  64. addSubview(topEdgeView)
  65. leftEdgeView.delegate = self
  66. addSubview(leftEdgeView)
  67. rightEdgeView.delegate = self
  68. addSubview(rightEdgeView)
  69. bottomEdgeView.delegate = self
  70. addSubview(bottomEdgeView)
  71. topLeftCornerView.delegate = self
  72. addSubview(topLeftCornerView)
  73. topRightCornerView.delegate = self
  74. addSubview(topRightCornerView)
  75. bottomLeftCornerView.delegate = self
  76. addSubview(bottomLeftCornerView)
  77. bottomRightCornerView.delegate = self
  78. addSubview(bottomRightCornerView)
  79. }
  80. override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
  81. for subview in subviews where subview is ResizeControl {
  82. if subview.frame.contains(point) {
  83. return subview
  84. }
  85. }
  86. return nil
  87. }
  88. override func draw(_ rect: CGRect) {
  89. super.draw(rect)
  90. let width = bounds.width
  91. let height = bounds.height
  92. for i in 0 ..< 3 {
  93. let borderPadding: CGFloat = 0.5
  94. if showsGridMinor {
  95. for j in 1 ..< 3 {
  96. UIColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 0.3).set()
  97. UIRectFill(CGRect(x: round((width / 9.0) * CGFloat(j) + (width / 3.0) * CGFloat(i)), y: borderPadding, width: 1.0, height: round(height) - borderPadding * 2.0))
  98. UIRectFill(CGRect(x: borderPadding, y: round((height / 9.0) * CGFloat(j) + (height / 3.0) * CGFloat(i)), width: round(width) - borderPadding * 2.0, height: 1.0))
  99. }
  100. }
  101. if showsGridMajor {
  102. if i > 0 {
  103. UIColor.white.set()
  104. UIRectFill(CGRect(x: round(CGFloat(i) * width / 3.0), y: borderPadding, width: 1.0, height: round(height) - borderPadding * 2.0))
  105. UIRectFill(CGRect(x: borderPadding, y: round(CGFloat(i) * height / 3.0), width: round(width) - borderPadding * 2.0, height: 1.0))
  106. }
  107. }
  108. }
  109. }
  110. override func layoutSubviews() {
  111. super.layoutSubviews()
  112. topLeftCornerView.frame.origin = CGPoint(x: topLeftCornerView.bounds.width / -2.0, y: topLeftCornerView.bounds.height / -2.0)
  113. topRightCornerView.frame.origin = CGPoint(x: bounds.width - topRightCornerView.bounds.width - 2.0, y: topRightCornerView.bounds.height / -2.0)
  114. bottomLeftCornerView.frame.origin = CGPoint(x: bottomLeftCornerView.bounds.width / -2.0, y: bounds.height - bottomLeftCornerView.bounds.height / 2.0)
  115. bottomRightCornerView.frame.origin = CGPoint(x: bounds.width - bottomRightCornerView.bounds.width / 2.0, y: bounds.height - bottomRightCornerView.bounds.height / 2.0)
  116. topEdgeView.frame = CGRect(x: topLeftCornerView.frame.maxX, y: topEdgeView.frame.height / -2.0, width: topRightCornerView.frame.minX - topLeftCornerView.frame.maxX, height: topEdgeView.bounds.height)
  117. leftEdgeView.frame = CGRect(x: leftEdgeView.frame.width / -2.0, y: topLeftCornerView.frame.maxY, width: leftEdgeView.frame.width, height: bottomLeftCornerView.frame.minY - topLeftCornerView.frame.maxY)
  118. bottomEdgeView.frame = CGRect(x: bottomLeftCornerView.frame.maxX, y: bottomLeftCornerView.frame.minY, width: bottomRightCornerView.frame.minX - bottomLeftCornerView.frame.maxX, height: bottomEdgeView.frame.height)
  119. rightEdgeView.frame = CGRect(x: bounds.width - rightEdgeView.frame.width / 2.0, y: topRightCornerView.frame.maxY, width: rightEdgeView.frame.width, height: bottomRightCornerView.frame.minY - topRightCornerView.frame.maxY)
  120. }
  121. func enableResizing(_ enabled: Bool) {
  122. resizeImageView.isHidden = !enabled
  123. topLeftCornerView.enabled = enabled
  124. topRightCornerView.enabled = enabled
  125. bottomLeftCornerView.enabled = enabled
  126. bottomRightCornerView.enabled = enabled
  127. topEdgeView.enabled = enabled
  128. leftEdgeView.enabled = enabled
  129. bottomEdgeView.enabled = enabled
  130. rightEdgeView.enabled = enabled
  131. }
  132. // MARK: - ResizeControl delegate methods
  133. func resizeControlDidBeginResizing(_ control: ResizeControl) {
  134. initialRect = frame
  135. delegate?.cropRectViewDidBeginEditing(self)
  136. }
  137. func resizeControlDidResize(_ control: ResizeControl) {
  138. frame = cropRectWithResizeControlView(control)
  139. delegate?.cropRectViewDidChange(self)
  140. }
  141. func resizeControlDidEndResizing(_ control: ResizeControl) {
  142. delegate?.cropRectViewDidEndEditing(self)
  143. }
  144. fileprivate func cropRectWithResizeControlView(_ resizeControl: ResizeControl) -> CGRect {
  145. var rect = frame
  146. if resizeControl == topEdgeView {
  147. rect = CGRect(x: initialRect.minX,
  148. y: initialRect.minY + resizeControl.translation.y,
  149. width: initialRect.width,
  150. height: initialRect.height - resizeControl.translation.y)
  151. if keepAspectRatio {
  152. rect = constrainedRectWithRectBasisOfHeight(rect)
  153. }
  154. } else if resizeControl == leftEdgeView {
  155. rect = CGRect(x: initialRect.minX + resizeControl.translation.x,
  156. y: initialRect.minY,
  157. width: initialRect.width - resizeControl.translation.x,
  158. height: initialRect.height)
  159. if keepAspectRatio {
  160. rect = constrainedRectWithRectBasisOfWidth(rect)
  161. }
  162. } else if resizeControl == bottomEdgeView {
  163. rect = CGRect(x: initialRect.minX,
  164. y: initialRect.minY,
  165. width: initialRect.width,
  166. height: initialRect.height + resizeControl.translation.y)
  167. if keepAspectRatio {
  168. rect = constrainedRectWithRectBasisOfHeight(rect)
  169. }
  170. } else if resizeControl == rightEdgeView {
  171. rect = CGRect(x: initialRect.minX,
  172. y: initialRect.minY,
  173. width: initialRect.width + resizeControl.translation.x,
  174. height: initialRect.height)
  175. if keepAspectRatio {
  176. rect = constrainedRectWithRectBasisOfWidth(rect)
  177. }
  178. } else if resizeControl == topLeftCornerView {
  179. rect = CGRect(x: initialRect.minX + resizeControl.translation.x,
  180. y: initialRect.minY + resizeControl.translation.y,
  181. width: initialRect.width - resizeControl.translation.x,
  182. height: initialRect.height - resizeControl.translation.y)
  183. if keepAspectRatio {
  184. var constrainedFrame: CGRect
  185. if abs(resizeControl.translation.x) < abs(resizeControl.translation.y) {
  186. constrainedFrame = constrainedRectWithRectBasisOfHeight(rect)
  187. } else {
  188. constrainedFrame = constrainedRectWithRectBasisOfWidth(rect)
  189. }
  190. constrainedFrame.origin.x -= constrainedFrame.width - rect.width
  191. constrainedFrame.origin.y -= constrainedFrame.height - rect.height
  192. rect = constrainedFrame
  193. }
  194. } else if resizeControl == topRightCornerView {
  195. rect = CGRect(x: initialRect.minX,
  196. y: initialRect.minY + resizeControl.translation.y,
  197. width: initialRect.width + resizeControl.translation.x,
  198. height: initialRect.height - resizeControl.translation.y)
  199. if keepAspectRatio {
  200. if abs(resizeControl.translation.x) < abs(resizeControl.translation.y) {
  201. rect = constrainedRectWithRectBasisOfHeight(rect)
  202. } else {
  203. rect = constrainedRectWithRectBasisOfWidth(rect)
  204. }
  205. }
  206. } else if resizeControl == bottomLeftCornerView {
  207. rect = CGRect(x: initialRect.minX + resizeControl.translation.x,
  208. y: initialRect.minY,
  209. width: initialRect.width - resizeControl.translation.x,
  210. height: initialRect.height + resizeControl.translation.y)
  211. if keepAspectRatio {
  212. var constrainedFrame: CGRect
  213. if abs(resizeControl.translation.x) < abs(resizeControl.translation.y) {
  214. constrainedFrame = constrainedRectWithRectBasisOfHeight(rect)
  215. } else {
  216. constrainedFrame = constrainedRectWithRectBasisOfWidth(rect)
  217. }
  218. constrainedFrame.origin.x -= constrainedFrame.width - rect.width
  219. rect = constrainedFrame
  220. }
  221. } else if resizeControl == bottomRightCornerView {
  222. rect = CGRect(x: initialRect.minX,
  223. y: initialRect.minY,
  224. width: initialRect.width + resizeControl.translation.x,
  225. height: initialRect.height + resizeControl.translation.y)
  226. if keepAspectRatio {
  227. if abs(resizeControl.translation.x) < abs(resizeControl.translation.y) {
  228. rect = constrainedRectWithRectBasisOfHeight(rect)
  229. } else {
  230. rect = constrainedRectWithRectBasisOfWidth(rect)
  231. }
  232. }
  233. }
  234. let minWidth = leftEdgeView.bounds.width + rightEdgeView.bounds.width
  235. if rect.width < minWidth {
  236. rect.origin.x = frame.maxX - minWidth
  237. rect.size.width = minWidth
  238. }
  239. let minHeight = topEdgeView.bounds.height + bottomEdgeView.bounds.height
  240. if rect.height < minHeight {
  241. rect.origin.y = frame.maxY - minHeight
  242. rect.size.height = minHeight
  243. }
  244. if fixedAspectRatio > 0 {
  245. var constraintedFrame = rect
  246. if rect.width < minWidth {
  247. constraintedFrame.size.width = rect.size.height * (minWidth / rect.size.width)
  248. }
  249. if rect.height < minHeight {
  250. constraintedFrame.size.height = rect.size.width * (minHeight / rect.size.height)
  251. }
  252. rect = constraintedFrame
  253. }
  254. return rect
  255. }
  256. fileprivate func constrainedRectWithRectBasisOfWidth(_ frame: CGRect) -> CGRect {
  257. var result = frame
  258. let width = frame.width
  259. var height = frame.height
  260. if width < height {
  261. height = width / fixedAspectRatio
  262. } else {
  263. height = width * fixedAspectRatio
  264. }
  265. result.size = CGSize(width: width, height: height)
  266. return result
  267. }
  268. fileprivate func constrainedRectWithRectBasisOfHeight(_ frame: CGRect) -> CGRect {
  269. var result = frame
  270. var width = frame.width
  271. let height = frame.height
  272. if width < height {
  273. width = height * fixedAspectRatio
  274. } else {
  275. width = height / fixedAspectRatio
  276. }
  277. result.size = CGSize(width: width, height: height)
  278. return result
  279. }
  280. }