TLPhontoPreviewView.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. //
  2. // TLPhontoPreviewView.swift
  3. // RainbowPlanet
  4. //
  5. // Created by 南鑫林 on 2019/8/25.
  6. // Copyright © 2019 RainbowPlanet. All rights reserved.
  7. //
  8. import UIKit
  9. import PhotosUI
  10. import Kingfisher
  11. extension TLPhontoPreviewView {
  12. private class scrollView: UIScrollView, UIScrollViewDelegate {
  13. private enum imageDirection {
  14. case equal
  15. case transversal
  16. case lengthwise
  17. }
  18. required init?(coder aDecoder: NSCoder) {
  19. fatalError("init(coder:) has not been implemented")
  20. }
  21. public let imageView = UIImageView()
  22. override init(frame: CGRect) {
  23. super.init(frame: frame)
  24. backgroundColor = .clear
  25. showsVerticalScrollIndicator = false
  26. showsHorizontalScrollIndicator = false
  27. alwaysBounceVertical = true
  28. alwaysBounceHorizontal = true
  29. maximumZoomScale = 3.0
  30. clipsToBounds = true
  31. if #available(iOS 11.0, *) {
  32. contentInsetAdjustmentBehavior = .never
  33. }
  34. addSubview(imageView)
  35. delegate = self
  36. }
  37. private var _isSquare = false
  38. override open var frame: CGRect {
  39. set {
  40. if frame.size != newValue.size {
  41. let windowSize = newValue.size
  42. _isSquare = floor(windowSize.width) == floor(windowSize.height)
  43. _imageDirection = _imageDirection(from: _imageSize, windowSize)
  44. }
  45. if _isNeedLayoutSubviews {
  46. zoomScale = 1.0
  47. contentOffset = .zero
  48. }
  49. super.frame = newValue
  50. }
  51. get {
  52. return super.frame
  53. }
  54. }
  55. private var _isNeedLayoutSubviews = true
  56. public func set(frame: CGRect, isNeedLayoutSubviews: Bool) {
  57. _isNeedLayoutSubviews = isNeedLayoutSubviews
  58. self.frame = frame
  59. }
  60. open var itemModel: KSMediaPickerItemModel? {
  61. didSet {
  62. guard let k_itemModel = itemModel else {
  63. return
  64. }
  65. let asset = k_itemModel.asset
  66. let floatWidth = CGFloat(asset.pixelWidth)
  67. let floatHeight = CGFloat(asset.pixelHeight)
  68. _imageSize = CGSize(width: floatWidth, height: floatHeight)
  69. }
  70. }
  71. var _imageSize = CGSize.zero {
  72. didSet {
  73. _imageDirection = _imageDirection(from: _imageSize, frame.size)
  74. _isNeedLayoutSubviews = true
  75. setNeedsLayout()
  76. }
  77. }
  78. override func layoutSubviews() {
  79. super.layoutSubviews()
  80. guard _isNeedLayoutSubviews || imageView.frame.size == .zero else {
  81. return
  82. }
  83. _isNeedLayoutSubviews = false
  84. let offset: CGPoint
  85. if _imageDirection == .equal {
  86. imageView.frame = bounds
  87. offset = .zero
  88. } else if _imageSize != .zero {
  89. let windowSize = frame.size
  90. let windowWidth = windowSize.width
  91. let windowHeight = windowSize.height
  92. let floatZore = CGFloat(0.0)
  93. let viewX: CGFloat
  94. let viewY: CGFloat
  95. let viewW: CGFloat
  96. let viewH: CGFloat
  97. let imageWidth = _imageSize.width
  98. let imageHeight = _imageSize.height
  99. if _imageDirection == .transversal {
  100. viewH = windowHeight
  101. viewW = imageWidth/imageHeight*viewH
  102. viewY = floatZore
  103. viewX = (windowWidth-viewW)*0.5
  104. } else {
  105. viewW = windowWidth
  106. viewH = imageHeight/imageWidth*viewW
  107. viewX = floatZore
  108. viewY = (windowHeight-viewH)*0.5
  109. }
  110. imageView.frame = CGRect(origin: .zero, size: CGSize(width: viewW, height: viewH))
  111. offset = CGPoint(x: -viewX, y: -viewY)
  112. } else {
  113. offset = .zero
  114. }
  115. contentSize = imageView.frame.size
  116. zoomScale = itemModel?.zoomScale ?? 1.0
  117. contentOffset = itemModel?.contentOffset ?? offset
  118. let minimumZoomScale: CGFloat
  119. if _isSquare {
  120. switch _imageDirection {
  121. case .equal:
  122. minimumZoomScale = 1.0
  123. break
  124. case .transversal:
  125. minimumZoomScale = frame.size.width/imageView.frame.size.width
  126. break
  127. case .lengthwise:
  128. minimumZoomScale = frame.size.height/imageView.frame.size.height
  129. break
  130. }
  131. } else {
  132. minimumZoomScale = 1.0
  133. }
  134. self.minimumZoomScale = minimumZoomScale
  135. }
  136. private func _imageDirection(from imageSize: CGSize, _ superSize: CGSize) -> TLPhontoPreviewView.scrollView.imageDirection {
  137. let superWidth = floor(superSize.width)
  138. let superHeight = floor(superSize.height)
  139. let imageWidth = floor(imageSize.width)
  140. let imageHeight = floor(imageSize.height)
  141. let constrainImageHeight = floor(imageHeight/imageWidth*superWidth)
  142. let dirction: TLPhontoPreviewView.scrollView.imageDirection
  143. if superHeight == constrainImageHeight {
  144. dirction = .equal
  145. } else {
  146. let maxHeight = max(superHeight, constrainImageHeight)
  147. if maxHeight == superHeight {
  148. dirction = .transversal
  149. } else {
  150. dirction = .lengthwise
  151. }
  152. }
  153. return dirction
  154. }
  155. private var _imageDirection = TLPhontoPreviewView.scrollView.imageDirection.equal
  156. func viewForZooming(in scrollView: UIScrollView) -> UIView? {
  157. return imageView
  158. }
  159. func scrollViewDidZoom(_ scrollView: UIScrollView) {
  160. let contentSize = scrollView.contentSize
  161. let contentSizeWidth = floor(contentSize.width)
  162. let contentSizeHeight = floor(contentSize.height)
  163. let size = scrollView.frame.size
  164. var center = scrollView.center
  165. if contentSizeWidth >= floor(size.width) {
  166. center.x = contentSizeWidth*0.5
  167. }
  168. if contentSizeHeight >= floor(size.height) {
  169. center.y = contentSizeHeight*0.5
  170. }
  171. imageView.center = center
  172. }
  173. }
  174. }
  175. import Photos
  176. open class TLPhontoPreviewView: UIView {
  177. required public init?(coder aDecoder: NSCoder) {
  178. fatalError("init(coder:) has not been implemented")
  179. }
  180. private var _photoLibrary = TLPhotoLibrary()
  181. private let _videoView = {() -> KSVideoPlayerBaseView in
  182. let videoView = KSVideoPlayerBaseView()
  183. videoView.videoGravity = .resizeAspect
  184. videoView.videoPlaybackFinished = {[weak videoView] in
  185. videoView?.play()
  186. }
  187. videoView.isHidden = true
  188. return videoView
  189. }()
  190. private let _scrollview = TLPhontoPreviewView.scrollView()
  191. private let _changeSizeButton = {() -> UIButton in
  192. let changeSizeButton = UIButton(type: .custom)
  193. changeSizeButton.setImage(UIImage(named: "icon_mediaPicker_preview_nocut"), for: .normal)
  194. changeSizeButton.setImage(UIImage(named: "icon_mediaPicker_preview_cut"), for: .selected)
  195. changeSizeButton.backgroundColor = .clear
  196. return changeSizeButton
  197. }()
  198. override public init(frame: CGRect) {
  199. super.init(frame: frame)
  200. backgroundColor = .black
  201. clipsToBounds = true
  202. addSubview(_scrollview)
  203. _changeSizeButton.addTarget(self, action: #selector(_didClick(changeSizeButton:)), for: .touchUpInside)
  204. addSubview(_changeSizeButton)
  205. addSubview(_videoView)
  206. }
  207. override open func layoutSubviews() {
  208. super.layoutSubviews()
  209. let bounds = self.bounds
  210. _videoView.frame = bounds
  211. let windowSize = bounds.size
  212. let windowWidth = windowSize.width
  213. let windowHeight = windowSize.height
  214. let scrollviewSize = _scrollview.frame.size
  215. var viewW = scrollviewSize.width
  216. var viewH = scrollviewSize.height
  217. var viewX = (windowWidth-viewW)*0.5
  218. var viewY = (windowHeight-viewH)*0.5
  219. _scrollview.frame = CGRect(x: viewX, y: viewY, width: viewW, height: viewH)
  220. viewW = 30.0
  221. viewH = viewW
  222. viewX = CGFloat(10.0)
  223. viewY = windowHeight-viewH-10.0
  224. _changeSizeButton.frame = CGRect(x: viewX, y: viewY, width: viewW, height: viewH)
  225. // _changeSizeButton.layer.cornerRadius = viewH*0.5
  226. }
  227. @objc private func _didClick(changeSizeButton: UIButton) {
  228. let isChangedSize = changeSizeButton.isSelected
  229. changeSizeButton.isSelected = !isChangedSize
  230. let frame: CGRect
  231. if isChangedSize {
  232. frame = bounds
  233. } else {
  234. let windowSize = bounds.size
  235. let windowWidth = windowSize.width
  236. let windowHeight = windowSize.height
  237. let viewW = _minScrollViewSize.width
  238. let viewH = _minScrollViewSize.height
  239. let viewX = (windowWidth-viewW)*0.5
  240. let viewY = (windowHeight-viewH)*0.5
  241. frame = CGRect(x: viewX, y: viewY, width: viewW, height: viewH)
  242. }
  243. UIView.animate(withDuration: 0.2, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .layoutSubviews, animations: {[weak self] in
  244. self?._scrollview.set(frame: frame, isNeedLayoutSubviews: true)
  245. }, completion: nil)
  246. }
  247. static private let _minScale = CGFloat(3.0/4.0)
  248. private var _minScrollViewSize = CGSize.zero
  249. private var _normalScrollViewSize = CGSize.zero
  250. var _asset : TLPHAsset? {
  251. didSet {
  252. guard let asset = _asset else {
  253. return
  254. }
  255. if asset.type == .video {
  256. _videoView.isHidden = false
  257. _scrollview.isHidden = true
  258. _changeSizeButton.isHidden = true
  259. _ = _photoLibrary.videoAsset(asset: asset.phAsset!, size: CGSize(width: kScreenWidth, height: kScreenWidth), progressBlock: { (double, error, unsafeMutablePointer, dict) in
  260. }) { [weak self] (avPlayerItem, dict) in
  261. guard let videoView = self?._videoView else {
  262. return
  263. }
  264. if Thread.current.isMainThread {
  265. videoView.playerItem = avPlayerItem
  266. videoView.play()
  267. } else {
  268. DispatchQueue.main.async {
  269. videoView.playerItem = avPlayerItem
  270. videoView.play()
  271. }
  272. }
  273. }
  274. }else if asset.type == .photo || asset.type == .livePhoto {
  275. let mainSize = UIScreen.main.bounds.size
  276. _videoView.isHidden = true
  277. if _videoView.isPlaying {
  278. _videoView.pause()
  279. }
  280. _scrollview.isHidden = false
  281. let pixelWidth = asset.phAsset?.pixelWidth
  282. let pixelHeight = asset.phAsset?.pixelHeight
  283. _scrollview._imageSize = CGSize(width: pixelWidth ?? 0, height: pixelHeight ?? 0)
  284. let windowWidth = mainSize.width
  285. if _isStandard {
  286. _normalScrollViewSize = CGSize(width: windowWidth, height: windowWidth)
  287. if pixelWidth == pixelHeight {
  288. _changeSizeButton.isHidden = true
  289. _minScrollViewSize = _normalScrollViewSize
  290. } else {
  291. _changeSizeButton.isHidden = false
  292. let maxWidth = max(pixelHeight ?? 0, pixelWidth ?? 0)
  293. let minScale = TLPhontoPreviewView._minScale
  294. let floatWidth = CGFloat(pixelWidth ?? 0)
  295. let floatHeight = CGFloat(pixelHeight ?? 0)
  296. let scrollViewSize: CGSize
  297. if maxWidth == pixelWidth {//横向
  298. let scale = floatHeight/floatWidth
  299. let minHeight: CGFloat
  300. let minWidth = windowWidth
  301. if scale > minScale {
  302. minHeight = floor(floatHeight*minWidth/floatWidth)
  303. } else {
  304. minHeight = floor(minWidth*minScale)
  305. }
  306. scrollViewSize = CGSize(width: minWidth, height: minHeight)
  307. } else {//纵向
  308. let scale = floatWidth/floatHeight
  309. let minWidth: CGFloat
  310. let minHeight = windowWidth
  311. if scale > minScale {
  312. minWidth = floor(floatWidth*minHeight/floatHeight)
  313. } else {
  314. minWidth = floor(minHeight*minScale)
  315. }
  316. scrollViewSize = CGSize(width: minWidth, height: minHeight)
  317. }
  318. _minScrollViewSize = scrollViewSize
  319. }
  320. } else {
  321. _changeSizeButton.isHidden = true
  322. }
  323. _scrollview.set(frame: CGRect(origin: .zero, size: _changeSizeButton.isSelected ? _minScrollViewSize : _normalScrollViewSize), isNeedLayoutSubviews: true)
  324. setNeedsLayout()
  325. self._scrollview.imageView.image = asset.fullResolutionImage
  326. }
  327. }
  328. }
  329. private var _isStandard = true
  330. open var isStandard: Bool {
  331. return _isStandard
  332. }
  333. public func videoPlay() {
  334. if !_videoView.isHidden {
  335. _videoView.play()
  336. }
  337. }
  338. public func videoPause() {
  339. if !_videoView.isHidden || _videoView.isPlaying {
  340. _videoView.pause()
  341. }
  342. }
  343. }