KSMediaPickerView.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. //
  2. // KSMediaPickerView.swift
  3. //
  4. //
  5. // Created by kinsun on 2019/3/1.
  6. //
  7. import UIKit
  8. open class KSMediaPickerView: UIView {
  9. required public init?(coder aDecoder: NSCoder) {
  10. fatalError("init(coder:) has not been implemented")
  11. }
  12. public let albumNavigationView = KSMediaPickerView.navigationView()
  13. var jumpType: KSMediaPickerController.jumpType?
  14. public let collectionView = {() -> KSMediaPickerCollectionView in
  15. let layout = UICollectionViewFlowLayout()
  16. layout.scrollDirection = .vertical
  17. let collectionView = KSMediaPickerCollectionView(frame: .zero, collectionViewLayout: layout)
  18. collectionView.backgroundColor = .clear
  19. collectionView.alwaysBounceVertical = true
  20. collectionView.clipsToBounds = true
  21. collectionView.bounces = false
  22. if #available(iOS 11.0, *) {
  23. collectionView.contentInsetAdjustmentBehavior = .never
  24. }
  25. return collectionView
  26. }()
  27. public let previewView = KSMediaPickerPreviewView()
  28. private let _toolBarSafeAreaView = {() -> UIView in
  29. let toolBarSafeAreaView = UIView()
  30. toolBarSafeAreaView.backgroundColor = .clear
  31. return toolBarSafeAreaView
  32. }()
  33. private let _blackBackgroundLayer = {() -> CALayer in
  34. let blackBackgroundLayer = CALayer()
  35. blackBackgroundLayer.opacity = 0.0
  36. blackBackgroundLayer.backgroundColor = UIColor.ks_black.cgColor
  37. return blackBackgroundLayer
  38. }()
  39. override public init(frame: CGRect) {
  40. super.init(frame: frame)
  41. backgroundColor = .ks_white
  42. layer.addSublayer(_blackBackgroundLayer)
  43. addSubview(collectionView)
  44. addSubview(previewView)
  45. albumNavigationView.nextButton.isEnabled = false
  46. addSubview(albumNavigationView)
  47. collectionView.handlePanCallback = {[weak self] (pan) in
  48. self?._collectionView(did: pan)
  49. }
  50. collectionView.scrollViewDidScrollCallback = {[weak self] (scrollView) in
  51. self?._collectionViewDidScroll(scrollView)
  52. }
  53. collectionView.scrollViewDidEndDraggingCallback = {[weak self] (scrollView, decelerate) in
  54. self?._collectionViewDidEndDragging(scrollView, decelerate: decelerate)
  55. }
  56. }
  57. override open func layoutSublayers(of layer: CALayer) {
  58. super.layoutSublayers(of: layer)
  59. _blackBackgroundLayer.frame = layer.bounds
  60. }
  61. override open func layoutSubviews() {
  62. super.layoutSubviews()
  63. let bounds = self.bounds
  64. let safeArea = UIEdgeInsets.safeAreaInsets
  65. let windowSize = bounds.size
  66. let windowWidth = windowSize.width
  67. let windowHeight = windowSize.height
  68. let floatZore = CGFloat(0.0)
  69. let safeBottomMargin = safeArea.bottom
  70. var viewW = windowWidth
  71. var viewH = safeBottomMargin+48.0
  72. var viewX = floatZore
  73. var viewY = windowHeight-viewH
  74. _toolBarSafeAreaView.frame = CGRect(x: viewX, y: viewY, width: viewW, height: viewH)
  75. let margin = CGFloat(3.0)
  76. let columnCount = UInt(4)
  77. let itemW = floor((windowWidth-margin*CGFloat(columnCount-1))/CGFloat(columnCount))
  78. let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
  79. layout.itemSize = CGSize(width: itemW, height: itemW)
  80. layout.minimumLineSpacing = margin
  81. layout.minimumInteritemSpacing = margin
  82. layout.sectionInset = .zero
  83. viewX = floatZore
  84. viewY = floatZore
  85. viewW = windowWidth
  86. viewH = UIView.statusBarNavigationBarSize.height
  87. albumNavigationView.frame = CGRect(x: viewX, y: viewY, width: viewW, height: viewH)
  88. let navHeight = albumNavigationView.frame.maxY
  89. let baseY = previewView.frame.origin.y
  90. viewX = floatZore
  91. if baseY == 0 {
  92. viewY = navHeight
  93. _baseY = viewY
  94. } else {
  95. viewY = baseY
  96. }
  97. viewW = windowWidth
  98. viewH = viewW
  99. previewView.frame = CGRect(x: viewX, y: viewY, width: viewW, height: viewH)
  100. let previewViewFrameMaxY = previewView.frame.maxY
  101. viewY = floatZore
  102. viewH = previewViewFrameMaxY+20.0
  103. _previewGestureCorrespondingArea = CGRect(x: viewX, y: viewY, width: viewW, height: viewH)
  104. let toolBarSafeAreaViewHeight = _toolBarSafeAreaView.bounds.size.height
  105. viewX = floatZore
  106. viewY = floatZore
  107. viewW = windowWidth
  108. viewH = windowHeight-toolBarSafeAreaViewHeight
  109. if jumpType == .publishEdit {
  110. let frame = CGRect(x: viewX, y: viewY, width: viewW, height: viewH + kTabBarTotalHeight)
  111. collectionView.frame = frame
  112. }else {
  113. let frame = CGRect(x: viewX, y: viewY, width: viewW, height: viewH)
  114. collectionView.frame = frame
  115. }
  116. let conetntInset = UIEdgeInsets(top: previewViewFrameMaxY+3.0, left: 0.0, bottom: 0.0, right: 0.0)
  117. collectionView.contentInset = conetntInset
  118. collectionView.scrollIndicatorInsets = conetntInset
  119. }
  120. private var _baseY: CGFloat?
  121. private var _previewGestureCorrespondingArea: CGRect?
  122. private var _isInGestureCorrespondingArea = false
  123. private var _panBeginLocationY = CGFloat(0)
  124. private var _isScrollDown = false
  125. private var _isRetract = false {
  126. didSet {
  127. if albumNavigationView.isHidden != _isRetract {
  128. let trans = CATransition()
  129. trans.duration = 0.2
  130. trans.type = .push
  131. trans.subtype = _isRetract ? .fromTop : .fromBottom
  132. albumNavigationView.isHidden = _isRetract
  133. albumNavigationView.layer.add(trans, forKey: nil)
  134. }
  135. }
  136. }
  137. private func _collectionView(did pan: UIPanGestureRecognizer) {
  138. switch pan.state {
  139. case .began:
  140. _panBeginLocationY = pan.location(in: self).y
  141. break
  142. case .changed:
  143. // FIXME: preview视图收起展示逻辑
  144. // guard let baseY = _baseY else {
  145. // return
  146. // }
  147. // let location = pan.location(in: self)
  148. // let locationY = location.y
  149. // _isScrollDown = locationY > _panBeginLocationY
  150. // _isInGestureCorrespondingArea = _previewGestureCorrespondingArea != nil && _previewGestureCorrespondingArea!.contains(location)
  151. // if _isInGestureCorrespondingArea {
  152. // var previewFrame = previewView.frame
  153. // var y = locationY-previewFrame.size.height
  154. // if y >= baseY {
  155. // y = baseY
  156. // }
  157. // previewFrame.origin.y = y
  158. // previewView.frame = previewFrame
  159. //
  160. // _previewGestureCorrespondingArea!.size.height = previewFrame.maxY+20.0
  161. // }
  162. // _panBeginLocationY = locationY
  163. break
  164. case .cancelled, .ended, .failed:
  165. guard _isInGestureCorrespondingArea, let baseY = _baseY else {
  166. return
  167. }
  168. var previewFrame = previewView.frame
  169. let maxY = previewFrame.maxY
  170. let boundary = (previewFrame.size.height+baseY)*(_isScrollDown ? 0.2 : 0.8)
  171. if maxY < boundary {
  172. _isRetract = true
  173. previewFrame.origin.y = baseY-previewFrame.size.height
  174. } else {
  175. _isRetract = false
  176. previewFrame.origin.y = baseY
  177. }
  178. let height = previewFrame.maxY
  179. _previewGestureCorrespondingArea!.size.height = height+20.0
  180. var topPoint: CGPoint? = nil
  181. let offsetY = -collectionView.contentOffset.y
  182. if offsetY > height {
  183. topPoint = CGPoint(x: 0.0, y: -(height+3.0))
  184. }
  185. UIView.animate(withDuration: 0.2, animations: {[weak self, weak previewView] in
  186. previewView?.frame = previewFrame
  187. guard let k_topPoint = topPoint else {
  188. return
  189. }
  190. self?.collectionView.contentOffset = k_topPoint
  191. })
  192. break
  193. default:
  194. break
  195. }
  196. }
  197. private func _collectionViewDidScroll(_ scrollView: KSMediaPickerCollectionView) {
  198. guard !_isInGestureCorrespondingArea, _isScrollDown, _isRetract, let baseY = _baseY else {
  199. return
  200. }
  201. let offsetY = -(scrollView.contentOffset.y)
  202. if offsetY >= baseY {
  203. var previewFrame = previewView.frame
  204. var y = offsetY-previewFrame.size.height
  205. if y >= baseY {
  206. y = baseY
  207. }
  208. previewFrame.origin.y = y
  209. previewView.frame = previewFrame
  210. _previewGestureCorrespondingArea!.size.height = previewFrame.maxY+20.0
  211. }
  212. }
  213. private func _collectionViewDidEndDragging(_ scrollView: KSMediaPickerCollectionView, decelerate: Bool) {
  214. var previewFrame = previewView.frame
  215. let maxY = previewFrame.maxY
  216. guard scrollView.contentOffset.y <= -maxY, !_isInGestureCorrespondingArea, let baseY = _baseY else {
  217. return
  218. }
  219. let boundary = (previewFrame.size.height+baseY)*(_isScrollDown ? 0.2 : 0.8)
  220. if maxY < boundary && !_isRetract {
  221. _isRetract = true
  222. previewFrame.origin.y = baseY-previewFrame.size.height
  223. } else if _isRetract {
  224. _isRetract = false
  225. previewFrame.origin.y = baseY
  226. } else {
  227. return
  228. }
  229. let height = previewFrame.maxY
  230. _previewGestureCorrespondingArea!.size.height = height+20.0
  231. let topPoint = CGPoint(x: 0.0, y: -(height+3.0))
  232. UIView.animate(withDuration: 0.2, animations: {[weak self, weak previewView] in
  233. previewView?.frame = previewFrame
  234. self?.collectionView.contentOffset = topPoint
  235. })
  236. }
  237. public func showPreview(_ animated: Bool) {
  238. var previewFrame = previewView.frame
  239. previewFrame.origin.y = _baseY ?? 0
  240. _isRetract = false
  241. if animated {
  242. UIView.animate(withDuration: 0.2, animations: {[weak self] in
  243. self?.previewView.frame = previewFrame
  244. }) {[weak self] (finish) in
  245. self?.setNeedsLayout()
  246. }
  247. } else {
  248. setNeedsLayout()
  249. }
  250. }
  251. public func collectionViewScrollToTop() {
  252. let point = CGPoint(x: 0.0, y: -collectionView.contentInset.top)
  253. if !point.equalTo(collectionView.contentOffset) {
  254. collectionView.setContentOffset(point, animated: false)
  255. showPreview(false)
  256. }
  257. }
  258. }