ZLClipImageViewController.swift 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283
  1. //
  2. // ZLClipImageViewController.swift
  3. // ZLPhotoBrowser
  4. //
  5. // Created by long on 2020/8/27.
  6. //
  7. // Copyright (c) 2020 Long Zhang <495181165@qq.com>
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. import UIKit
  27. extension ZLClipImageViewController {
  28. enum ClipPanEdge {
  29. case none
  30. case top
  31. case bottom
  32. case left
  33. case right
  34. case topLeft
  35. case topRight
  36. case bottomLeft
  37. case bottomRight
  38. }
  39. }
  40. class ZLClipImageViewController: UIViewController {
  41. static let bottomToolViewH: CGFloat = 90
  42. static let clipRatioItemSize: CGSize = CGSize(width: 60, height: 70)
  43. var animate = true
  44. /// 用作进入裁剪界面首次动画frame
  45. var presentAnimateFrame: CGRect?
  46. /// 用作进入裁剪界面首次动画和取消裁剪时动画的image
  47. var presentAnimateImage: UIImage?
  48. /// 取消裁剪时动画frame
  49. var cancelClipAnimateFrame: CGRect = .zero
  50. var viewDidAppearCount = 0
  51. let originalImage: UIImage
  52. let clipRatios: [ZLImageClipRatio]
  53. var editImage: UIImage
  54. /// 初次进入界面时候,裁剪范围
  55. var editRect: CGRect
  56. var scrollView: UIScrollView!
  57. var containerView: UIView!
  58. var imageView: UIImageView!
  59. var shadowView: ZLClipShadowView!
  60. var overlayView: ZLClipOverlayView!
  61. var gridPanGes: UIPanGestureRecognizer!
  62. var bottomToolView: UIView!
  63. var bottomShadowLayer: CAGradientLayer!
  64. var bottomToolLineView: UIView!
  65. var cancelBtn: UIButton!
  66. var revertBtn: UIButton!
  67. var doneBtn: UIButton!
  68. var rotateBtn: UIButton!
  69. var clipRatioColView: UICollectionView!
  70. var shouldLayout = true
  71. var panEdge: ZLClipImageViewController.ClipPanEdge = .none
  72. var beginPanPoint: CGPoint = .zero
  73. var clipBoxFrame: CGRect = .zero
  74. var clipOriginFrame: CGRect = .zero
  75. var isRotating = false
  76. var angle: CGFloat = 0
  77. var selectedRatio: ZLImageClipRatio
  78. var thumbnailImage: UIImage?
  79. lazy var maxClipFrame: CGRect = {
  80. var insets = deviceSafeAreaInsets()
  81. insets.top += 20
  82. var rect = CGRect.zero
  83. rect.origin.x = 15
  84. rect.origin.y = insets.top
  85. rect.size.width = UIScreen.main.bounds.width - 15 * 2
  86. rect.size.height = UIScreen.main.bounds.height - insets.top - ZLClipImageViewController.bottomToolViewH - ZLClipImageViewController.clipRatioItemSize.height - 25
  87. return rect
  88. }()
  89. var minClipSize = CGSize(width: 45, height: 45)
  90. var resetTimer: Timer?
  91. var dismissAnimateFromRect: CGRect = .zero
  92. var dismissAnimateImage: UIImage? = nil
  93. /// 传回旋转角度,图片编辑区域的rect
  94. var clipDoneBlock: ( (CGFloat, CGRect, ZLImageClipRatio) -> Void )?
  95. var cancelClipBlock: ( () -> Void )?
  96. override var prefersStatusBarHidden: Bool {
  97. return true
  98. }
  99. override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
  100. return .portrait
  101. }
  102. deinit {
  103. zl_debugPrint("ZLClipImageViewController deinit")
  104. self.cleanTimer()
  105. }
  106. init(image: UIImage, editRect: CGRect?, angle: CGFloat = 0, selectRatio: ZLImageClipRatio?) {
  107. self.originalImage = image
  108. self.clipRatios = ZLPhotoConfiguration.default().editImageClipRatios
  109. self.editRect = editRect ?? .zero
  110. self.angle = angle
  111. if angle == -90 {
  112. self.editImage = image.rotate(orientation: .left)
  113. } else if self.angle == -180 {
  114. self.editImage = image.rotate(orientation: .down)
  115. } else if self.angle == -270 {
  116. self.editImage = image.rotate(orientation: .right)
  117. } else {
  118. self.editImage = image
  119. }
  120. var firstEnter = false
  121. if let sr = selectRatio {
  122. self.selectedRatio = sr
  123. } else {
  124. firstEnter = true
  125. self.selectedRatio = ZLPhotoConfiguration.default().editImageClipRatios.first!
  126. }
  127. super.init(nibName: nil, bundle: nil)
  128. if firstEnter {
  129. self.calculateClipRect()
  130. }
  131. }
  132. required init?(coder: NSCoder) {
  133. fatalError("init(coder:) has not been implemented")
  134. }
  135. override func viewDidLoad() {
  136. super.viewDidLoad()
  137. self.setupUI()
  138. self.generateThumbnailImage()
  139. }
  140. override func viewDidAppear(_ animated: Bool) {
  141. super.viewDidAppear(animated)
  142. self.viewDidAppearCount += 1
  143. if self.presentingViewController is ZLEditImageViewController {
  144. self.transitioningDelegate = self
  145. }
  146. guard self.viewDidAppearCount == 1 else {
  147. return
  148. }
  149. if let frame = self.presentAnimateFrame, let image = self.presentAnimateImage {
  150. let animateImageView = UIImageView(image: image)
  151. animateImageView.contentMode = .scaleAspectFill
  152. animateImageView.clipsToBounds = true
  153. animateImageView.frame = frame
  154. self.view.addSubview(animateImageView)
  155. self.cancelClipAnimateFrame = self.clipBoxFrame
  156. UIView.animate(withDuration: 0.25, animations: {
  157. animateImageView.frame = self.clipBoxFrame
  158. self.bottomToolView.alpha = 1
  159. self.rotateBtn.alpha = 1
  160. }) { (_) in
  161. UIView.animate(withDuration: 0.1, animations: {
  162. self.scrollView.alpha = 1
  163. self.overlayView.alpha = 1
  164. }) { (_) in
  165. animateImageView.removeFromSuperview()
  166. }
  167. }
  168. } else {
  169. self.bottomToolView.alpha = 1
  170. self.rotateBtn.alpha = 1
  171. self.scrollView.alpha = 1
  172. self.overlayView.alpha = 1
  173. }
  174. }
  175. override func viewDidLayoutSubviews() {
  176. super.viewDidLayoutSubviews()
  177. guard self.shouldLayout else {
  178. return
  179. }
  180. self.shouldLayout = false
  181. self.scrollView.frame = self.view.bounds
  182. self.shadowView.frame = self.view.bounds
  183. self.layoutInitialImage()
  184. self.bottomToolView.frame = CGRect(x: 0, y: self.view.bounds.height-ZLClipImageViewController.bottomToolViewH, width: self.view.bounds.width, height: ZLClipImageViewController.bottomToolViewH)
  185. self.bottomShadowLayer.frame = self.bottomToolView.bounds
  186. self.bottomToolLineView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 1/UIScreen.main.scale)
  187. let toolBtnH: CGFloat = 25
  188. let toolBtnY = (ZLClipImageViewController.bottomToolViewH - toolBtnH) / 2 - 10
  189. self.cancelBtn.frame = CGRect(x: 30, y: toolBtnY, width: toolBtnH, height: toolBtnH)
  190. let revertBtnW = localLanguageTextValue(.revert).boundingRect(font: ZLLayout.bottomToolTitleFont, limitSize: CGSize(width: CGFloat.greatestFiniteMagnitude, height: toolBtnH)).width + 20
  191. self.revertBtn.frame = CGRect(x: (self.view.bounds.width-revertBtnW)/2, y: toolBtnY, width: revertBtnW, height: toolBtnH)
  192. self.doneBtn.frame = CGRect(x: self.view.bounds.width-30-toolBtnH, y: toolBtnY, width: toolBtnH, height: toolBtnH)
  193. let ratioColViewY = self.bottomToolView.frame.minY - ZLClipImageViewController.clipRatioItemSize.height - 5
  194. self.rotateBtn.frame = CGRect(x: 30, y: ratioColViewY + (ZLClipImageViewController.clipRatioItemSize.height-25)/2, width: 25, height: 25)
  195. let ratioColViewX = self.rotateBtn.frame.maxX + 15
  196. self.clipRatioColView.frame = CGRect(x: ratioColViewX, y: ratioColViewY, width: self.view.bounds.width - ratioColViewX, height: 70)
  197. if self.clipRatios.count > 1, let index = self.clipRatios.firstIndex(where: { $0 == self.selectedRatio}) {
  198. self.clipRatioColView.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: false)
  199. }
  200. }
  201. func setupUI() {
  202. self.view.backgroundColor = .black
  203. self.scrollView = UIScrollView()
  204. self.scrollView.alwaysBounceVertical = true
  205. self.scrollView.alwaysBounceHorizontal = true
  206. self.scrollView.showsVerticalScrollIndicator = false
  207. self.scrollView.showsHorizontalScrollIndicator = false
  208. if #available(iOS 11.0, *) {
  209. self.scrollView.contentInsetAdjustmentBehavior = .never
  210. } else {
  211. // Fallback on earlier versions
  212. }
  213. self.scrollView.delegate = self
  214. self.view.addSubview(self.scrollView)
  215. self.containerView = UIView()
  216. self.scrollView.addSubview(self.containerView)
  217. self.imageView = UIImageView(image: self.editImage)
  218. self.imageView.contentMode = .scaleAspectFit
  219. self.imageView.clipsToBounds = true
  220. self.containerView.addSubview(self.imageView)
  221. self.shadowView = ZLClipShadowView()
  222. self.shadowView.isUserInteractionEnabled = false
  223. self.shadowView.backgroundColor = UIColor.black.withAlphaComponent(0.3)
  224. self.view.addSubview(self.shadowView)
  225. self.overlayView = ZLClipOverlayView()
  226. self.overlayView.isUserInteractionEnabled = false
  227. self.view.addSubview(self.overlayView)
  228. self.bottomToolView = UIView()
  229. self.view.addSubview(self.bottomToolView)
  230. let color1 = UIColor.black.withAlphaComponent(0.15).cgColor
  231. let color2 = UIColor.black.withAlphaComponent(0.35).cgColor
  232. self.bottomShadowLayer = CAGradientLayer()
  233. self.bottomShadowLayer.colors = [color1, color2]
  234. self.bottomShadowLayer.locations = [0, 1]
  235. self.bottomToolView.layer.addSublayer(self.bottomShadowLayer)
  236. self.bottomToolLineView = UIView()
  237. self.bottomToolLineView.backgroundColor = zlRGB(240, 240, 240)
  238. self.bottomToolView.addSubview(self.bottomToolLineView)
  239. self.cancelBtn = UIButton(type: .custom)
  240. self.cancelBtn.setImage(getImage("zl_close"), for: .normal)
  241. self.cancelBtn.adjustsImageWhenHighlighted = false
  242. self.cancelBtn.zl_enlargeValidTouchArea(inset: 20)
  243. self.cancelBtn.addTarget(self, action: #selector(cancelBtnClick), for: .touchUpInside)
  244. self.bottomToolView.addSubview(self.cancelBtn)
  245. self.revertBtn = UIButton(type: .custom)
  246. self.revertBtn.setTitleColor(.white, for: .normal)
  247. self.revertBtn.setTitle(localLanguageTextValue(.revert), for: .normal)
  248. self.revertBtn.zl_enlargeValidTouchArea(inset: 20)
  249. self.revertBtn.titleLabel?.font = ZLLayout.bottomToolTitleFont
  250. self.revertBtn.addTarget(self, action: #selector(revertBtnClick), for: .touchUpInside)
  251. self.bottomToolView.addSubview(self.revertBtn)
  252. self.doneBtn = UIButton(type: .custom)
  253. self.doneBtn.setImage(getImage("zl_right"), for: .normal)
  254. self.doneBtn.adjustsImageWhenHighlighted = false
  255. self.doneBtn.zl_enlargeValidTouchArea(inset: 20)
  256. self.doneBtn.addTarget(self, action: #selector(doneBtnClick), for: .touchUpInside)
  257. self.bottomToolView.addSubview(self.doneBtn)
  258. self.rotateBtn = UIButton(type: .custom)
  259. self.rotateBtn.setImage(getImage("zl_rotateimage"), for: .normal)
  260. self.rotateBtn.adjustsImageWhenHighlighted = false
  261. self.rotateBtn.zl_enlargeValidTouchArea(inset: 20)
  262. self.rotateBtn.addTarget(self, action: #selector(rotateBtnClick), for: .touchUpInside)
  263. self.view.addSubview(self.rotateBtn)
  264. let layout = UICollectionViewFlowLayout()
  265. layout.itemSize = ZLClipImageViewController.clipRatioItemSize
  266. layout.scrollDirection = .horizontal
  267. layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 20)
  268. self.clipRatioColView = UICollectionView(frame: .zero, collectionViewLayout: layout)
  269. self.clipRatioColView.delegate = self
  270. self.clipRatioColView.dataSource = self
  271. self.clipRatioColView.backgroundColor = .clear
  272. self.clipRatioColView.isHidden = self.clipRatios.count <= 1
  273. self.clipRatioColView.showsHorizontalScrollIndicator = false
  274. self.view.addSubview(self.clipRatioColView)
  275. ZLImageClipRatioCell.zl_register(self.clipRatioColView)
  276. self.gridPanGes = UIPanGestureRecognizer(target: self, action: #selector(gridGesPanAction(_:)))
  277. self.gridPanGes.delegate = self
  278. self.view.addGestureRecognizer(self.gridPanGes)
  279. self.scrollView.panGestureRecognizer.require(toFail: self.gridPanGes)
  280. self.scrollView.alpha = 0
  281. self.overlayView.alpha = 0
  282. self.bottomToolView.alpha = 0
  283. self.rotateBtn.alpha = 0
  284. }
  285. func generateThumbnailImage() {
  286. let size: CGSize
  287. let ratio = (self.editImage.size.width / self.editImage.size.height)
  288. let fixLength: CGFloat = 100
  289. if ratio >= 1 {
  290. size = CGSize(width: fixLength * ratio, height: fixLength)
  291. } else {
  292. size = CGSize(width: fixLength, height: fixLength / ratio)
  293. }
  294. self.thumbnailImage = self.editImage.resize(size)
  295. }
  296. func calculateClipRect() {
  297. if self.selectedRatio.whRatio == 0 {
  298. self.editRect = CGRect(origin: .zero, size: self.editImage.size)
  299. } else {
  300. let imageSize = self.editImage.size
  301. let imageWHRatio = imageSize.width / imageSize.height
  302. var w: CGFloat = 0, h: CGFloat = 0
  303. if self.selectedRatio.whRatio >= imageWHRatio {
  304. w = imageSize.width
  305. h = w / self.selectedRatio.whRatio
  306. } else {
  307. h = imageSize.height
  308. w = h * self.selectedRatio.whRatio
  309. }
  310. self.editRect = CGRect(x: (imageSize.width - w) / 2, y: (imageSize.height - h) / 2, width: w, height: h)
  311. }
  312. }
  313. func layoutInitialImage() {
  314. self.scrollView.minimumZoomScale = 1
  315. self.scrollView.maximumZoomScale = 1
  316. self.scrollView.zoomScale = 1
  317. let editSize = self.editRect.size
  318. self.scrollView.contentSize = editSize
  319. let maxClipRect = self.maxClipFrame
  320. self.containerView.frame = CGRect(origin: .zero, size: self.editImage.size)
  321. self.imageView.frame = self.containerView.bounds
  322. // editRect比例,计算editRect所占frame
  323. let editScale = min(maxClipRect.width/editSize.width, maxClipRect.height/editSize.height)
  324. let scaledSize = CGSize(width: floor(editSize.width * editScale), height: floor(editSize.height * editScale))
  325. var frame = CGRect.zero
  326. frame.size = scaledSize
  327. frame.origin.x = maxClipRect.minX + floor((maxClipRect.width-frame.width) / 2)
  328. frame.origin.y = maxClipRect.minY + floor((maxClipRect.height-frame.height) / 2)
  329. // 按照edit image进行计算最小缩放比例
  330. let originalScale = min(maxClipRect.width/self.editImage.size.width, maxClipRect.height/self.editImage.size.height)
  331. // 将 edit rect 相对 originalScale 进行缩放,缩放到图片未放大时候的clip rect
  332. let scaleEditSize = CGSize(width: self.editRect.width * originalScale, height: self.editRect.height * originalScale)
  333. // 计算缩放后的clip rect相对maxClipRect的比例
  334. let clipRectZoomScale = min(maxClipRect.width/scaleEditSize.width, maxClipRect.height/scaleEditSize.height)
  335. self.scrollView.minimumZoomScale = originalScale
  336. self.scrollView.maximumZoomScale = 10
  337. // 设置当前zoom scale
  338. let zoomScale = (clipRectZoomScale * originalScale)
  339. self.scrollView.zoomScale = zoomScale
  340. self.scrollView.contentSize = CGSize(width: self.editImage.size.width * zoomScale, height: self.editImage.size.height * zoomScale)
  341. self.changeClipBoxFrame(newFrame: frame)
  342. if (frame.size.width < scaledSize.width - CGFloat.ulpOfOne) || (frame.size.height < scaledSize.height - CGFloat.ulpOfOne) {
  343. var offset = CGPoint.zero
  344. offset.x = -floor((self.scrollView.frame.width - scaledSize.width) / 2)
  345. offset.y = -floor((self.scrollView.frame.height - scaledSize.height) / 2)
  346. self.scrollView.contentOffset = offset
  347. }
  348. // edit rect 相对 image size 的 偏移量
  349. let diffX = self.editRect.origin.x / self.editImage.size.width * self.scrollView.contentSize.width
  350. let diffY = self.editRect.origin.y / self.editImage.size.height * self.scrollView.contentSize.height
  351. self.scrollView.contentOffset = CGPoint(x: -self.scrollView.contentInset.left+diffX, y: -self.scrollView.contentInset.top+diffY)
  352. }
  353. func changeClipBoxFrame(newFrame: CGRect) {
  354. guard self.clipBoxFrame != newFrame else {
  355. return
  356. }
  357. if newFrame.width < CGFloat.ulpOfOne || newFrame.height < CGFloat.ulpOfOne {
  358. return
  359. }
  360. var frame = newFrame
  361. let originX = ceil(self.maxClipFrame.minX)
  362. let diffX = frame.minX - originX
  363. frame.origin.x = max(frame.minX, originX)
  364. // frame.origin.x = floor(max(frame.minX, originX))
  365. if diffX < -CGFloat.ulpOfOne {
  366. frame.size.width += diffX
  367. }
  368. let originY = ceil(self.maxClipFrame.minY)
  369. let diffY = frame.minY - originY
  370. frame.origin.y = max(frame.minY, originY)
  371. // frame.origin.y = floor(max(frame.minY, originY))
  372. if diffY < -CGFloat.ulpOfOne {
  373. frame.size.height += diffY
  374. }
  375. let maxW = self.maxClipFrame.width + self.maxClipFrame.minX - frame.minX
  376. frame.size.width = max(self.minClipSize.width, min(frame.width, maxW))
  377. // frame.size.width = floor(max(self.minClipSize.width, min(frame.width, maxW)))
  378. let maxH = self.maxClipFrame.height + self.maxClipFrame.minY - frame.minY
  379. frame.size.height = max(self.minClipSize.height, min(frame.height, maxH))
  380. // frame.size.height = floor(max(self.minClipSize.height, min(frame.height, maxH)))
  381. self.clipBoxFrame = frame
  382. self.shadowView.clearRect = frame
  383. self.overlayView.frame = frame.insetBy(dx: -ZLClipOverlayView.cornerLineWidth, dy: -ZLClipOverlayView.cornerLineWidth)
  384. self.scrollView.contentInset = UIEdgeInsets(top: frame.minY, left: frame.minX, bottom: self.scrollView.frame.maxY-frame.maxY, right: self.scrollView.frame.maxX-frame.maxX)
  385. let scale = max(frame.height/self.editImage.size.height, frame.width/self.editImage.size.width)
  386. self.scrollView.minimumZoomScale = scale
  387. // var size = self.scrollView.contentSize
  388. // size.width = floor(size.width)
  389. // size.height = floor(size.height)
  390. // self.scrollView.contentSize = size
  391. self.scrollView.zoomScale = self.scrollView.zoomScale
  392. }
  393. @objc func cancelBtnClick() {
  394. self.dismissAnimateFromRect = self.cancelClipAnimateFrame
  395. self.dismissAnimateImage = self.presentAnimateImage
  396. self.cancelClipBlock?()
  397. self.dismiss(animated: self.animate, completion: nil)
  398. }
  399. @objc func revertBtnClick() {
  400. self.angle = 0
  401. self.editImage = self.originalImage
  402. self.calculateClipRect()
  403. self.imageView.image = self.editImage
  404. self.layoutInitialImage()
  405. self.generateThumbnailImage()
  406. self.clipRatioColView.reloadData()
  407. }
  408. @objc func doneBtnClick() {
  409. let image = self.clipImage()
  410. self.dismissAnimateFromRect = self.clipBoxFrame
  411. self.dismissAnimateImage = image.clipImage
  412. self.clipDoneBlock?(self.angle, image.editRect, self.selectedRatio)
  413. self.dismiss(animated: self.animate, completion: nil)
  414. }
  415. @objc func rotateBtnClick() {
  416. guard !self.isRotating else {
  417. return
  418. }
  419. self.angle -= 90
  420. if self.angle == -360 {
  421. self.angle = 0
  422. }
  423. self.isRotating = true
  424. let animateImageView = UIImageView(image: self.editImage)
  425. animateImageView.contentMode = .scaleAspectFit
  426. animateImageView.clipsToBounds = true
  427. let originFrame = self.view.convert(self.containerView.frame, from: self.scrollView)
  428. animateImageView.frame = originFrame
  429. self.view.addSubview(animateImageView)
  430. if self.selectedRatio.whRatio == 0 || self.selectedRatio.whRatio == 1 {
  431. // 自由比例和1:1比例,进行edit rect转换
  432. // 将edit rect转换为相对edit image的rect
  433. let rect = self.convertClipRectToEditImageRect()
  434. // 旋转图片
  435. self.editImage = self.editImage.rotate(orientation: .left)
  436. // 将rect进行旋转,转换到相对于旋转后的edit image的rect
  437. self.editRect = CGRect(x: rect.minY, y: self.editImage.size.height-rect.minX-rect.width, width: rect.height, height: rect.width)
  438. } else {
  439. // 其他比例的裁剪框,旋转后都重置edit rect
  440. // 旋转图片
  441. self.editImage = self.editImage.rotate(orientation: .left)
  442. self.calculateClipRect()
  443. }
  444. self.imageView.image = self.editImage
  445. self.layoutInitialImage()
  446. let toFrame = self.view.convert(self.containerView.frame, from: self.scrollView)
  447. let transform = CGAffineTransform(rotationAngle: -CGFloat.pi/2)
  448. self.overlayView.alpha = 0
  449. self.containerView.alpha = 0
  450. UIView.animate(withDuration: 0.3, animations: {
  451. animateImageView.transform = transform
  452. animateImageView.frame = toFrame
  453. }) { (_) in
  454. animateImageView.removeFromSuperview()
  455. self.overlayView.alpha = 1
  456. self.containerView.alpha = 1
  457. self.isRotating = false
  458. }
  459. self.generateThumbnailImage()
  460. self.clipRatioColView.reloadData()
  461. }
  462. @objc func gridGesPanAction(_ pan: UIPanGestureRecognizer) {
  463. let point = pan.location(in: self.view)
  464. if pan.state == .began {
  465. self.startEditing()
  466. self.beginPanPoint = point
  467. self.clipOriginFrame = self.clipBoxFrame
  468. self.panEdge = self.calculatePanEdge(at: point)
  469. } else if pan.state == .changed {
  470. guard self.panEdge != .none else {
  471. return
  472. }
  473. self.updateClipBoxFrame(point: point)
  474. } else if pan.state == .cancelled || pan.state == .ended {
  475. self.panEdge = .none
  476. self.startTimer()
  477. }
  478. }
  479. func calculatePanEdge(at point: CGPoint) -> ZLClipImageViewController.ClipPanEdge {
  480. let frame = self.clipBoxFrame.insetBy(dx: -30, dy: -30)
  481. let cornerSize = CGSize(width: 60, height: 60)
  482. let topLeftRect = CGRect(origin: frame.origin, size: cornerSize)
  483. if topLeftRect.contains(point) {
  484. return .topLeft
  485. }
  486. let topRightRect = CGRect(origin: CGPoint(x: frame.maxX-cornerSize.width, y: frame.minY), size: cornerSize)
  487. if topRightRect.contains(point) {
  488. return .topRight
  489. }
  490. let bottomLeftRect = CGRect(origin: CGPoint(x: frame.minX, y: frame.maxY-cornerSize.height), size: cornerSize)
  491. if bottomLeftRect.contains(point) {
  492. return .bottomLeft
  493. }
  494. let bottomRightRect = CGRect(origin: CGPoint(x: frame.maxX-cornerSize.width, y: frame.maxY-cornerSize.height), size: cornerSize)
  495. if bottomRightRect.contains(point) {
  496. return .bottomRight
  497. }
  498. let topRect = CGRect(origin: frame.origin, size: CGSize(width: frame.width, height: cornerSize.height))
  499. if topRect.contains(point) {
  500. return .top
  501. }
  502. let bottomRect = CGRect(origin: CGPoint(x: frame.minX, y: frame.maxY-cornerSize.height), size: CGSize(width: frame.width, height: cornerSize.height))
  503. if bottomRect.contains(point) {
  504. return .bottom
  505. }
  506. let leftRect = CGRect(origin: frame.origin, size: CGSize(width: cornerSize.width, height: frame.height))
  507. if leftRect.contains(point) {
  508. return .left
  509. }
  510. let rightRect = CGRect(origin: CGPoint(x: frame.maxX-cornerSize.width, y: frame.minY), size: CGSize(width: cornerSize.width, height: frame.height))
  511. if rightRect.contains(point) {
  512. return .right
  513. }
  514. return .none
  515. }
  516. func updateClipBoxFrame(point: CGPoint) {
  517. var frame = self.clipBoxFrame
  518. let originFrame = self.clipOriginFrame
  519. var newPoint = point
  520. newPoint.x = max(self.maxClipFrame.minX, newPoint.x)
  521. newPoint.y = max(self.maxClipFrame.minY, newPoint.y)
  522. let diffX = ceil(newPoint.x - self.beginPanPoint.x)
  523. let diffY = ceil(newPoint.y - self.beginPanPoint.y)
  524. let ratio = self.selectedRatio.whRatio
  525. switch self.panEdge {
  526. case .left:
  527. frame.origin.x = originFrame.minX + diffX
  528. frame.size.width = originFrame.width - diffX
  529. if ratio != 0 {
  530. frame.size.height = originFrame.height - diffX / ratio
  531. }
  532. case .right:
  533. frame.size.width = originFrame.width + diffX
  534. if ratio != 0 {
  535. frame.size.height = originFrame.height + diffX / ratio
  536. }
  537. case .top:
  538. frame.origin.y = originFrame.minY + diffY
  539. frame.size.height = originFrame.height - diffY
  540. if ratio != 0 {
  541. frame.size.width = originFrame.width - diffY * ratio
  542. }
  543. case .bottom:
  544. frame.size.height = originFrame.height + diffY
  545. if ratio != 0 {
  546. frame.size.width = originFrame.width + diffY * ratio
  547. }
  548. case .topLeft:
  549. if ratio != 0 {
  550. // if abs(diffX / ratio) >= abs(diffY) {
  551. frame.origin.x = originFrame.minX + diffX
  552. frame.size.width = originFrame.width - diffX
  553. frame.origin.y = originFrame.minY + diffX / ratio
  554. frame.size.height = originFrame.height - diffX / ratio
  555. // } else {
  556. // frame.origin.y = originFrame.minY + diffY
  557. // frame.size.height = originFrame.height - diffY
  558. // frame.origin.x = originFrame.minX + diffY * ratio
  559. // frame.size.width = originFrame.width - diffY * ratio
  560. // }
  561. } else {
  562. frame.origin.x = originFrame.minX + diffX
  563. frame.size.width = originFrame.width - diffX
  564. frame.origin.y = originFrame.minY + diffY
  565. frame.size.height = originFrame.height - diffY
  566. }
  567. case .topRight:
  568. if ratio != 0 {
  569. // if abs(diffX / ratio) >= abs(diffY) {
  570. frame.size.width = originFrame.width + diffX
  571. frame.origin.y = originFrame.minY - diffX / ratio
  572. frame.size.height = originFrame.height + diffX / ratio
  573. // } else {
  574. // frame.origin.y = originFrame.minY + diffY
  575. // frame.size.height = originFrame.height - diffY
  576. // frame.size.width = originFrame.width - diffY * ratio
  577. // }
  578. } else {
  579. frame.size.width = originFrame.width + diffX
  580. frame.origin.y = originFrame.minY + diffY
  581. frame.size.height = originFrame.height - diffY
  582. }
  583. case .bottomLeft:
  584. if ratio != 0 {
  585. // if abs(diffX / ratio) >= abs(diffY) {
  586. frame.origin.x = originFrame.minX + diffX
  587. frame.size.width = originFrame.width - diffX
  588. frame.size.height = originFrame.height - diffX / ratio
  589. // } else {
  590. // frame.origin.x = originFrame.minX - diffY * ratio
  591. // frame.size.width = originFrame.width + diffY * ratio
  592. // frame.size.height = originFrame.height + diffY
  593. // }
  594. } else {
  595. frame.origin.x = originFrame.minX + diffX
  596. frame.size.width = originFrame.width - diffX
  597. frame.size.height = originFrame.height + diffY
  598. }
  599. case .bottomRight:
  600. if ratio != 0 {
  601. // if abs(diffX / ratio) >= abs(diffY) {
  602. frame.size.width = originFrame.width + diffX
  603. frame.size.height = originFrame.height + diffX / ratio
  604. // } else {
  605. // frame.size.width += diffY * ratio
  606. // frame.size.height += diffY
  607. // }
  608. } else {
  609. frame.size.width = originFrame.width + diffX
  610. frame.size.height = originFrame.height + diffY
  611. }
  612. default:
  613. break
  614. }
  615. let minSize: CGSize
  616. let maxSize: CGSize
  617. let maxClipFrame: CGRect
  618. if ratio != 0 {
  619. if ratio >= 1 {
  620. minSize = CGSize(width: self.minClipSize.height * ratio, height: self.minClipSize.height)
  621. } else {
  622. minSize = CGSize(width: self.minClipSize.width, height: self.minClipSize.width / ratio)
  623. }
  624. if ratio > self.maxClipFrame.width / self.maxClipFrame.height {
  625. maxSize = CGSize(width: self.maxClipFrame.width, height: self.maxClipFrame.width / ratio)
  626. } else {
  627. maxSize = CGSize(width: self.maxClipFrame.height * ratio, height: self.maxClipFrame.height)
  628. }
  629. maxClipFrame = CGRect(origin: CGPoint(x: self.maxClipFrame.minX + (self.maxClipFrame.width-maxSize.width)/2, y: self.maxClipFrame.minY + (self.maxClipFrame.height-maxSize.height)/2), size: maxSize)
  630. } else {
  631. minSize = self.minClipSize
  632. maxSize = self.maxClipFrame.size
  633. maxClipFrame = self.maxClipFrame
  634. }
  635. frame.size.width = min(maxSize.width, max(minSize.width, frame.size.width))
  636. frame.size.height = min(maxSize.height, max(minSize.height, frame.size.height))
  637. frame.origin.x = min(maxClipFrame.maxX-minSize.width, max(frame.origin.x, maxClipFrame.minX))
  638. frame.origin.y = min(maxClipFrame.maxY-minSize.height, max(frame.origin.y, maxClipFrame.minY))
  639. if (self.panEdge == .topLeft || self.panEdge == .bottomLeft || self.panEdge == .left) && frame.size.width <= minSize.width + CGFloat.ulpOfOne {
  640. frame.origin.x = originFrame.maxX - minSize.width
  641. }
  642. if (self.panEdge == .topLeft || self.panEdge == .topRight || self.panEdge == .top) && frame.size.height <= minSize.height + CGFloat.ulpOfOne {
  643. frame.origin.y = originFrame.maxY - minSize.height
  644. }
  645. self.changeClipBoxFrame(newFrame: frame)
  646. }
  647. func startEditing() {
  648. self.cleanTimer()
  649. self.shadowView.alpha = 0
  650. if self.rotateBtn.alpha != 0 {
  651. self.rotateBtn.layer.removeAllAnimations()
  652. self.clipRatioColView.layer.removeAllAnimations()
  653. UIView.animate(withDuration: 0.2) {
  654. self.rotateBtn.alpha = 0
  655. self.clipRatioColView.alpha = 0
  656. }
  657. }
  658. }
  659. func endEditing() {
  660. self.moveClipContentToCenter()
  661. }
  662. func startTimer() {
  663. self.cleanTimer()
  664. self.resetTimer = Timer.scheduledTimer(withTimeInterval: 0.8, repeats: false, block: { (timer) in
  665. self.cleanTimer()
  666. self.endEditing()
  667. })
  668. }
  669. func cleanTimer() {
  670. self.resetTimer?.invalidate()
  671. self.resetTimer = nil
  672. }
  673. func moveClipContentToCenter() {
  674. let maxClipRect = self.maxClipFrame
  675. var clipRect = self.clipBoxFrame
  676. if clipRect.width < CGFloat.ulpOfOne || clipRect.height < CGFloat.ulpOfOne {
  677. return
  678. }
  679. let scale = min(maxClipRect.width/clipRect.width, maxClipRect.height/clipRect.height)
  680. let focusPoint = CGPoint(x: clipRect.midX, y: clipRect.midY)
  681. let midPoint = CGPoint(x: maxClipRect.midX, y: maxClipRect.midY)
  682. clipRect.size.width = ceil(clipRect.width * scale)
  683. clipRect.size.height = ceil(clipRect.height * scale)
  684. clipRect.origin.x = maxClipRect.minX + ceil((maxClipRect.width-clipRect.width)/2)
  685. clipRect.origin.y = maxClipRect.minY + ceil((maxClipRect.height-clipRect.height)/2)
  686. var contentTargetPoint = CGPoint.zero
  687. contentTargetPoint.x = (focusPoint.x + self.scrollView.contentOffset.x) * scale
  688. contentTargetPoint.y = (focusPoint.y + self.scrollView.contentOffset.y) * scale
  689. var offset = CGPoint(x: contentTargetPoint.x - midPoint.x, y: contentTargetPoint.y - midPoint.y)
  690. offset.x = max(-clipRect.minX, offset.x)
  691. offset.y = max(-clipRect.minY, offset.y)
  692. UIView.animate(withDuration: 0.3) {
  693. if scale < 1 - CGFloat.ulpOfOne || scale > 1 + CGFloat.ulpOfOne {
  694. self.scrollView.zoomScale *= scale
  695. self.scrollView.zoomScale = min(self.scrollView.maximumZoomScale, self.scrollView.zoomScale)
  696. }
  697. if self.scrollView.zoomScale < self.scrollView.maximumZoomScale - CGFloat.ulpOfOne {
  698. offset.x = min(self.scrollView.contentSize.width-clipRect.maxX, offset.x)
  699. offset.y = min(self.scrollView.contentSize.height-clipRect.maxY, offset.y)
  700. self.scrollView.contentOffset = offset
  701. }
  702. self.rotateBtn.alpha = 1
  703. self.clipRatioColView.alpha = 1
  704. self.shadowView.alpha = 1
  705. self.changeClipBoxFrame(newFrame: clipRect)
  706. }
  707. }
  708. func clipImage() -> (clipImage: UIImage, editRect: CGRect) {
  709. let frame = self.convertClipRectToEditImageRect()
  710. let origin = CGPoint(x: -frame.minX, y: -frame.minY)
  711. UIGraphicsBeginImageContextWithOptions(frame.size, false, self.editImage.scale)
  712. self.editImage.draw(at: origin)
  713. let temp = UIGraphicsGetImageFromCurrentImageContext()
  714. UIGraphicsEndImageContext()
  715. guard let cgi = temp?.cgImage else {
  716. return (self.editImage, CGRect(origin: .zero, size: self.editImage.size))
  717. }
  718. let newImage = UIImage(cgImage: cgi, scale: self.editImage.scale, orientation: .up)
  719. return (newImage, frame)
  720. }
  721. func convertClipRectToEditImageRect() -> CGRect {
  722. let imageSize = self.editImage.size
  723. let contentSize = self.scrollView.contentSize
  724. let offset = self.scrollView.contentOffset
  725. let insets = self.scrollView.contentInset
  726. var frame = CGRect.zero
  727. frame.origin.x = floor((offset.x + insets.left) * (imageSize.width / contentSize.width))
  728. frame.origin.x = max(0, frame.origin.x)
  729. frame.origin.y = floor((offset.y + insets.top) * (imageSize.height / contentSize.height))
  730. frame.origin.y = max(0, frame.origin.y)
  731. frame.size.width = ceil(self.clipBoxFrame.width * (imageSize.width / contentSize.width))
  732. frame.size.width = min(imageSize.width, frame.width)
  733. frame.size.height = ceil(self.clipBoxFrame.height * (imageSize.height / contentSize.height))
  734. frame.size.height = min(imageSize.height, frame.height)
  735. return frame
  736. }
  737. }
  738. extension ZLClipImageViewController: UIGestureRecognizerDelegate {
  739. func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
  740. guard gestureRecognizer == self.gridPanGes else {
  741. return true
  742. }
  743. let point = gestureRecognizer.location(in: self.view)
  744. let frame = self.overlayView.frame
  745. let innerFrame = frame.insetBy(dx: 22, dy: 22)
  746. let outerFrame = frame.insetBy(dx: -22, dy: -22)
  747. if innerFrame.contains(point) || !outerFrame.contains(point) {
  748. return false
  749. }
  750. return true
  751. }
  752. }
  753. extension ZLClipImageViewController: UICollectionViewDataSource, UICollectionViewDelegate {
  754. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  755. return self.clipRatios.count
  756. }
  757. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  758. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLImageClipRatioCell.zl_identifier(), for: indexPath) as! ZLImageClipRatioCell
  759. let ratio = self.clipRatios[indexPath.row]
  760. cell.configureCell(image: self.thumbnailImage ?? self.editImage, ratio: ratio)
  761. if ratio == self.selectedRatio {
  762. cell.titleLabel.textColor = .white
  763. } else {
  764. cell.titleLabel.textColor = zlRGB(160, 160, 160)
  765. }
  766. return cell
  767. }
  768. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  769. let ratio = self.clipRatios[indexPath.row]
  770. guard ratio != self.selectedRatio else {
  771. return
  772. }
  773. self.selectedRatio = ratio
  774. self.clipRatioColView.reloadData()
  775. self.clipRatioColView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
  776. self.calculateClipRect()
  777. self.layoutInitialImage()
  778. }
  779. }
  780. extension ZLClipImageViewController: UIScrollViewDelegate {
  781. func viewForZooming(in scrollView: UIScrollView) -> UIView? {
  782. return self.containerView
  783. }
  784. func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
  785. self.startEditing()
  786. }
  787. func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
  788. guard scrollView == self.scrollView else {
  789. return
  790. }
  791. self.startEditing()
  792. }
  793. func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  794. guard scrollView == self.scrollView else {
  795. return
  796. }
  797. self.startTimer()
  798. }
  799. func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
  800. guard scrollView == self.scrollView else {
  801. return
  802. }
  803. if !decelerate {
  804. self.startTimer()
  805. }
  806. }
  807. }
  808. extension ZLClipImageViewController: UIViewControllerTransitioningDelegate {
  809. func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
  810. return ZLClipImageDismissAnimatedTransition()
  811. }
  812. }
  813. // MARK: 裁剪比例cell
  814. class ZLImageClipRatioCell: UICollectionViewCell {
  815. var imageView: UIImageView!
  816. var titleLabel: UILabel!
  817. var image: UIImage?
  818. var ratio: ZLImageClipRatio!
  819. override init(frame: CGRect) {
  820. super.init(frame: frame)
  821. self.setupUI()
  822. }
  823. required init?(coder: NSCoder) {
  824. fatalError("init(coder:) has not been implemented")
  825. }
  826. override func layoutSubviews() {
  827. super.layoutSubviews()
  828. guard let ratio = self.ratio, let image = self.image else {
  829. return
  830. }
  831. let center = self.imageView.center
  832. var w: CGFloat = 0, h: CGFloat = 0
  833. let imageMaxW = self.bounds.width-10
  834. if ratio.whRatio == 0 {
  835. let maxSide = max(image.size.width, image.size.height)
  836. w = imageMaxW * image.size.width / maxSide
  837. h = imageMaxW * image.size.height / maxSide
  838. } else {
  839. if ratio.whRatio >= 1 {
  840. w = imageMaxW
  841. h = w / ratio.whRatio
  842. } else {
  843. h = imageMaxW
  844. w = h * ratio.whRatio
  845. }
  846. }
  847. self.imageView.frame = CGRect(x: center.x-w/2, y: center.y-h/2, width: w, height: h)
  848. }
  849. func setupUI() {
  850. self.imageView = UIImageView(frame: CGRect(x: 8, y: 5, width: self.bounds.width-16, height: self.bounds.width-16))
  851. self.imageView.contentMode = .scaleAspectFill
  852. self.imageView.layer.cornerRadius = 3
  853. self.imageView.layer.masksToBounds = true
  854. self.imageView.clipsToBounds = true
  855. self.contentView.addSubview(self.imageView)
  856. self.titleLabel = UILabel(frame: CGRect(x: 0, y: self.bounds.height-15, width: self.bounds.width, height: 12))
  857. self.titleLabel.font = getFont(12)
  858. self.titleLabel.textColor = .white
  859. self.titleLabel.textAlignment = .center
  860. self.titleLabel.layer.shadowColor = UIColor.black.withAlphaComponent(0.3).cgColor
  861. self.titleLabel.layer.shadowOffset = .zero
  862. self.titleLabel.layer.shadowOpacity = 1
  863. self.contentView.addSubview(self.titleLabel)
  864. }
  865. func configureCell(image: UIImage, ratio: ZLImageClipRatio) {
  866. self.imageView.image = image
  867. self.titleLabel.text = ratio.title
  868. self.image = image
  869. self.ratio = ratio
  870. self.setNeedsLayout()
  871. }
  872. }
  873. class ZLClipShadowView: UIView {
  874. var clearRect: CGRect = .zero {
  875. didSet {
  876. self.setNeedsDisplay()
  877. }
  878. }
  879. override init(frame: CGRect) {
  880. super.init(frame: frame)
  881. self.backgroundColor = .clear
  882. self.isOpaque = false
  883. }
  884. required init?(coder: NSCoder) {
  885. fatalError("init(coder:) has not been implemented")
  886. }
  887. override func draw(_ rect: CGRect) {
  888. UIColor(white: 0, alpha: 0.7).setFill()
  889. UIRectFill(rect)
  890. let cr = self.clearRect.intersection(rect)
  891. UIColor.clear.setFill()
  892. UIRectFill(cr)
  893. }
  894. }
  895. // MARK: 裁剪网格视图
  896. class ZLClipOverlayView: UIView {
  897. static let cornerLineWidth: CGFloat = 3
  898. var cornerBoldLines: [UIView] = []
  899. var velLines: [UIView] = []
  900. var horLines: [UIView] = []
  901. override init(frame: CGRect) {
  902. super.init(frame: frame)
  903. self.backgroundColor = .clear
  904. self.clipsToBounds = false
  905. // 两种方法实现裁剪框,drawrect动画效果 更好一点
  906. // func line(_ isCorner: Bool) -> UIView {
  907. // let line = UIView()
  908. // line.backgroundColor = .white
  909. // line.layer.shadowColor = UIColor.black.cgColor
  910. // if !isCorner {
  911. // line.layer.shadowOffset = .zero
  912. // line.layer.shadowRadius = 1.5
  913. // line.layer.shadowOpacity = 0.8
  914. // }
  915. // self.addSubview(line)
  916. // return line
  917. // }
  918. //
  919. // (0..<8).forEach { (_) in
  920. // self.cornerBoldLines.append(line(true))
  921. // }
  922. //
  923. // (0..<4).forEach { (_) in
  924. // self.velLines.append(line(false))
  925. // self.horLines.append(line(false))
  926. // }
  927. }
  928. required init?(coder: NSCoder) {
  929. fatalError("init(coder:) has not been implemented")
  930. }
  931. override func layoutSubviews() {
  932. super.layoutSubviews()
  933. self.setNeedsDisplay()
  934. // let borderLineLength: CGFloat = 20
  935. // let borderLineWidth: CGFloat = ZLClipOverlayView.cornerLineWidth
  936. // for (i, line) in self.cornerBoldLines.enumerated() {
  937. // switch i {
  938. // case 0:
  939. // // 左上 hor
  940. // line.frame = CGRect(x: -borderLineWidth, y: -borderLineWidth, width: borderLineLength, height: borderLineWidth)
  941. // case 1:
  942. // // 左上 vel
  943. // line.frame = CGRect(x: -borderLineWidth, y: -borderLineWidth, width: borderLineWidth, height: borderLineLength)
  944. // case 2:
  945. // // 右上 hor
  946. // line.frame = CGRect(x: self.bounds.width-borderLineLength+borderLineWidth, y: -borderLineWidth, width: borderLineLength, height: borderLineWidth)
  947. // case 3:
  948. // // 右上 vel
  949. // line.frame = CGRect(x: self.bounds.width, y: -borderLineWidth, width: borderLineWidth, height: borderLineLength)
  950. // case 4:
  951. // // 左下 hor
  952. // line.frame = CGRect(x: -borderLineWidth, y: self.bounds.height, width: borderLineLength, height: borderLineWidth)
  953. // case 5:
  954. // // 左下 vel
  955. // line.frame = CGRect(x: -borderLineWidth, y: self.bounds.height-borderLineLength+borderLineWidth, width: borderLineWidth, height: borderLineLength)
  956. // case 6:
  957. // // 右下 hor
  958. // line.frame = CGRect(x: self.bounds.width-borderLineLength+borderLineWidth, y: self.bounds.height, width: borderLineLength, height: borderLineWidth)
  959. // case 7:
  960. // line.frame = CGRect(x: self.bounds.width, y: self.bounds.height-borderLineLength+borderLineWidth, width: borderLineWidth, height: borderLineLength)
  961. //
  962. // default:
  963. // break
  964. // }
  965. // }
  966. //
  967. // let normalLineWidth: CGFloat = 1
  968. // var x: CGFloat = 0
  969. // var y: CGFloat = -1
  970. // // 横线
  971. // for (index, line) in self.horLines.enumerated() {
  972. // if index == 0 || index == 3 {
  973. // x = borderLineLength-borderLineWidth
  974. // } else {
  975. // x = 0
  976. // }
  977. // line.frame = CGRect(x: x, y: y, width: self.bounds.width - x * 2, height: normalLineWidth)
  978. // y += (self.bounds.height + 1) / 3
  979. // }
  980. //
  981. // x = -1
  982. // y = 0
  983. // // 竖线
  984. // for (index, line) in self.velLines.enumerated() {
  985. // if index == 0 || index == 3 {
  986. // y = borderLineLength-borderLineWidth
  987. // } else {
  988. // y = 0
  989. // }
  990. // line.frame = CGRect(x: x, y: y, width: normalLineWidth, height: self.bounds.height - y * 2)
  991. // x += (self.bounds.width + 1) / 3
  992. // }
  993. }
  994. override func draw(_ rect: CGRect) {
  995. let context = UIGraphicsGetCurrentContext()
  996. context?.setFillColor(UIColor.clear.cgColor)
  997. context?.setStrokeColor(UIColor.white.cgColor)
  998. context?.setLineWidth(1)
  999. context?.beginPath()
  1000. var dw: CGFloat = 3
  1001. for _ in 0..<4 {
  1002. context?.move(to: CGPoint(x: rect.origin.x+dw, y: rect.origin.y+3))
  1003. context?.addLine(to: CGPoint(x: rect.origin.x+dw, y: rect.origin.y+rect.height-3))
  1004. dw += (rect.size.width - 6) / 3
  1005. }
  1006. var dh: CGFloat = 3
  1007. for _ in 0..<4 {
  1008. context?.move(to: CGPoint(x: rect.origin.x+3, y: rect.origin.y+dh))
  1009. context?.addLine(to: CGPoint(x: rect.origin.x+rect.width-3, y: rect.origin.y+dh))
  1010. dh += (rect.size.height - 6) / 3
  1011. }
  1012. context?.strokePath()
  1013. context?.setLineWidth(3)
  1014. let boldLineLength: CGFloat = 20
  1015. // 左上
  1016. context?.move(to: CGPoint(x: 0, y: 1.5))
  1017. context?.addLine(to: CGPoint(x: boldLineLength, y: 1.5))
  1018. context?.move(to: CGPoint(x: 1.5, y: 0))
  1019. context?.addLine(to: CGPoint(x: 1.5, y: boldLineLength))
  1020. // 右上
  1021. context?.move(to: CGPoint(x: rect.width-boldLineLength, y: 1.5))
  1022. context?.addLine(to: CGPoint(x: rect.width, y: 1.5))
  1023. context?.move(to: CGPoint(x: rect.width-1.5, y: 0))
  1024. context?.addLine(to: CGPoint(x: rect.width-1.5, y: boldLineLength))
  1025. // 左下
  1026. context?.move(to: CGPoint(x: 1.5, y: rect.height-boldLineLength))
  1027. context?.addLine(to: CGPoint(x: 1.5, y: rect.height))
  1028. context?.move(to: CGPoint(x: 0, y: rect.height-1.5))
  1029. context?.addLine(to: CGPoint(x: boldLineLength, y: rect.height-1.5))
  1030. // 右下
  1031. context?.move(to: CGPoint(x: rect.width-boldLineLength, y: rect.height-1.5))
  1032. context?.addLine(to: CGPoint(x: rect.width, y: rect.height-1.5))
  1033. context?.move(to: CGPoint(x: rect.width-1.5, y: rect.height-boldLineLength))
  1034. context?.addLine(to: CGPoint(x: rect.width-1.5, y: rect.height))
  1035. context?.strokePath()
  1036. context?.setShadow(offset: CGSize(width: 1, height: 1), blur: 0)
  1037. }
  1038. }