ZLEditImageViewController.swift 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525
  1. //
  2. // ZLEditImageViewController.swift
  3. // ZLPhotoBrowser
  4. //
  5. // Created by long on 2020/8/26.
  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. public class ZLEditImageModel: NSObject {
  28. public let drawPaths: [ZLDrawPath]
  29. public let mosaicPaths: [ZLMosaicPath]
  30. public let editRect: CGRect?
  31. public let angle: CGFloat
  32. public let selectRatio: ZLImageClipRatio?
  33. public let selectFilter: ZLFilter?
  34. public let textStickers: [(state: ZLTextStickerState, index: Int)]?
  35. public let imageStickers: [(state: ZLImageStickerState, index: Int)]?
  36. init(drawPaths: [ZLDrawPath], mosaicPaths: [ZLMosaicPath], editRect: CGRect?, angle: CGFloat, selectRatio: ZLImageClipRatio?, selectFilter: ZLFilter, textStickers: [(state: ZLTextStickerState, index: Int)]?, imageStickers: [(state: ZLImageStickerState, index: Int)]?) {
  37. self.drawPaths = drawPaths
  38. self.mosaicPaths = mosaicPaths
  39. self.editRect = editRect
  40. self.angle = angle
  41. self.selectRatio = selectRatio
  42. self.selectFilter = selectFilter
  43. self.textStickers = textStickers
  44. self.imageStickers = imageStickers
  45. super.init()
  46. }
  47. }
  48. public class ZLEditImageViewController: UIViewController {
  49. static let filterColViewH: CGFloat = 80
  50. static let maxDrawLineImageWidth: CGFloat = 600
  51. static let ashbinNormalBgColor = zlRGB(40, 40, 40).withAlphaComponent(0.8)
  52. var animate = false
  53. var originalImage: UIImage
  54. // 第一次进入界面时,布局后frame,裁剪dimiss动画使用
  55. var originalFrame: CGRect = .zero
  56. // 图片可编辑rect
  57. var editRect: CGRect
  58. let tools: [ZLEditImageViewController.EditImageTool]
  59. var selectRatio: ZLImageClipRatio?
  60. var editImage: UIImage
  61. var cancelBtn: UIButton!
  62. var scrollView: UIScrollView!
  63. var containerView: UIView!
  64. // Show image.
  65. var imageView: UIImageView!
  66. // Show draw lines.
  67. var drawingImageView: UIImageView!
  68. // Show text and image stickers.
  69. var stickersContainer: UIView!
  70. // 处理好的马赛克图片
  71. var mosaicImage: UIImage?
  72. // 显示马赛克图片的layer
  73. var mosaicImageLayer: CALayer?
  74. // 显示马赛克图片的layer的mask
  75. var mosaicImageLayerMaskLayer: CAShapeLayer?
  76. // 上方渐变阴影层
  77. var topShadowView: UIView!
  78. var topShadowLayer: CAGradientLayer!
  79. // 下方渐变阴影层
  80. var bottomShadowView: UIView!
  81. var bottomShadowLayer: CAGradientLayer!
  82. var doneBtn: UIButton!
  83. var revokeBtn: UIButton!
  84. var selectedTool: ZLEditImageViewController.EditImageTool?
  85. var editToolCollectionView: UICollectionView!
  86. var drawColorCollectionView: UICollectionView!
  87. var filterCollectionView: UICollectionView!
  88. var ashbinView: UIView!
  89. var ashbinImgView: UIImageView!
  90. let drawColors: [UIColor]
  91. var currentDrawColor = ZLPhotoConfiguration.default().editImageDefaultDrawColor
  92. var drawPaths: [ZLDrawPath]
  93. var drawLineWidth: CGFloat = 5
  94. var mosaicPaths: [ZLMosaicPath]
  95. var mosaicLineWidth: CGFloat = 25
  96. // collectionview 中的添加滤镜的小图
  97. var thumbnailFilterImages: [UIImage] = []
  98. // 选择滤镜后对原图添加滤镜后的图片
  99. var filterImages: [String: UIImage] = [:]
  100. var currentFilter: ZLFilter
  101. var stickers: [UIView] = []
  102. var isScrolling = false
  103. var shouldLayout = true
  104. var imageStickerContainerIsHidden = true
  105. var angle: CGFloat
  106. var panGes: UIPanGestureRecognizer!
  107. var imageSize: CGSize {
  108. if self.angle == -90 || self.angle == -270 {
  109. return CGSize(width: self.originalImage.size.height, height: self.originalImage.size.width)
  110. }
  111. return self.originalImage.size
  112. }
  113. @objc public var editFinishBlock: ( (UIImage, ZLEditImageModel?) -> Void )?
  114. public override var prefersStatusBarHidden: Bool {
  115. return true
  116. }
  117. public override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
  118. return .portrait
  119. }
  120. deinit {
  121. zl_debugPrint("ZLEditImageViewController deinit")
  122. }
  123. @objc public class func showEditImageVC(parentVC: UIViewController?, animate: Bool = false, image: UIImage, editModel: ZLEditImageModel? = nil, completion: ( (UIImage, ZLEditImageModel?) -> Void )? ) {
  124. let tools = ZLPhotoConfiguration.default().editImageTools
  125. if ZLPhotoConfiguration.default().showClipDirectlyIfOnlyHasClipTool, tools.count == 1, tools.contains(.clip) {
  126. let vc = ZLClipImageViewController(image: image, editRect: editModel?.editRect, angle: editModel?.angle ?? 0, selectRatio: editModel?.selectRatio)
  127. vc.clipDoneBlock = { (angle, editRect, ratio) in
  128. let m = ZLEditImageModel(drawPaths: [], mosaicPaths: [], editRect: editRect, angle: angle, selectRatio: ratio, selectFilter: .normal, textStickers: nil, imageStickers: nil)
  129. completion?(image.clipImage(angle, editRect) ?? image, m)
  130. }
  131. vc.animate = animate
  132. vc.modalPresentationStyle = .fullScreen
  133. parentVC?.present(vc, animated: animate, completion: nil)
  134. } else {
  135. let vc = ZLEditImageViewController(image: image, editModel: editModel)
  136. vc.editFinishBlock = { (ei, editImageModel) in
  137. completion?(ei, editImageModel)
  138. }
  139. vc.animate = animate
  140. vc.modalPresentationStyle = .fullScreen
  141. parentVC?.present(vc, animated: animate, completion: nil)
  142. }
  143. }
  144. @objc public init(image: UIImage, editModel: ZLEditImageModel? = nil) {
  145. self.originalImage = image
  146. self.editImage = image
  147. self.editRect = editModel?.editRect ?? CGRect(origin: .zero, size: image.size)
  148. self.drawColors = ZLPhotoConfiguration.default().editImageDrawColors
  149. self.currentFilter = editModel?.selectFilter ?? .normal
  150. self.drawPaths = editModel?.drawPaths ?? []
  151. self.mosaicPaths = editModel?.mosaicPaths ?? []
  152. self.angle = editModel?.angle ?? 0
  153. self.selectRatio = editModel?.selectRatio
  154. var ts = ZLPhotoConfiguration.default().editImageTools
  155. if ts.contains(.imageSticker), ZLPhotoConfiguration.default().imageStickerContainerView == nil {
  156. ts.removeAll { $0 == .imageSticker }
  157. }
  158. self.tools = ts
  159. super.init(nibName: nil, bundle: nil)
  160. if !self.drawColors.contains(self.currentDrawColor) {
  161. self.currentDrawColor = self.drawColors.first!
  162. }
  163. let teStic = editModel?.textStickers ?? []
  164. let imStic = editModel?.imageStickers ?? []
  165. var stickers: [UIView?] = Array(repeating: nil, count: teStic.count + imStic.count)
  166. teStic.forEach { (cache) in
  167. let v = ZLTextStickerView(from: cache.state)
  168. stickers[cache.index] = v
  169. }
  170. imStic.forEach { (cache) in
  171. let v = ZLImageStickerView(from: cache.state)
  172. stickers[cache.index] = v
  173. }
  174. self.stickers = stickers.compactMap { $0 }
  175. }
  176. required init?(coder: NSCoder) {
  177. fatalError("init(coder:) has not been implemented")
  178. }
  179. public override func viewDidLoad() {
  180. super.viewDidLoad()
  181. self.setupUI()
  182. self.rotationImageView()
  183. if self.tools.contains(.filter) {
  184. self.generateFilterImages()
  185. }
  186. }
  187. public override func viewDidLayoutSubviews() {
  188. super.viewDidLayoutSubviews()
  189. guard self.shouldLayout else {
  190. return
  191. }
  192. self.shouldLayout = false
  193. zl_debugPrint("edit image layout subviews")
  194. var insets = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
  195. if #available(iOS 11.0, *) {
  196. insets = self.view.safeAreaInsets
  197. }
  198. insets.top = max(20, insets.top)
  199. self.scrollView.frame = self.view.bounds
  200. self.resetContainerViewFrame()
  201. self.topShadowView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 150)
  202. self.topShadowLayer.frame = self.topShadowView.bounds
  203. self.cancelBtn.frame = CGRect(x: 30, y: insets.top+10, width: 28, height: 28)
  204. self.bottomShadowView.frame = CGRect(x: 0, y: self.view.frame.height-140-insets.bottom, width: self.view.frame.width, height: 140+insets.bottom)
  205. self.bottomShadowLayer.frame = self.bottomShadowView.bounds
  206. self.drawColorCollectionView.frame = CGRect(x: 20, y: 20, width: self.view.frame.width - 80, height: 50)
  207. self.revokeBtn.frame = CGRect(x: self.view.frame.width - 15 - 35, y: 30, width: 35, height: 30)
  208. self.filterCollectionView.frame = CGRect(x: 20, y: 0, width: self.view.frame.width-40, height: ZLEditImageViewController.filterColViewH)
  209. let toolY: CGFloat = 85
  210. let doneBtnH = ZLLayout.bottomToolBtnH
  211. let doneBtnW = localLanguageTextValue(.editFinish).boundingRect(font: ZLLayout.bottomToolTitleFont, limitSize: CGSize(width: CGFloat.greatestFiniteMagnitude, height: doneBtnH)).width + 20
  212. self.doneBtn.frame = CGRect(x: self.view.frame.width-20-doneBtnW, y: toolY-2, width: doneBtnW, height: doneBtnH)
  213. self.editToolCollectionView.frame = CGRect(x: 20, y: toolY, width: self.view.bounds.width - 20 - 20 - doneBtnW - 20, height: 30)
  214. if !self.drawPaths.isEmpty {
  215. self.drawLine()
  216. }
  217. if !self.mosaicPaths.isEmpty {
  218. self.generateNewMosaicImage()
  219. }
  220. if let index = self.drawColors.firstIndex(where: { $0 == self.currentDrawColor}) {
  221. self.drawColorCollectionView.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: false)
  222. }
  223. }
  224. func generateFilterImages() {
  225. let size: CGSize
  226. let ratio = (self.originalImage.size.width / self.originalImage.size.height)
  227. let fixLength: CGFloat = 200
  228. if ratio >= 1 {
  229. size = CGSize(width: fixLength * ratio, height: fixLength)
  230. } else {
  231. size = CGSize(width: fixLength, height: fixLength / ratio)
  232. }
  233. let thumbnailImage = self.originalImage.resize(size) ?? self.originalImage
  234. DispatchQueue.global().async {
  235. self.thumbnailFilterImages = ZLPhotoConfiguration.default().filters.map { $0.applier?(thumbnailImage) ?? thumbnailImage }
  236. DispatchQueue.main.async {
  237. self.filterCollectionView.reloadData()
  238. self.filterCollectionView.performBatchUpdates {
  239. } completion: { (_) in
  240. if let index = ZLPhotoConfiguration.default().filters.firstIndex(where: { $0 == self.currentFilter }) {
  241. self.filterCollectionView.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: false)
  242. }
  243. }
  244. }
  245. }
  246. }
  247. func resetContainerViewFrame() {
  248. self.scrollView.setZoomScale(1, animated: true)
  249. self.imageView.image = self.editImage
  250. let editSize = self.editRect.size
  251. let scrollViewSize = self.scrollView.frame.size
  252. let ratio = min(scrollViewSize.width / editSize.width, scrollViewSize.height / editSize.height)
  253. let w = ratio * editSize.width * self.scrollView.zoomScale
  254. let h = ratio * editSize.height * self.scrollView.zoomScale
  255. self.containerView.frame = CGRect(x: max(0, (scrollViewSize.width-w)/2), y: max(0, (scrollViewSize.height-h)/2), width: w, height: h)
  256. let scaleImageOrigin = CGPoint(x: -self.editRect.origin.x*ratio, y: -self.editRect.origin.y*ratio)
  257. let scaleImageSize = CGSize(width: self.imageSize.width * ratio, height: self.imageSize.height * ratio)
  258. self.imageView.frame = CGRect(origin: scaleImageOrigin, size: scaleImageSize)
  259. self.mosaicImageLayer?.frame = self.imageView.bounds
  260. self.mosaicImageLayerMaskLayer?.frame = self.imageView.bounds
  261. self.drawingImageView.frame = self.imageView.frame
  262. self.stickersContainer.frame = self.imageView.frame
  263. // 针对于长图的优化
  264. if (self.editRect.height / self.editRect.width) > (self.view.frame.height / self.view.frame.width * 1.1) {
  265. let widthScale = self.view.frame.width / w
  266. self.scrollView.maximumZoomScale = widthScale
  267. self.scrollView.zoomScale = widthScale
  268. self.scrollView.contentOffset = .zero
  269. } else if self.editRect.width / self.editRect.height > 1 {
  270. self.scrollView.maximumZoomScale = max(3, self.view.frame.height / h)
  271. }
  272. self.originalFrame = self.view.convert(self.containerView.frame, from: self.scrollView)
  273. self.isScrolling = false
  274. }
  275. func setupUI() {
  276. self.view.backgroundColor = .black
  277. self.scrollView = UIScrollView()
  278. self.scrollView.backgroundColor = .black
  279. self.scrollView.minimumZoomScale = 1
  280. self.scrollView.maximumZoomScale = 3
  281. self.scrollView.delegate = self
  282. self.view.addSubview(self.scrollView)
  283. self.containerView = UIView()
  284. self.containerView.clipsToBounds = true
  285. self.scrollView.addSubview(self.containerView)
  286. self.imageView = UIImageView(image: self.originalImage)
  287. self.imageView.contentMode = .scaleAspectFit
  288. self.imageView.clipsToBounds = true
  289. self.imageView.backgroundColor = .black
  290. self.containerView.addSubview(self.imageView)
  291. self.drawingImageView = UIImageView()
  292. self.drawingImageView.contentMode = .scaleAspectFit
  293. self.drawingImageView.isUserInteractionEnabled = true
  294. self.containerView.addSubview(self.drawingImageView)
  295. self.stickersContainer = UIView()
  296. self.containerView.addSubview(self.stickersContainer)
  297. let color1 = UIColor.black.withAlphaComponent(0.35).cgColor
  298. let color2 = UIColor.black.withAlphaComponent(0).cgColor
  299. self.topShadowView = UIView()
  300. self.view.addSubview(self.topShadowView)
  301. self.topShadowLayer = CAGradientLayer()
  302. self.topShadowLayer.colors = [color1, color2]
  303. self.topShadowLayer.locations = [0, 1]
  304. self.topShadowView.layer.addSublayer(self.topShadowLayer)
  305. self.cancelBtn = UIButton(type: .custom)
  306. self.cancelBtn.setImage(getImage("zl_retake"), for: .normal)
  307. self.cancelBtn.addTarget(self, action: #selector(cancelBtnClick), for: .touchUpInside)
  308. self.cancelBtn.adjustsImageWhenHighlighted = false
  309. self.cancelBtn.zl_enlargeValidTouchArea(inset: 30)
  310. self.topShadowView.addSubview(self.cancelBtn)
  311. self.bottomShadowView = UIView()
  312. self.view.addSubview(self.bottomShadowView)
  313. self.bottomShadowLayer = CAGradientLayer()
  314. self.bottomShadowLayer.colors = [color2, color1]
  315. self.bottomShadowLayer.locations = [0, 1]
  316. self.bottomShadowView.layer.addSublayer(self.bottomShadowLayer)
  317. let editToolLayout = UICollectionViewFlowLayout()
  318. editToolLayout.itemSize = CGSize(width: 30, height: 30)
  319. editToolLayout.minimumLineSpacing = 20
  320. editToolLayout.minimumInteritemSpacing = 20
  321. editToolLayout.scrollDirection = .horizontal
  322. self.editToolCollectionView = UICollectionView(frame: .zero, collectionViewLayout: editToolLayout)
  323. self.editToolCollectionView.backgroundColor = .clear
  324. self.editToolCollectionView.delegate = self
  325. self.editToolCollectionView.dataSource = self
  326. self.editToolCollectionView.showsHorizontalScrollIndicator = false
  327. self.bottomShadowView.addSubview(self.editToolCollectionView)
  328. ZLEditToolCell.zl_register(self.editToolCollectionView)
  329. self.doneBtn = UIButton(type: .custom)
  330. self.doneBtn.titleLabel?.font = ZLLayout.bottomToolTitleFont
  331. self.doneBtn.backgroundColor = .bottomToolViewBtnNormalBgColor
  332. self.doneBtn.setTitle(localLanguageTextValue(.editFinish), for: .normal)
  333. self.doneBtn.addTarget(self, action: #selector(doneBtnClick), for: .touchUpInside)
  334. self.doneBtn.layer.masksToBounds = true
  335. self.doneBtn.layer.cornerRadius = ZLLayout.bottomToolBtnCornerRadius
  336. self.bottomShadowView.addSubview(self.doneBtn)
  337. let drawColorLayout = UICollectionViewFlowLayout()
  338. drawColorLayout.itemSize = CGSize(width: 30, height: 30)
  339. drawColorLayout.minimumLineSpacing = 15
  340. drawColorLayout.minimumInteritemSpacing = 15
  341. drawColorLayout.scrollDirection = .horizontal
  342. drawColorLayout.sectionInset = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)
  343. self.drawColorCollectionView = UICollectionView(frame: .zero, collectionViewLayout: drawColorLayout)
  344. self.drawColorCollectionView.backgroundColor = .clear
  345. self.drawColorCollectionView.delegate = self
  346. self.drawColorCollectionView.dataSource = self
  347. self.drawColorCollectionView.isHidden = true
  348. self.drawColorCollectionView.showsHorizontalScrollIndicator = false
  349. self.bottomShadowView.addSubview(self.drawColorCollectionView)
  350. ZLDrawColorCell.zl_register(self.drawColorCollectionView)
  351. let filterLayout = UICollectionViewFlowLayout()
  352. filterLayout.itemSize = CGSize(width: ZLEditImageViewController.filterColViewH-20, height: ZLEditImageViewController.filterColViewH)
  353. filterLayout.minimumLineSpacing = 15
  354. filterLayout.minimumInteritemSpacing = 15
  355. filterLayout.scrollDirection = .horizontal
  356. self.filterCollectionView = UICollectionView(frame: .zero, collectionViewLayout: filterLayout)
  357. self.filterCollectionView.backgroundColor = .clear
  358. self.filterCollectionView.delegate = self
  359. self.filterCollectionView.dataSource = self
  360. self.filterCollectionView.isHidden = true
  361. self.filterCollectionView.showsHorizontalScrollIndicator = false
  362. self.bottomShadowView.addSubview(self.filterCollectionView)
  363. ZLFilterImageCell.zl_register(self.filterCollectionView)
  364. self.revokeBtn = UIButton(type: .custom)
  365. self.revokeBtn.setImage(getImage("zl_revoke_disable"), for: .disabled)
  366. self.revokeBtn.setImage(getImage("zl_revoke"), for: .normal)
  367. self.revokeBtn.adjustsImageWhenHighlighted = false
  368. self.revokeBtn.isEnabled = false
  369. self.revokeBtn.isHidden = true
  370. self.revokeBtn.addTarget(self, action: #selector(revokeBtnClick), for: .touchUpInside)
  371. self.bottomShadowView.addSubview(self.revokeBtn)
  372. let ashbinSize = CGSize(width: 160, height: 80)
  373. self.ashbinView = UIView(frame: CGRect(x: (self.view.frame.width-ashbinSize.width)/2, y: self.view.frame.height-ashbinSize.height-40, width: ashbinSize.width, height: ashbinSize.height))
  374. self.ashbinView.backgroundColor = ZLEditImageViewController.ashbinNormalBgColor
  375. self.ashbinView.layer.cornerRadius = 15
  376. self.ashbinView.layer.masksToBounds = true
  377. self.ashbinView.isHidden = true
  378. self.view.addSubview(self.ashbinView)
  379. self.ashbinImgView = UIImageView(image: getImage("zl_ashbin"), highlightedImage: getImage("zl_ashbin_open"))
  380. self.ashbinImgView.frame = CGRect(x: (ashbinSize.width-25)/2, y: 15, width: 25, height: 25)
  381. self.ashbinView.addSubview(self.ashbinImgView)
  382. let asbinTipLabel = UILabel(frame: CGRect(x: 0, y: ashbinSize.height-34, width: ashbinSize.width, height: 34))
  383. asbinTipLabel.font = getFont(12)
  384. asbinTipLabel.textAlignment = .center
  385. asbinTipLabel.textColor = .white
  386. asbinTipLabel.text = localLanguageTextValue(.textStickerRemoveTips)
  387. asbinTipLabel.numberOfLines = 2
  388. asbinTipLabel.lineBreakMode = .byCharWrapping
  389. self.ashbinView.addSubview(asbinTipLabel)
  390. if self.tools.contains(.mosaic) {
  391. // 之前选择过滤镜
  392. if let applier = self.currentFilter.applier {
  393. let image = applier(self.originalImage)
  394. self.editImage = image
  395. self.filterImages[self.currentFilter.name] = image
  396. self.mosaicImage = self.editImage.mosaicImage()
  397. } else {
  398. self.mosaicImage = self.originalImage.mosaicImage()
  399. }
  400. self.mosaicImageLayer = CALayer()
  401. self.mosaicImageLayer?.contents = self.mosaicImage?.cgImage
  402. self.imageView.layer.addSublayer(self.mosaicImageLayer!)
  403. self.mosaicImageLayerMaskLayer = CAShapeLayer()
  404. self.mosaicImageLayerMaskLayer?.strokeColor = UIColor.blue.cgColor
  405. self.mosaicImageLayerMaskLayer?.fillColor = nil
  406. self.mosaicImageLayerMaskLayer?.lineCap = .round
  407. self.mosaicImageLayerMaskLayer?.lineJoin = .round
  408. self.imageView.layer.addSublayer(self.mosaicImageLayerMaskLayer!)
  409. self.mosaicImageLayer?.mask = self.mosaicImageLayerMaskLayer
  410. }
  411. if self.tools.contains(.imageSticker) {
  412. ZLPhotoConfiguration.default().imageStickerContainerView?.hideBlock = { [weak self] in
  413. self?.setToolView(show: true)
  414. self?.imageStickerContainerIsHidden = true
  415. }
  416. ZLPhotoConfiguration.default().imageStickerContainerView?.selectImageBlock = { [weak self] (image) in
  417. self?.addImageStickerView(image)
  418. }
  419. }
  420. let tapGes = UITapGestureRecognizer(target: self, action: #selector(tapAction(_:)))
  421. tapGes.delegate = self
  422. self.view.addGestureRecognizer(tapGes)
  423. self.panGes = UIPanGestureRecognizer(target: self, action: #selector(drawAction(_:)))
  424. self.panGes.maximumNumberOfTouches = 1
  425. self.panGes.delegate = self
  426. self.view.addGestureRecognizer(self.panGes)
  427. self.scrollView.panGestureRecognizer.require(toFail: self.panGes)
  428. self.stickers.forEach { (view) in
  429. self.stickersContainer.addSubview(view)
  430. if let tv = view as? ZLTextStickerView {
  431. tv.frame = tv.originFrame
  432. self.configTextSticker(tv)
  433. } else if let iv = view as? ZLImageStickerView {
  434. iv.frame = iv.originFrame
  435. self.configImageSticker(iv)
  436. }
  437. }
  438. }
  439. func rotationImageView() {
  440. let transform = CGAffineTransform(rotationAngle: self.angle.toPi)
  441. self.imageView.transform = transform
  442. self.drawingImageView.transform = transform
  443. self.stickersContainer.transform = transform
  444. }
  445. @objc func cancelBtnClick() {
  446. self.dismiss(animated: self.animate, completion: nil)
  447. }
  448. func drawBtnClick() {
  449. let isSelected = self.selectedTool != .draw
  450. if isSelected {
  451. self.selectedTool = .draw
  452. } else {
  453. self.selectedTool = nil
  454. }
  455. self.drawColorCollectionView.isHidden = !isSelected
  456. self.revokeBtn.isHidden = !isSelected
  457. self.revokeBtn.isEnabled = self.drawPaths.count > 0
  458. self.filterCollectionView.isHidden = true
  459. }
  460. func clipBtnClick() {
  461. let currentEditImage = self.buildImage()
  462. let vc = ZLClipImageViewController(image: currentEditImage, editRect: self.editRect, angle: self.angle, selectRatio: self.selectRatio)
  463. let rect = self.scrollView.convert(self.containerView.frame, to: self.view)
  464. vc.presentAnimateFrame = rect
  465. vc.presentAnimateImage = currentEditImage.clipImage(self.angle, self.editRect)
  466. vc.modalPresentationStyle = .fullScreen
  467. vc.clipDoneBlock = { [weak self] (angle, editFrame, selectRatio) in
  468. guard let `self` = self else { return }
  469. let oldAngle = self.angle
  470. let oldContainerSize = self.stickersContainer.frame.size
  471. if self.angle != angle {
  472. self.angle = angle
  473. self.rotationImageView()
  474. }
  475. self.editRect = editFrame
  476. self.selectRatio = selectRatio
  477. self.resetContainerViewFrame()
  478. self.reCalculateStickersFrame(oldContainerSize, oldAngle, angle)
  479. }
  480. vc.cancelClipBlock = { [weak self] () in
  481. self?.resetContainerViewFrame()
  482. }
  483. self.present(vc, animated: false) {
  484. self.scrollView.alpha = 0
  485. self.topShadowView.alpha = 0
  486. self.bottomShadowView.alpha = 0
  487. }
  488. }
  489. func imageStickerBtnClick() {
  490. ZLPhotoConfiguration.default().imageStickerContainerView?.show(in: self.view)
  491. self.setToolView(show: false)
  492. self.imageStickerContainerIsHidden = false
  493. }
  494. func textStickerBtnClick() {
  495. self.showInputTextVC { [weak self] (text, textColor, bgColor) in
  496. self?.addTextStickersView(text, textColor: textColor, bgColor: bgColor)
  497. }
  498. }
  499. func mosaicBtnClick() {
  500. let isSelected = self.selectedTool != .mosaic
  501. if isSelected {
  502. self.selectedTool = .mosaic
  503. } else {
  504. self.selectedTool = nil
  505. }
  506. self.drawColorCollectionView.isHidden = true
  507. self.filterCollectionView.isHidden = true
  508. self.revokeBtn.isHidden = !isSelected
  509. self.revokeBtn.isEnabled = self.mosaicPaths.count > 0
  510. }
  511. func filterBtnClick() {
  512. let isSelected = self.selectedTool != .filter
  513. if isSelected {
  514. self.selectedTool = .filter
  515. } else {
  516. self.selectedTool = nil
  517. }
  518. self.drawColorCollectionView.isHidden = true
  519. self.revokeBtn.isHidden = true
  520. self.filterCollectionView.isHidden = !isSelected
  521. }
  522. @objc func doneBtnClick() {
  523. var textStickers: [(ZLTextStickerState, Int)] = []
  524. var imageStickers: [(ZLImageStickerState, Int)] = []
  525. for (index, view) in self.stickersContainer.subviews.enumerated() {
  526. if let ts = view as? ZLTextStickerView, let _ = ts.label.text {
  527. textStickers.append((ts.state, index))
  528. } else if let ts = view as? ZLImageStickerView {
  529. imageStickers.append((ts.state, index))
  530. }
  531. }
  532. var hasEdit = true
  533. if self.drawPaths.isEmpty, self.editRect.size == self.imageSize, self.angle == 0, self.mosaicPaths.isEmpty, imageStickers.isEmpty, textStickers.isEmpty, self.currentFilter.applier == nil {
  534. hasEdit = false
  535. }
  536. var resImage = self.originalImage
  537. var editModel: ZLEditImageModel? = nil
  538. if hasEdit {
  539. resImage = self.buildImage()
  540. resImage = resImage.clipImage(self.angle, self.editRect) ?? resImage
  541. editModel = ZLEditImageModel(drawPaths: self.drawPaths, mosaicPaths: self.mosaicPaths, editRect: self.editRect, angle: self.angle, selectRatio: self.selectRatio, selectFilter: self.currentFilter, textStickers: textStickers, imageStickers: imageStickers)
  542. }
  543. self.editFinishBlock?(resImage, editModel)
  544. self.dismiss(animated: self.animate, completion: nil)
  545. }
  546. @objc func revokeBtnClick() {
  547. if self.selectedTool == .draw {
  548. guard !self.drawPaths.isEmpty else {
  549. return
  550. }
  551. self.drawPaths.removeLast()
  552. self.revokeBtn.isEnabled = self.drawPaths.count > 0
  553. self.drawLine()
  554. } else if self.selectedTool == .mosaic {
  555. guard !self.mosaicPaths.isEmpty else {
  556. return
  557. }
  558. self.mosaicPaths.removeLast()
  559. self.revokeBtn.isEnabled = self.mosaicPaths.count > 0
  560. self.generateNewMosaicImage()
  561. }
  562. }
  563. @objc func tapAction(_ tap: UITapGestureRecognizer) {
  564. if self.bottomShadowView.alpha == 1 {
  565. self.setToolView(show: false)
  566. } else {
  567. self.setToolView(show: true)
  568. }
  569. }
  570. @objc func drawAction(_ pan: UIPanGestureRecognizer) {
  571. if self.selectedTool == .draw {
  572. let point = pan.location(in: self.drawingImageView)
  573. if pan.state == .began {
  574. self.setToolView(show: false)
  575. let originalRatio = min(self.scrollView.frame.width / self.originalImage.size.width, self.scrollView.frame.height / self.originalImage.size.height)
  576. let ratio = min(self.scrollView.frame.width / self.editRect.width, self.scrollView.frame.height / self.editRect.height)
  577. let scale = ratio / originalRatio
  578. // 缩放到最初的size
  579. var size = self.drawingImageView.frame.size
  580. size.width /= scale
  581. size.height /= scale
  582. if self.angle == -90 || self.angle == -270 {
  583. swap(&size.width, &size.height)
  584. }
  585. var toImageScale = ZLEditImageViewController.maxDrawLineImageWidth / size.width
  586. if self.editImage.size.width / self.editImage.size.height > 1 {
  587. toImageScale = ZLEditImageViewController.maxDrawLineImageWidth / size.height
  588. }
  589. let path = ZLDrawPath(pathColor: self.currentDrawColor, pathWidth: self.drawLineWidth / self.scrollView.zoomScale, ratio: ratio / originalRatio / toImageScale, startPoint: point)
  590. self.drawPaths.append(path)
  591. } else if pan.state == .changed {
  592. let path = self.drawPaths.last
  593. path?.addLine(to: point)
  594. self.drawLine()
  595. } else if pan.state == .cancelled || pan.state == .ended {
  596. self.setToolView(show: true)
  597. self.revokeBtn.isEnabled = self.drawPaths.count > 0
  598. }
  599. } else if self.selectedTool == .mosaic {
  600. let point = pan.location(in: self.imageView)
  601. if pan.state == .began {
  602. self.setToolView(show: false)
  603. var actualSize = self.editRect.size
  604. if self.angle == -90 || self.angle == -270 {
  605. swap(&actualSize.width, &actualSize.height)
  606. }
  607. let ratio = min(self.scrollView.frame.width / self.editRect.width, self.scrollView.frame.height / self.editRect.height)
  608. let pathW = self.mosaicLineWidth / self.scrollView.zoomScale
  609. let path = ZLMosaicPath(pathWidth: pathW, ratio: ratio, startPoint: point)
  610. self.mosaicImageLayerMaskLayer?.lineWidth = pathW
  611. self.mosaicImageLayerMaskLayer?.path = path.path.cgPath
  612. self.mosaicPaths.append(path)
  613. } else if pan.state == .changed {
  614. let path = self.mosaicPaths.last
  615. path?.addLine(to: point)
  616. self.mosaicImageLayerMaskLayer?.path = path?.path.cgPath
  617. } else if pan.state == .cancelled || pan.state == .ended {
  618. self.setToolView(show: true)
  619. self.revokeBtn.isEnabled = self.mosaicPaths.count > 0
  620. self.generateNewMosaicImage()
  621. }
  622. }
  623. }
  624. func setToolView(show: Bool) {
  625. self.topShadowView.layer.removeAllAnimations()
  626. self.bottomShadowView.layer.removeAllAnimations()
  627. if show {
  628. UIView.animate(withDuration: 0.25) {
  629. self.topShadowView.alpha = 1
  630. self.bottomShadowView.alpha = 1
  631. }
  632. } else {
  633. UIView.animate(withDuration: 0.25) {
  634. self.topShadowView.alpha = 0
  635. self.bottomShadowView.alpha = 0
  636. }
  637. }
  638. }
  639. func showInputTextVC(_ text: String? = nil, textColor: UIColor? = nil, bgColor: UIColor? = nil, completion: @escaping ( (String, UIColor, UIColor) -> Void )) {
  640. // Calculate image displayed frame on the screen.
  641. var r = self.scrollView.convert(self.view.frame, to: self.containerView)
  642. r.origin.x += self.scrollView.contentOffset.x / self.scrollView.zoomScale
  643. r.origin.y += self.scrollView.contentOffset.y / self.scrollView.zoomScale
  644. let scale = self.imageSize.width / self.imageView.frame.width
  645. r.origin.x *= scale
  646. r.origin.y *= scale
  647. r.size.width *= scale
  648. r.size.height *= scale
  649. let bgImage = self.buildImage().clipImage(self.angle, self.editRect)?.clipImage(0, r)
  650. let vc = ZLInputTextViewController(image: bgImage, text: text, textColor: textColor, bgColor: bgColor)
  651. vc.endInput = { (text, textColor, bgColor) in
  652. completion(text, textColor, bgColor)
  653. }
  654. vc.modalPresentationStyle = .fullScreen
  655. self.showDetailViewController(vc, sender: nil)
  656. }
  657. func getStickerOriginFrame(_ size: CGSize) -> CGRect {
  658. let scale = self.scrollView.zoomScale
  659. // Calculate the display rect of container view.
  660. let x = (self.scrollView.contentOffset.x - self.containerView.frame.minX) / scale
  661. let y = (self.scrollView.contentOffset.y - self.containerView.frame.minY) / scale
  662. let w = view.frame.width / scale
  663. let h = view.frame.height / scale
  664. // Convert to text stickers container view.
  665. let r = self.containerView.convert(CGRect(x: x, y: y, width: w, height: h), to: self.stickersContainer)
  666. let originFrame = CGRect(x: r.minX + (r.width - size.width) / 2, y: r.minY + (r.height - size.height) / 2, width: size.width, height: size.height)
  667. return originFrame
  668. }
  669. /// Add image sticker
  670. func addImageStickerView(_ image: UIImage) {
  671. let scale = self.scrollView.zoomScale
  672. let size = ZLImageStickerView.calculateSize(image: image, width: self.view.frame.width)
  673. let originFrame = self.getStickerOriginFrame(size)
  674. let imageSticker = ZLImageStickerView(image: image, originScale: 1 / scale, originAngle: -self.angle, originFrame: originFrame)
  675. self.stickersContainer.addSubview(imageSticker)
  676. imageSticker.frame = originFrame
  677. self.view.layoutIfNeeded()
  678. self.configImageSticker(imageSticker)
  679. }
  680. /// Add text sticker
  681. func addTextStickersView(_ text: String, textColor: UIColor, bgColor: UIColor) {
  682. guard !text.isEmpty else { return }
  683. let scale = self.scrollView.zoomScale
  684. let size = ZLTextStickerView.calculateSize(text: text, width: self.view.frame.width)
  685. let originFrame = self.getStickerOriginFrame(size)
  686. let textSticker = ZLTextStickerView(text: text, textColor: textColor, bgColor: bgColor, originScale: 1 / scale, originAngle: -self.angle, originFrame: originFrame)
  687. self.stickersContainer.addSubview(textSticker)
  688. textSticker.frame = originFrame
  689. self.configTextSticker(textSticker)
  690. }
  691. func configTextSticker(_ textSticker: ZLTextStickerView) {
  692. textSticker.delegate = self
  693. self.scrollView.pinchGestureRecognizer?.require(toFail: textSticker.pinchGes)
  694. self.scrollView.panGestureRecognizer.require(toFail: textSticker.panGes)
  695. self.panGes.require(toFail: textSticker.panGes)
  696. }
  697. func configImageSticker(_ imageSticker: ZLImageStickerView) {
  698. imageSticker.delegate = self
  699. self.scrollView.pinchGestureRecognizer?.require(toFail: imageSticker.pinchGes)
  700. self.scrollView.panGestureRecognizer.require(toFail: imageSticker.panGes)
  701. self.panGes.require(toFail: imageSticker.panGes)
  702. }
  703. func reCalculateStickersFrame(_ oldSize: CGSize, _ oldAngle: CGFloat, _ newAngle: CGFloat) {
  704. let currSize = self.stickersContainer.frame.size
  705. let scale: CGFloat
  706. if Int(newAngle - oldAngle) % 180 == 0{
  707. scale = currSize.width / oldSize.width
  708. } else {
  709. scale = currSize.height / oldSize.width
  710. }
  711. self.stickersContainer.subviews.forEach { (view) in
  712. (view as? ZLStickerViewAdditional)?.addScale(scale)
  713. }
  714. }
  715. func drawLine() {
  716. let originalRatio = min(self.scrollView.frame.width / self.originalImage.size.width, self.scrollView.frame.height / self.originalImage.size.height)
  717. let ratio = min(self.scrollView.frame.width / self.editRect.width, self.scrollView.frame.height / self.editRect.height)
  718. let scale = ratio / originalRatio
  719. // 缩放到最初的size
  720. var size = self.drawingImageView.frame.size
  721. size.width /= scale
  722. size.height /= scale
  723. if self.angle == -90 || self.angle == -270 {
  724. swap(&size.width, &size.height)
  725. }
  726. var toImageScale = ZLEditImageViewController.maxDrawLineImageWidth / size.width
  727. if self.editImage.size.width / self.editImage.size.height > 1 {
  728. toImageScale = ZLEditImageViewController.maxDrawLineImageWidth / size.height
  729. }
  730. size.width *= toImageScale
  731. size.height *= toImageScale
  732. UIGraphicsBeginImageContextWithOptions(size, false, self.editImage.scale)
  733. let context = UIGraphicsGetCurrentContext()
  734. // 去掉锯齿
  735. context?.setAllowsAntialiasing(true)
  736. context?.setShouldAntialias(true)
  737. for path in self.drawPaths {
  738. path.drawPath()
  739. }
  740. self.drawingImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  741. UIGraphicsEndImageContext()
  742. }
  743. func generateNewMosaicImage() {
  744. UIGraphicsBeginImageContextWithOptions(self.originalImage.size, false, self.originalImage.scale)
  745. if self.tools.contains(.filter), let image = self.filterImages[self.currentFilter.name] {
  746. image.draw(at: .zero)
  747. } else {
  748. self.originalImage.draw(at: .zero)
  749. }
  750. let context = UIGraphicsGetCurrentContext()
  751. self.mosaicPaths.forEach { (path) in
  752. context?.move(to: path.startPoint)
  753. path.linePoints.forEach { (point) in
  754. context?.addLine(to: point)
  755. }
  756. context?.setLineWidth(path.path.lineWidth / path.ratio)
  757. context?.setLineCap(.round)
  758. context?.setLineJoin(.round)
  759. context?.setBlendMode(.clear)
  760. context?.strokePath()
  761. }
  762. var midImage = UIGraphicsGetImageFromCurrentImageContext()
  763. UIGraphicsEndImageContext()
  764. guard let midCgImage = midImage?.cgImage else {
  765. return
  766. }
  767. midImage = UIImage(cgImage: midCgImage, scale: self.editImage.scale, orientation: .up)
  768. UIGraphicsBeginImageContextWithOptions(self.originalImage.size, false, self.originalImage.scale)
  769. self.mosaicImage?.draw(at: .zero)
  770. midImage?.draw(at: .zero)
  771. let temp = UIGraphicsGetImageFromCurrentImageContext()
  772. UIGraphicsEndImageContext()
  773. guard let cgi = temp?.cgImage else {
  774. return
  775. }
  776. let image = UIImage(cgImage: cgi, scale: self.editImage.scale, orientation: .up)
  777. self.editImage = image
  778. self.imageView.image = self.editImage
  779. self.mosaicImageLayerMaskLayer?.path = nil
  780. }
  781. func buildImage() -> UIImage {
  782. let imageSize = self.originalImage.size
  783. UIGraphicsBeginImageContextWithOptions(self.editImage.size, false, self.editImage.scale)
  784. self.editImage.draw(at: .zero)
  785. self.drawingImageView.image?.draw(in: CGRect(origin: .zero, size: imageSize))
  786. if !self.stickersContainer.subviews.isEmpty, let context = UIGraphicsGetCurrentContext() {
  787. let scale = self.imageSize.width / self.stickersContainer.frame.width
  788. self.stickersContainer.subviews.forEach { (view) in
  789. (view as? ZLStickerViewAdditional)?.resetState()
  790. }
  791. context.concatenate(CGAffineTransform(scaleX: scale, y: scale))
  792. self.stickersContainer.layer.render(in: context)
  793. context.concatenate(CGAffineTransform(scaleX: 1/scale, y: 1/scale))
  794. }
  795. let temp = UIGraphicsGetImageFromCurrentImageContext()
  796. UIGraphicsEndImageContext()
  797. guard let cgi = temp?.cgImage else {
  798. return self.editImage
  799. }
  800. return UIImage(cgImage: cgi, scale: self.editImage.scale, orientation: .up)
  801. }
  802. func finishClipDismissAnimate() {
  803. self.scrollView.alpha = 1
  804. UIView.animate(withDuration: 0.1) {
  805. self.topShadowView.alpha = 1
  806. self.bottomShadowView.alpha = 1
  807. }
  808. }
  809. }
  810. extension ZLEditImageViewController: UIGestureRecognizerDelegate {
  811. public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
  812. guard self.imageStickerContainerIsHidden else {
  813. return false
  814. }
  815. if gestureRecognizer is UITapGestureRecognizer {
  816. if self.bottomShadowView.alpha == 1 {
  817. let p = gestureRecognizer.location(in: self.view)
  818. return !self.bottomShadowView.frame.contains(p)
  819. } else {
  820. return true
  821. }
  822. } else if gestureRecognizer is UIPanGestureRecognizer {
  823. guard let st = self.selectedTool else {
  824. return false
  825. }
  826. return (st == .draw || st == .mosaic) && !self.isScrolling
  827. }
  828. return true
  829. }
  830. }
  831. // MARK: scroll view delegate
  832. extension ZLEditImageViewController: UIScrollViewDelegate {
  833. public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
  834. return self.containerView
  835. }
  836. public func scrollViewDidZoom(_ scrollView: UIScrollView) {
  837. let offsetX = (scrollView.frame.width > scrollView.contentSize.width) ? (scrollView.frame.width - scrollView.contentSize.width) * 0.5 : 0
  838. let offsetY = (scrollView.frame.height > scrollView.contentSize.height) ? (scrollView.frame.height - scrollView.contentSize.height) * 0.5 : 0
  839. self.containerView.center = CGPoint(x: scrollView.contentSize.width * 0.5 + offsetX, y: scrollView.contentSize.height * 0.5 + offsetY)
  840. }
  841. public func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
  842. self.isScrolling = false
  843. }
  844. public func scrollViewDidScroll(_ scrollView: UIScrollView) {
  845. guard scrollView == self.scrollView else {
  846. return
  847. }
  848. self.isScrolling = true
  849. }
  850. public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
  851. guard scrollView == self.scrollView else {
  852. return
  853. }
  854. self.isScrolling = decelerate
  855. }
  856. public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  857. guard scrollView == self.scrollView else {
  858. return
  859. }
  860. self.isScrolling = false
  861. }
  862. public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
  863. guard scrollView == self.scrollView else {
  864. return
  865. }
  866. self.isScrolling = false
  867. }
  868. }
  869. extension ZLEditImageViewController: UICollectionViewDataSource, UICollectionViewDelegate {
  870. public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  871. if collectionView == self.editToolCollectionView {
  872. return self.tools.count
  873. } else if collectionView == self.drawColorCollectionView {
  874. return self.drawColors.count
  875. } else {
  876. return self.thumbnailFilterImages.count
  877. }
  878. }
  879. public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  880. if collectionView == self.editToolCollectionView {
  881. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLEditToolCell.zl_identifier(), for: indexPath) as! ZLEditToolCell
  882. let toolType = self.tools[indexPath.row]
  883. cell.icon.isHighlighted = false
  884. cell.toolType = toolType
  885. cell.icon.isHighlighted = toolType == self.selectedTool
  886. return cell
  887. } else if collectionView == self.drawColorCollectionView {
  888. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLDrawColorCell.zl_identifier(), for: indexPath) as! ZLDrawColorCell
  889. let c = self.drawColors[indexPath.row]
  890. cell.color = c
  891. if c == self.currentDrawColor {
  892. cell.bgWhiteView.layer.transform = CATransform3DMakeScale(1.2, 1.2, 1)
  893. } else {
  894. cell.bgWhiteView.layer.transform = CATransform3DIdentity
  895. }
  896. return cell
  897. } else {
  898. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLFilterImageCell.zl_identifier(), for: indexPath) as! ZLFilterImageCell
  899. let image = self.thumbnailFilterImages[indexPath.row]
  900. let filter = ZLPhotoConfiguration.default().filters[indexPath.row]
  901. cell.nameLabel.text = filter.name
  902. cell.imageView.image = image
  903. if self.currentFilter === filter {
  904. cell.nameLabel.textColor = .white
  905. } else {
  906. cell.nameLabel.textColor = zlRGB(160, 160, 160)
  907. }
  908. return cell
  909. }
  910. }
  911. public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  912. if collectionView == self.editToolCollectionView {
  913. let toolType = self.tools[indexPath.row]
  914. switch toolType {
  915. case .draw:
  916. self.drawBtnClick()
  917. case .clip:
  918. self.clipBtnClick()
  919. case .imageSticker:
  920. self.imageStickerBtnClick()
  921. case .textSticker:
  922. self.textStickerBtnClick()
  923. case .mosaic:
  924. self.mosaicBtnClick()
  925. case .filter:
  926. self.filterBtnClick()
  927. }
  928. } else if collectionView == self.drawColorCollectionView {
  929. self.currentDrawColor = self.drawColors[indexPath.row]
  930. } else {
  931. self.currentFilter = ZLPhotoConfiguration.default().filters[indexPath.row]
  932. if let image = self.filterImages[self.currentFilter.name] {
  933. self.editImage = image
  934. } else {
  935. let image = self.currentFilter.applier?(self.originalImage) ?? self.originalImage
  936. self.editImage = image
  937. self.filterImages[self.currentFilter.name] = image
  938. }
  939. if self.tools.contains(.mosaic) {
  940. self.mosaicImage = self.editImage.mosaicImage()
  941. self.mosaicImageLayer?.removeFromSuperlayer()
  942. self.mosaicImageLayer = CALayer()
  943. self.mosaicImageLayer?.frame = self.imageView.bounds
  944. self.mosaicImageLayer?.contents = self.mosaicImage?.cgImage
  945. self.imageView.layer.insertSublayer(self.mosaicImageLayer!, below: self.mosaicImageLayerMaskLayer)
  946. self.mosaicImageLayer?.mask = self.mosaicImageLayerMaskLayer
  947. if self.mosaicPaths.isEmpty {
  948. self.imageView.image = self.editImage
  949. } else {
  950. self.generateNewMosaicImage()
  951. }
  952. } else {
  953. self.imageView.image = self.editImage
  954. }
  955. }
  956. collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
  957. collectionView.reloadData()
  958. }
  959. }
  960. extension ZLEditImageViewController: ZLTextStickerViewDelegate {
  961. func stickerBeginOperation(_ sticker: UIView) {
  962. self.setToolView(show: false)
  963. self.ashbinView.layer.removeAllAnimations()
  964. self.ashbinView.isHidden = false
  965. var frame = self.ashbinView.frame
  966. let diff = self.view.frame.height - frame.minY
  967. frame.origin.y += diff
  968. self.ashbinView.frame = frame
  969. frame.origin.y -= diff
  970. UIView.animate(withDuration: 0.25) {
  971. self.ashbinView.frame = frame
  972. }
  973. self.stickersContainer.subviews.forEach { (view) in
  974. if view !== sticker {
  975. (view as? ZLStickerViewAdditional)?.resetState()
  976. (view as? ZLStickerViewAdditional)?.gesIsEnabled = false
  977. }
  978. }
  979. }
  980. func stickerOnOperation(_ sticker: UIView, panGes: UIPanGestureRecognizer) {
  981. let point = panGes.location(in: self.view)
  982. if self.ashbinView.frame.contains(point) {
  983. self.ashbinView.backgroundColor = zlRGB(241, 79, 79).withAlphaComponent(0.98)
  984. self.ashbinImgView.isHighlighted = true
  985. if sticker.alpha == 1 {
  986. sticker.layer.removeAllAnimations()
  987. UIView.animate(withDuration: 0.25) {
  988. sticker.alpha = 0.5
  989. }
  990. }
  991. } else {
  992. self.ashbinView.backgroundColor = ZLEditImageViewController.ashbinNormalBgColor
  993. self.ashbinImgView.isHighlighted = false
  994. if sticker.alpha != 1 {
  995. sticker.layer.removeAllAnimations()
  996. UIView.animate(withDuration: 0.25) {
  997. sticker.alpha = 1
  998. }
  999. }
  1000. }
  1001. }
  1002. func stickerEndOperation(_ sticker: UIView, panGes: UIPanGestureRecognizer) {
  1003. self.setToolView(show: true)
  1004. self.ashbinView.layer.removeAllAnimations()
  1005. self.ashbinView.isHidden = true
  1006. let point = panGes.location(in: self.view)
  1007. if self.ashbinView.frame.contains(point) {
  1008. (sticker as? ZLStickerViewAdditional)?.moveToAshbin()
  1009. }
  1010. self.stickersContainer.subviews.forEach { (view) in
  1011. (view as? ZLStickerViewAdditional)?.gesIsEnabled = true
  1012. }
  1013. }
  1014. func stickerDidTap(_ sticker: UIView) {
  1015. self.stickersContainer.subviews.forEach { (view) in
  1016. if view !== sticker {
  1017. (view as? ZLStickerViewAdditional)?.resetState()
  1018. }
  1019. }
  1020. }
  1021. func sticker(_ textSticker: ZLTextStickerView, editText text: String) {
  1022. self.showInputTextVC(text, textColor: textSticker.textColor, bgColor: textSticker.bgColor) { [weak self] (text, textColor, bgColor) in
  1023. guard let `self` = self else { return }
  1024. if text.isEmpty {
  1025. textSticker.moveToAshbin()
  1026. } else {
  1027. textSticker.startTimer()
  1028. guard textSticker.text != text || textSticker.textColor != textColor || textSticker.bgColor != bgColor else {
  1029. return
  1030. }
  1031. textSticker.text = text
  1032. textSticker.textColor = textColor
  1033. textSticker.bgColor = bgColor
  1034. let newSize = ZLTextStickerView.calculateSize(text: text, width: self.view.frame.width)
  1035. textSticker.changeSize(to: newSize)
  1036. }
  1037. }
  1038. }
  1039. }
  1040. extension ZLEditImageViewController {
  1041. @objc public enum EditImageTool: Int {
  1042. case draw
  1043. case clip
  1044. case imageSticker
  1045. case textSticker
  1046. case mosaic
  1047. case filter
  1048. }
  1049. }
  1050. // MARK: 裁剪比例
  1051. public class ZLImageClipRatio: NSObject {
  1052. let title: String
  1053. let whRatio: CGFloat
  1054. @objc public init(title: String, whRatio: CGFloat) {
  1055. self.title = title
  1056. self.whRatio = whRatio
  1057. }
  1058. }
  1059. func ==(lhs: ZLImageClipRatio, rhs: ZLImageClipRatio) -> Bool {
  1060. return lhs.whRatio == rhs.whRatio
  1061. }
  1062. extension ZLImageClipRatio {
  1063. @objc public static let custom = ZLImageClipRatio(title: "custom", whRatio: 0)
  1064. @objc public static let wh1x1 = ZLImageClipRatio(title: "1 : 1", whRatio: 1)
  1065. @objc public static let wh3x4 = ZLImageClipRatio(title: "3 : 4", whRatio: 3.0/4.0)
  1066. @objc public static let wh4x3 = ZLImageClipRatio(title: "4 : 3", whRatio: 4.0/3.0)
  1067. @objc public static let wh2x3 = ZLImageClipRatio(title: "2 : 3", whRatio: 2.0/3.0)
  1068. @objc public static let wh3x2 = ZLImageClipRatio(title: "3 : 2", whRatio: 3.0/2.0)
  1069. @objc public static let wh9x16 = ZLImageClipRatio(title: "9 : 16", whRatio: 9.0/16.0)
  1070. @objc public static let wh16x9 = ZLImageClipRatio(title: "16 : 9", whRatio: 16.0/9.0)
  1071. }
  1072. // MARK: Edit tool cell
  1073. class ZLEditToolCell: UICollectionViewCell {
  1074. var toolType: ZLEditImageViewController.EditImageTool? {
  1075. didSet {
  1076. switch toolType {
  1077. case .draw?:
  1078. self.icon.image = getImage("zl_drawLine")
  1079. self.icon.highlightedImage = getImage("zl_drawLine_selected")
  1080. case .clip?:
  1081. self.icon.image = getImage("zl_clip")
  1082. self.icon.highlightedImage = getImage("zl_clip")
  1083. case .imageSticker?:
  1084. self.icon.image = getImage("zl_imageSticker")
  1085. self.icon.highlightedImage = getImage("zl_imageSticker")
  1086. case .textSticker?:
  1087. self.icon.image = getImage("zl_textSticker")
  1088. self.icon.highlightedImage = getImage("zl_textSticker")
  1089. case .mosaic?:
  1090. self.icon.image = getImage("zl_mosaic")
  1091. self.icon.highlightedImage = getImage("zl_mosaic_selected")
  1092. case .filter?:
  1093. self.icon.image = getImage("zl_filter")
  1094. self.icon.highlightedImage = getImage("zl_filter_selected")
  1095. default:
  1096. break
  1097. }
  1098. }
  1099. }
  1100. var icon: UIImageView!
  1101. override init(frame: CGRect) {
  1102. super.init(frame: frame)
  1103. self.icon = UIImageView(frame: self.contentView.bounds)
  1104. self.contentView.addSubview(self.icon)
  1105. }
  1106. required init?(coder: NSCoder) {
  1107. fatalError("init(coder:) has not been implemented")
  1108. }
  1109. }
  1110. // MARK: draw color cell
  1111. class ZLDrawColorCell: UICollectionViewCell {
  1112. var bgWhiteView: UIView!
  1113. var colorView: UIView!
  1114. var color: UIColor! {
  1115. didSet {
  1116. self.colorView.backgroundColor = color
  1117. }
  1118. }
  1119. override init(frame: CGRect) {
  1120. super.init(frame: frame)
  1121. self.bgWhiteView = UIView()
  1122. self.bgWhiteView.backgroundColor = .white
  1123. self.bgWhiteView.layer.cornerRadius = 10
  1124. self.bgWhiteView.layer.masksToBounds = true
  1125. self.bgWhiteView.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
  1126. self.bgWhiteView.center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
  1127. self.contentView.addSubview(self.bgWhiteView)
  1128. self.colorView = UIView()
  1129. self.colorView.layer.cornerRadius = 8
  1130. self.colorView.layer.masksToBounds = true
  1131. self.colorView.frame = CGRect(x: 0, y: 0, width: 16, height: 16)
  1132. self.colorView.center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
  1133. self.contentView.addSubview(self.colorView)
  1134. }
  1135. required init?(coder: NSCoder) {
  1136. fatalError("init(coder:) has not been implemented")
  1137. }
  1138. }
  1139. // MARK: filter cell
  1140. class ZLFilterImageCell: UICollectionViewCell {
  1141. var nameLabel: UILabel!
  1142. var imageView: UIImageView!
  1143. override init(frame: CGRect) {
  1144. super.init(frame: frame)
  1145. self.nameLabel = UILabel(frame: CGRect(x: 0, y: self.bounds.height-20, width: self.bounds.width, height: 20))
  1146. self.nameLabel.font = getFont(12)
  1147. self.nameLabel.textColor = .white
  1148. self.nameLabel.textAlignment = .center
  1149. self.nameLabel.layer.shadowColor = UIColor.black.withAlphaComponent(0.3).cgColor
  1150. self.nameLabel.layer.shadowOffset = .zero
  1151. self.nameLabel.layer.shadowOpacity = 1
  1152. self.nameLabel.adjustsFontSizeToFitWidth = true
  1153. self.nameLabel.minimumScaleFactor = 0.5
  1154. self.contentView.addSubview(self.nameLabel)
  1155. self.imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.width))
  1156. self.imageView.contentMode = .scaleAspectFill
  1157. self.imageView.clipsToBounds = true
  1158. self.contentView.addSubview(self.imageView)
  1159. }
  1160. required init?(coder: NSCoder) {
  1161. fatalError("init(coder:) has not been implemented")
  1162. }
  1163. }
  1164. // MARK: 涂鸦path
  1165. public class ZLDrawPath: NSObject {
  1166. let pathColor: UIColor
  1167. let path: UIBezierPath
  1168. let ratio: CGFloat
  1169. let shapeLayer: CAShapeLayer
  1170. init(pathColor: UIColor, pathWidth: CGFloat, ratio: CGFloat, startPoint: CGPoint) {
  1171. self.pathColor = pathColor
  1172. self.path = UIBezierPath()
  1173. self.path.lineWidth = pathWidth / ratio
  1174. self.path.lineCapStyle = .round
  1175. self.path.lineJoinStyle = .round
  1176. self.path.move(to: CGPoint(x: startPoint.x / ratio, y: startPoint.y / ratio))
  1177. self.shapeLayer = CAShapeLayer()
  1178. self.shapeLayer.lineCap = .round
  1179. self.shapeLayer.lineJoin = .round
  1180. self.shapeLayer.lineWidth = pathWidth / ratio
  1181. self.shapeLayer.fillColor = UIColor.clear.cgColor
  1182. self.shapeLayer.strokeColor = pathColor.cgColor
  1183. self.shapeLayer.path = self.path.cgPath
  1184. self.ratio = ratio
  1185. super.init()
  1186. }
  1187. func addLine(to point: CGPoint) {
  1188. self.path.addLine(to: CGPoint(x: point.x / self.ratio, y: point.y / self.ratio))
  1189. self.shapeLayer.path = self.path.cgPath
  1190. }
  1191. func drawPath() {
  1192. self.pathColor.set()
  1193. self.path.stroke()
  1194. }
  1195. }
  1196. // MARK: 马赛克path
  1197. public class ZLMosaicPath: NSObject {
  1198. let path: UIBezierPath
  1199. let ratio: CGFloat
  1200. let startPoint: CGPoint
  1201. var linePoints: [CGPoint] = []
  1202. init(pathWidth: CGFloat, ratio: CGFloat, startPoint: CGPoint) {
  1203. self.path = UIBezierPath()
  1204. self.path.lineWidth = pathWidth
  1205. self.path.lineCapStyle = .round
  1206. self.path.lineJoinStyle = .round
  1207. self.path.move(to: startPoint)
  1208. self.ratio = ratio
  1209. self.startPoint = CGPoint(x: startPoint.x / ratio, y: startPoint.y / ratio)
  1210. super.init()
  1211. }
  1212. func addLine(to point: CGPoint) {
  1213. self.path.addLine(to: point)
  1214. self.linePoints.append(CGPoint(x: point.x / self.ratio, y: point.y / self.ratio))
  1215. }
  1216. }