PQUploadController.swift 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  1. //
  2. // PQUploadViewController.swift
  3. // PQSpeed
  4. //
  5. // Created by SanW on 2020/8/1.
  6. // Copyright © 2020 BytesFlow. All rights reserved.
  7. //
  8. import MobileCoreServices
  9. import Photos
  10. import UIKit
  11. import BFCommonKit
  12. let playerHeaderH: CGFloat = cScreenWidth * (250 / 375)
  13. open class PQUploadController: BFBaseViewController {
  14. // 最大的宽度
  15. public var maxWidth: CGFloat = cScreenWidth
  16. // 最大的高度
  17. public var maxHeight: CGFloat = adapterWidth(width: 300)
  18. public var jumptoPublicHandle:((_ selectData:PQUploadModel?) -> Void)?
  19. // 画面比例
  20. public var aspectRatio: aspectRatio?
  21. public var preViewSize: CGSize {
  22. if aspectRatio == nil, (isMember(of: PQUploadController.self) && selectedData == nil) || (!isMember(of: PQUploadController.self) && uploadData == nil) {
  23. return CGSize(width: maxHeight, height: maxHeight)
  24. }
  25. if selectedData != nil {
  26. aspectRatio = .origin(width: CGFloat(videoData[lastSeletedIndex?.item ?? 0].videoWidth), height: CGFloat(videoData[lastSeletedIndex?.item ?? 0].videoHeight))
  27. } else if aspectRatio == nil {
  28. aspectRatio = .origin(width: CGFloat(uploadData?.videoWidth ?? maxHeight), height: CGFloat(uploadData?.videoHeight ?? maxHeight))
  29. }
  30. switch aspectRatio {
  31. case let .origin(width, height):
  32. var tempHeight: CGFloat = 0
  33. var tempWidth: CGFloat = 0
  34. if width > height {
  35. tempHeight = (maxWidth * height / width)
  36. tempWidth = maxWidth
  37. } else {
  38. tempHeight = maxHeight
  39. tempWidth = (maxHeight * width / height)
  40. }
  41. if tempHeight.isNaN || tempWidth.isNaN {
  42. return CGSize.zero
  43. } else {
  44. return CGSize(width: tempWidth, height: tempHeight)
  45. }
  46. case .oneToOne:
  47. if maxWidth > maxHeight {
  48. return CGSize(width: maxHeight, height: maxHeight)
  49. } else {
  50. return CGSize(width: maxWidth, height: maxWidth)
  51. }
  52. case .sixteenToNine:
  53. return CGSize(width: maxWidth, height: maxWidth * 9.0 / 16.0)
  54. case .nineToSixteen:
  55. return CGSize(width: maxHeight * 9.0 / 16.0, height: maxHeight)
  56. default:
  57. break
  58. }
  59. return CGSize(width: maxHeight, height: maxHeight)
  60. }
  61. // 上传入口类型
  62. public var sourceType: videoUploadSourceType = .videoUpload
  63. // 制作视频项目Id
  64. public var makeVideoProjectId: String?
  65. // 再创作数据
  66. public var reCreateData: PQReCreateModel?
  67. // 视频创作埋点数据
  68. public var eventTrackData: PQVideoMakeEventTrackModel?
  69. // 制作视频草稿Id
  70. public var makeVideoDraftboxId: String?
  71. // 制作视频草稿结构化数据
  72. public var makeVideoSdata: String?
  73. public var isAssetImage: Bool = false // 是否请求的是图片
  74. public var videoWidth: CGFloat = 0 // 视频宽
  75. public var videoHeight: CGFloat = 0 // 视频高
  76. public var isLoop: Bool = true // 是否循环播放
  77. public var playerItem: AVPlayerItem? // 当前播放item
  78. public var videoOutput: AVPlayerItemVideoOutput?
  79. let itemSize = CGSize(width: ((cScreenWidth - cDefaultMargin) / 3) * UIScreen.main.scale, height: ((cScreenWidth - cDefaultMargin) / 3) * UIScreen.main.scale)
  80. public var categoryH: CGFloat = cDefaultMargin * 40 // 相簿高度
  81. public var videoData: [PQUploadModel] = Array<PQUploadModel>.init()
  82. public var categoryData: [PQUploadModel] = Array<PQUploadModel>.init()
  83. // 当前选中的数据
  84. public var selectedData: PQUploadModel?
  85. // 确定上传的数据
  86. public var uploadData: PQUploadModel?
  87. public var catagerySelectedIndex: IndexPath = IndexPath(item: 0, section: 0) // 更多图库选择
  88. public var isJumpToAuthorization: Bool = false // 是否跳到授权
  89. public var lastSeletedIndex: IndexPath? // 上次选中的index
  90. public lazy var imageManager: PHCachingImageManager = {
  91. (PHCachingImageManager.default() as? PHCachingImageManager) ?? PHCachingImageManager()
  92. }()
  93. public var allPhotos: PHFetchResult<PHAsset>!
  94. public var previousPreheatRect = CGRect.zero
  95. public var smartAlbums: PHFetchResult<PHAssetCollection>!
  96. public var userCollections: PHFetchResult<PHCollection>!
  97. public let sectionLocalizedTitles = ["", NSLocalizedString("Smart Albums", comment: ""), NSLocalizedString("Albums", comment: "")]
  98. public lazy var fetchOptions: PHFetchOptions = {
  99. let fetchOptions = PHFetchOptions()
  100. fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
  101. fetchOptions.predicate = NSPredicate(format: "mediaType = %d", !isAssetImage ? PHAssetMediaType.video.rawValue : PHAssetMediaType.image.rawValue)
  102. return fetchOptions
  103. }()
  104. public lazy var imagesOptions: PHImageRequestOptions = {
  105. let imagesOptions = PHImageRequestOptions()
  106. imagesOptions.isSynchronous = true
  107. imagesOptions.isNetworkAccessAllowed = false
  108. imagesOptions.deliveryMode = .opportunistic
  109. imagesOptions.resizeMode = .fast
  110. imagesOptions.progressHandler = { _, _, _, info in
  111. BFLog(message: "progressHandler = \(info)")
  112. }
  113. return imagesOptions
  114. }()
  115. public lazy var singleImageOptions: PHImageRequestOptions = {
  116. let singleImageOptions = PHImageRequestOptions()
  117. singleImageOptions.isSynchronous = true
  118. singleImageOptions.isNetworkAccessAllowed = false
  119. singleImageOptions.deliveryMode = .highQualityFormat
  120. return singleImageOptions
  121. }()
  122. public lazy var backBtn: UIButton = {
  123. let backBtn = UIButton(type: .custom)
  124. backBtn.frame = CGRect(x: 0, y: cDevice_iPhoneStatusBarHei, width: cDefaultMargin * 4, height: cDefaultMargin * 4)
  125. backBtn.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: -5, right: 0)
  126. backBtn.setImage(UIImage(named: "ic_close_black"), for: .normal)
  127. backBtn.addTarget(self, action: #selector(backBtnClick), for: .touchUpInside)
  128. return backBtn
  129. }()
  130. public var anthorEmptyData: BFEmptyModel? = {
  131. let anthorEmptyData = BFEmptyModel()
  132. anthorEmptyData.title = "开始上传"
  133. anthorEmptyData.summary = "要开始上传视频,请先授予相册使用权限"
  134. anthorEmptyData.emptyImageName = "icon_authorError"
  135. return anthorEmptyData
  136. }()
  137. public var emptyData: BFEmptyModel? = {
  138. let emptyData = BFEmptyModel()
  139. emptyData.title = "哦呜~ 你没有可上传的视频~"
  140. emptyData.emptyImageName = "video_empty"
  141. return emptyData
  142. }()
  143. public lazy var emptyRemindView: BFEmptyRemindView = {
  144. let emptyRemindView = BFEmptyRemindView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei))
  145. emptyRemindView.isHidden = true
  146. emptyRemindView.emptyData = anthorEmptyData
  147. view.addSubview(emptyRemindView)
  148. emptyRemindView.fullRefreshBloc = { [weak self, weak emptyRemindView] _, _ in
  149. self?.isJumpToAuthorization = true
  150. if emptyRemindView?.refreshBtn.currentTitle == "授予权限" {
  151. openAppSetting()
  152. }
  153. }
  154. return emptyRemindView
  155. }()
  156. public lazy var collectionView: UICollectionView = {
  157. let layout = UICollectionViewFlowLayout()
  158. layout.sectionInset = UIEdgeInsets.zero
  159. let collectionView = UICollectionView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei), collectionViewLayout: layout)
  160. collectionView.showsVerticalScrollIndicator = false
  161. collectionView.register(PQSelecteVideoItemCell.self, forCellWithReuseIdentifier: "PQSelecteVideoItemCell")
  162. collectionView.delegate = self
  163. collectionView.dataSource = self
  164. if #available(iOS 11.0, *) {
  165. collectionView.contentInsetAdjustmentBehavior = .never
  166. } else {
  167. automaticallyAdjustsScrollViewInsets = false
  168. }
  169. collectionView.backgroundColor = UIColor.hexColor(hexadecimal: "#191919")
  170. return collectionView
  171. }()
  172. public lazy var categoryCollectionView: UICollectionView = {
  173. let layout = UICollectionViewFlowLayout()
  174. layout.sectionInset = UIEdgeInsets.zero
  175. let categoryCollectionView = UICollectionView(frame: CGRect(x: 0, y: -categoryH, width: cScreenWidth, height: categoryH), collectionViewLayout: layout)
  176. categoryCollectionView.showsVerticalScrollIndicator = false
  177. categoryCollectionView.register(PQAssetCategoryCell.self, forCellWithReuseIdentifier: "PQAssetCategoryCell")
  178. categoryCollectionView.delegate = self
  179. categoryCollectionView.dataSource = self
  180. if #available(iOS 11.0, *) {
  181. categoryCollectionView.contentInsetAdjustmentBehavior = .never
  182. } else {
  183. automaticallyAdjustsScrollViewInsets = false
  184. }
  185. categoryCollectionView.backgroundColor = BFConfig.shared.editCoverimageSelectedbackgroundColor
  186. return categoryCollectionView
  187. }()
  188. public lazy var categoryView: UIView = {
  189. let categoryView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei))
  190. categoryView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
  191. let bgView = UIView(frame: CGRect(x: 0, y: categoryH, width: categoryView.frame.width, height: categoryView.frame.height - categoryH))
  192. let ges = UITapGestureRecognizer(target: self, action: #selector(cancelClick))
  193. bgView.addGestureRecognizer(ges)
  194. categoryView.addSubview(bgView)
  195. categoryView.isHidden = true
  196. return categoryView
  197. }()
  198. public lazy var avPlayer: AVPlayer = {
  199. let avPlayer = AVPlayer()
  200. NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem, queue: .main) { [weak self] notify in
  201. BFLog(message: "AVPlayerItemDidPlayToEndTime = \(notify)")
  202. avPlayer.seek(to: CMTime.zero)
  203. if self?.isLoop ?? false, bf_getCurrentViewController()?.isMember(of: PQUploadController.self) ?? false {
  204. if self?.playerLayer.superlayer == nil {
  205. self?.playerHeaderView.layer.insertSublayer(self!.playerLayer, at: 0)
  206. }
  207. avPlayer.play()
  208. } else {
  209. self?.sliderView.setValue(1.0, animated: false)
  210. self?.playBtn.isHidden = false
  211. }
  212. }
  213. NotificationCenter.default.addObserver(forName: .AVPlayerItemNewErrorLogEntry, object: avPlayer.currentItem, queue: .main) { notify in
  214. BFLog(message: "AVPlayerItemNewErrorLogEntry = \(notify)")
  215. }
  216. NotificationCenter.default.addObserver(forName: .AVPlayerItemFailedToPlayToEndTime, object: avPlayer.currentItem, queue: .main) { notify in
  217. BFLog(message: "AVPlayerItemFailedToPlayToEndTime = \(notify)")
  218. }
  219. NotificationCenter.default.addObserver(forName: .AVPlayerItemPlaybackStalled, object: avPlayer.currentItem, queue: .main) { notify in
  220. BFLog(message: "AVPlayerItemPlaybackStalled = \(notify)")
  221. }
  222. avPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 1000), queue: .main) { [weak self] _ in
  223. let progress = CMTimeGetSeconds(avPlayer.currentItem?.currentTime() ?? CMTime.zero) / CMTimeGetSeconds(avPlayer.currentItem?.duration ?? CMTime.zero)
  224. self?.sliderView.setValue(Float(progress), animated: false)
  225. if progress >= 1, !(self?.isLoop ?? false) {
  226. self?.sliderView.setValue(Float(progress), animated: false)
  227. self?.playBtn.isHidden = false
  228. }
  229. }
  230. return avPlayer
  231. }()
  232. public lazy var playerLayer: AVPlayerLayer = {
  233. let playerLayer = AVPlayerLayer(player: avPlayer)
  234. playerLayer.frame = CGRect(origin: CGPoint.zero, size: preViewSize)
  235. return playerLayer
  236. }()
  237. public lazy var sliderView: BFPlayerSlider = {
  238. let sliderView = BFPlayerSlider(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei + maxHeight, width: view.frame.width, height: cDefaultMargin))
  239. let thbImage = UIImage(named: "icon_point")
  240. sliderView.setMinimumTrackImage(thbImage, for: .normal)
  241. sliderView.setMaximumTrackImage(thbImage, for: .normal)
  242. sliderView.setThumbImage(thbImage, for: .highlighted)
  243. sliderView.setThumbImage(thbImage, for: .normal)
  244. sliderView.maximumTrackTintColor = UIColor.clear
  245. sliderView.addTarget(self, action: #selector(sliderValueDidChanged(sender:)), for: .valueChanged)
  246. sliderView.minimumTrackTintColor = UIColor.white
  247. sliderView.isHidden = true
  248. sliderView.backgroundColor = UIColor.black
  249. return sliderView
  250. }()
  251. public lazy var playBtn: UIButton = {
  252. let playBtn = UIButton(type: .custom)
  253. playBtn.frame = CGRect(x: (preViewSize.width - cDefaultMargin * 5) / 2, y: (preViewSize.height - cDefaultMargin * 5) / 2, width: cDefaultMargin * 5, height: cDefaultMargin * 5)
  254. playBtn.setImage(UIImage(named: "icon_video_play_big"), for: .normal)
  255. playBtn.tag = 4
  256. playBtn.isHidden = true
  257. playBtn.isUserInteractionEnabled = false
  258. // playBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  259. return playBtn
  260. }()
  261. public var playerHeaderView: UIImageView = {
  262. let playerHeaderView = UIImageView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: 0))
  263. playerHeaderView.isUserInteractionEnabled = true
  264. playerHeaderView.contentMode = .scaleAspectFit
  265. playerHeaderView.clipsToBounds = true
  266. playerHeaderView.backgroundColor = UIColor.black
  267. return playerHeaderView
  268. }()
  269. public lazy var selecteBtn: UIButton = {
  270. let selecteBtn = UIButton(frame: CGRect(x: deleteBtn.frame.maxX + cDefaultMargin, y: 0, width: cScreenWidth - nextBtn.frame.width - deleteBtn.frame.maxX - cDefaultMargin * 5, height: cDevice_iPhoneTabBarHei))
  271. selecteBtn.titleLabel?.lineBreakMode = .byTruncatingTail
  272. selecteBtn.setTitle("全部", for: .normal)
  273. selecteBtn.setImage(UIImage(named: "icon_uploadVideo_more"), for: .normal)
  274. selecteBtn.setTitleColor(UIColor.white, for: .normal)
  275. selecteBtn.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
  276. selecteBtn.tag = 2
  277. selecteBtn.imagePosition(at: .right, space: cDefaultMargin / 2)
  278. selecteBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  279. return selecteBtn
  280. }()
  281. public lazy var deleteBtn: UIButton = {
  282. let deleteBtn = UIButton(frame: CGRect(x: cDefaultMargin, y: 0, width: cDefaultMargin * 4, height: cDevice_iPhoneTabBarHei))
  283. deleteBtn.setImage(UIImage(named: "upload_delete"), for: .normal)
  284. deleteBtn.tag = 1
  285. deleteBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  286. return deleteBtn
  287. }()
  288. public lazy var nextBtn: UIButton = {
  289. let nextBtn = UIButton(frame: CGRect(x: 0, y: (cDevice_iPhoneTabBarHei - cDefaultMargin * 3) / 2, width: cDefaultMargin * 6, height: cDefaultMargin * 3))
  290. nextBtn.addCorner(corner: 3)
  291. nextBtn.setTitle("下一步", for: .normal)
  292. nextBtn.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .medium)
  293. nextBtn.tag = 3
  294. nextBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  295. nextBtn.backgroundColor = UIColor.hexColor(hexadecimal: "#333333")
  296. nextBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#999999"), for: .normal)
  297. return nextBtn
  298. }()
  299. public lazy var bottomView: UIView = {
  300. let bottomView = UIView(frame: CGRect(x: 0, y: cDefaultMargin * 2, width: cScreenWidth, height: cDevice_iPhoneNavBarHei))
  301. bottomView.addSubview(selecteBtn)
  302. bottomView.backgroundColor = UIColor.hexColor(hexadecimal: "#191919")
  303. selecteBtn.center.y = nextBtn.center.y
  304. bottomView.addSubview(deleteBtn)
  305. bottomView.addSubview(nextBtn)
  306. nextBtn.frame.origin.x = bottomView.frame.width - nextBtn.frame.width - cDefaultMargin * 2
  307. return bottomView
  308. }()
  309. open override func viewDidLoad() {
  310. super.viewDidLoad()
  311. view.backgroundColor = BFConfig.shared.editCoverimageSelectedbackgroundColor
  312. navHeadImageView?.backgroundColor = BFConfig.shared.editCoverimageSelectedbackgroundColor
  313. lineView?.backgroundColor = BFConfig.shared.editCoverimageSelectedbackgroundColor
  314. addSubViews()
  315. bottomView.backgroundColor = BFConfig.shared.editCoverimageSelectedbackgroundColor
  316. loadLocalData()
  317. }
  318. deinit {
  319. // PHPhotoLibrary.shared().unregisterChangeObserver(self)
  320. }
  321. open override func viewDidDisappear(_ animated: Bool) {
  322. super.viewDidDisappear(animated)
  323. if !isAssetImage {
  324. avPlayer.pause()
  325. playBtn.isHidden = false
  326. }
  327. }
  328. open override func viewWillAppear(_ animated: Bool) {
  329. super.viewDidAppear(animated)
  330. if !isAssetImage {
  331. if selectedData != nil {
  332. playBtn.isHidden = true
  333. if playerLayer.superlayer == nil {
  334. playerHeaderView.layer.insertSublayer(playerLayer, at: 0)
  335. }
  336. avPlayer.play()
  337. }
  338. }
  339. addPlayerItemObserver()
  340. }
  341. open override func viewWillDisappear(_ animated: Bool) {
  342. super.viewWillDisappear(animated)
  343. removePlayerItemObserver()
  344. }
  345. open func addSubViews() {
  346. view.addSubview(collectionView)
  347. navHeadImageView?.addSubview(bottomView)
  348. if !isAssetImage {
  349. view.addSubview(playerHeaderView)
  350. view.addSubview(sliderView)
  351. let ges = UITapGestureRecognizer(target: self, action: #selector(playPreVideo))
  352. playerHeaderView.addGestureRecognizer(ges)
  353. playerHeaderView.addSubview(playBtn)
  354. playerHeaderView.layer.insertSublayer(playerLayer, at: 0)
  355. }
  356. PQNotification.addObserver(self, selector: #selector(didBecomeActiveNotification), name: UIApplication.didBecomeActiveNotification, object: nil)
  357. }
  358. open func loadLocalData() {
  359. let authStatus = PHPhotoLibrary.authorizationStatus()
  360. if authStatus == .notDetermined {
  361. // 第一次触发授权 alert
  362. PHPhotoLibrary.requestAuthorization { [weak self] (status: PHAuthorizationStatus) -> Void in
  363. if status == .authorized {
  364. if (self?.allPhotos == nil) || (self?.allPhotos.count ?? 0) <= 0 {
  365. self?.loadPhotoData()
  366. }
  367. if self?.backBtn != nil {
  368. self?.backBtn.removeFromSuperview()
  369. }
  370. } else {
  371. DispatchQueue.main.async { [weak self] in
  372. self?.emptyRemindView.isHidden = false
  373. self?.emptyRemindView.refreshBtn.isHidden = false
  374. self?.emptyRemindView.refreshBtn.setTitle("授予权限", for: .normal)
  375. if self?.backBtn.superview == nil {
  376. self?.view.addSubview((self?.backBtn)!)
  377. } else {
  378. self?.backBtn.isHidden = false
  379. }
  380. }
  381. }
  382. }
  383. } else if authStatus == .authorized {
  384. if allPhotos == nil || allPhotos.count <= 0 {
  385. loadPhotoData()
  386. }
  387. if backBtn.superview != nil {
  388. backBtn.removeFromSuperview()
  389. }
  390. } else {
  391. emptyRemindView.isHidden = false
  392. emptyRemindView.refreshBtn.isHidden = false
  393. emptyRemindView.refreshBtn.setTitle("授予权限", for: .normal)
  394. if backBtn.superview == nil {
  395. view.addSubview(backBtn)
  396. } else {
  397. backBtn.isHidden = false
  398. }
  399. }
  400. }
  401. open func loadPhotoData() {
  402. DispatchQueue.main.async { [weak self] in
  403. BFLoadingHUB.shared.showHUB(superView: self!.view, isVerticality: false)
  404. }
  405. DispatchQueue.global().async { [weak self] in
  406. self?.allPhotos = PHAsset.fetchAssets(with: self?.fetchOptions)
  407. DispatchQueue.main.async { [weak self] in
  408. if self?.view != nil {
  409. BFLoadingHUB.shared.dismissHUB(superView: self!.view)
  410. }
  411. self?.collectionView.reloadData()
  412. if (self?.allPhotos.count ?? 0) <= 0 {
  413. self?.emptyRemindView.frame = CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei - cDevice_iPhoneTabBarHei)
  414. self?.emptyRemindView.emptyData = self?.emptyData
  415. self?.emptyRemindView.isHidden = false
  416. } else {
  417. self?.emptyRemindView.isHidden = true
  418. }
  419. if (self?.allPhotos.count ?? 0) > 0 {
  420. let tempData = PQUploadModel()
  421. tempData.title = "全部"
  422. tempData.categoryList = self!.allPhotos
  423. self?.categoryData.insert(tempData, at: 0)
  424. }
  425. self?.updateCachedAssets()
  426. }
  427. }
  428. PHPhotoLibrary.shared().register(self)
  429. DispatchQueue.global().async { [weak self] in
  430. self?.smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil)
  431. self?.smartAlbums?.enumerateObjects { [weak self] assCollection, _, _ in
  432. if assCollection.localizedTitle != "最近删除" {
  433. self?.convertCollection(collection: assCollection)
  434. }
  435. }
  436. self?.userCollections = PHCollectionList.fetchTopLevelUserCollections(with: nil)
  437. self?.userCollections.enumerateObjects { assCollection, index, point in
  438. BFLog(message: "userCollections == \(assCollection),index = \(index),point = \(point)")
  439. if assCollection is PHAssetCollection {
  440. if assCollection.localizedTitle != "最近删除" {
  441. self?.convertCollection(collection: assCollection as? PHAssetCollection)
  442. }
  443. }
  444. }
  445. }
  446. if !isAssetImage {
  447. // 视频上传相关上报
  448. PQEventTrackViewModel.baseReportUpload(businessType: .bt_pageView, objectType: .ot_pageView, pageSource: .sp_upload_videoSelect, extParams: ["source": videoUploadSourceType.videoUpload.rawValue, "projectId": "", "draftboxId": ""], remindmsg: "上传相关")
  449. }
  450. }
  451. // 转化处理获取到的相簿
  452. open func convertCollection(collection: PHAssetCollection?) {
  453. if collection == nil {
  454. return
  455. }
  456. DispatchQueue.global().async { [weak self] in
  457. let assetsFetchResult = PHAsset.fetchAssets(in: collection!, options: self?.fetchOptions)
  458. if assetsFetchResult.count > 0 {
  459. let tempData = PQUploadModel()
  460. tempData.title = collection?.localizedTitle
  461. tempData.categoryList = assetsFetchResult
  462. if tempData.categoryList.count > 0 {
  463. self?.categoryData.append(tempData)
  464. }
  465. BFLog(message: "assetsFetchResult = \(assetsFetchResult)")
  466. }
  467. }
  468. }
  469. @objc open func btnClick(sender: UIButton) {
  470. switch sender.tag {
  471. case 1: // 返回
  472. navigationController?.popViewController(animated: true)
  473. if !isAssetImage {
  474. // 视频上传相关上报
  475. // MARK: SanW--待修改-2021.11.08
  476. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_up_backBtn, pageSource: .sp_upload_videoSelect, extParams: ["source": videoUploadSourceType.videoUpload.rawValue, "projectId": "", "draftboxId":""], remindmsg: "上传相关")
  477. }
  478. case 2: // 筛选
  479. showCollects()
  480. case 3: // 下一步
  481. if selectedData == nil {
  482. cShowHUB(superView: nil, msg: isAssetImage ? "请选择图片" : "请选择视频")
  483. return
  484. }
  485. if !isAssetImage {
  486. avPlayer.pause()
  487. playBtn.isHidden = false
  488. if jumptoPublicHandle != nil {
  489. uploadData?.videoFromScene = .UploadNormal
  490. jumptoPublicHandle!(selectedData)
  491. }
  492. // 视频上传相关上报
  493. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_up_nextBtn, pageSource: .sp_upload_videoSelect, extParams: ["source": videoUploadSourceType.videoUpload.rawValue, "projectId": "", "draftboxId": ""], remindmsg: "上传相关")
  494. return
  495. }
  496. imageManager.requestImage(for: (selectedData?.asset)!, targetSize: itemSize, contentMode: .aspectFill, options: nil) { [weak self] image, _ in
  497. self?.selectedData?.image = image
  498. let vc = PQImageCropVC(image: (self?.selectedData?.image)!, aspectWidth: self?.videoWidth ?? 0.0, aspectHeight: self?.videoHeight ?? 0.0)
  499. vc.uploadData = self?.selectedData
  500. self?.navigationController?.pushViewController(vc, animated: true)
  501. }
  502. case 4: // 播放
  503. playBtn.isHidden = true
  504. if playerLayer.superlayer == nil {
  505. playerHeaderView.layer.insertSublayer(playerLayer, at: 0)
  506. }
  507. avPlayer.play()
  508. default:
  509. break
  510. }
  511. }
  512. @objc public func showCollects() {
  513. if categoryData.count <= 0 {
  514. return
  515. }
  516. categoryCollectionView.reloadData()
  517. if categoryView.superview == nil {
  518. view.insertSubview(categoryView, belowSubview: navHeadImageView!)
  519. categoryView.addSubview(categoryCollectionView)
  520. showCategoryView()
  521. return
  522. }
  523. if categoryView.isHidden {
  524. showCategoryView()
  525. } else {
  526. cancelClick()
  527. }
  528. }
  529. @objc public func showCategoryView() {
  530. categoryView.isHidden = false
  531. categoryView.alpha = 0
  532. view.bringSubviewToFront(categoryView)
  533. view.bringSubviewToFront(bottomView)
  534. UIView.animate(withDuration: 0.3, animations: {
  535. self.categoryCollectionView.frame = CGRect(x: 0, y: 0, width: cScreenWidth, height: self.categoryH)
  536. self.categoryView.alpha = 1
  537. }) { _ in
  538. }
  539. }
  540. @objc func cancelClick() {
  541. UIView.animate(withDuration: 0.3, animations: {
  542. self.categoryCollectionView.frame = CGRect(x: 0, y: -self.categoryH, width: cScreenWidth, height: self.categoryH)
  543. self.categoryView.alpha = 0
  544. }) { _ in
  545. self.categoryView.isHidden = true
  546. }
  547. }
  548. @objc open func playPreVideo() {
  549. playBtn.isHidden = !playBtn.isHidden
  550. if playBtn.isHidden {
  551. if playerLayer.superlayer == nil {
  552. playerHeaderView.layer.insertSublayer(playerLayer, at: 0)
  553. }
  554. avPlayer.play()
  555. } else {
  556. avPlayer.pause()
  557. }
  558. }
  559. @objc func sliderValueDidChanged(sender: UISlider) {
  560. avPlayer.seek(to: CMTime(value: CMTimeValue(sender.value * Float(CMTimeGetSeconds(avPlayer.currentItem?.duration ?? CMTime.zero))), timescale: 1))
  561. }
  562. @objc open func didBecomeActiveNotification() {
  563. if isJumpToAuthorization {
  564. loadLocalData()
  565. isJumpToAuthorization = false
  566. }
  567. }
  568. open func isPublishEnabled() {
  569. if selectedData != nil {
  570. nextBtn.backgroundColor = UIColor.hexColor(hexadecimal: "#EE0051")
  571. nextBtn.setTitleColor(UIColor.white, for: .normal)
  572. } else {
  573. nextBtn.backgroundColor = UIColor.hexColor(hexadecimal: "#333333")
  574. nextBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#999999"), for: .normal)
  575. }
  576. }
  577. }
  578. extension PQUploadController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UIScrollViewDelegate {
  579. open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection _: Int) -> Int {
  580. if collectionView == self.collectionView {
  581. return allPhotos == nil ? videoData.count : allPhotos.count
  582. }
  583. return categoryData.count
  584. }
  585. open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  586. if collectionView == self.collectionView {
  587. let cell = PQSelecteVideoItemCell.selecteVideoItemCell(collectionView: collectionView, indexPath: indexPath)
  588. if videoData.count <= indexPath.item, allPhotos != nil {
  589. let itemData = PQUploadModel()
  590. // itemData.image = UIImage.init(named: "cut_place")
  591. let asset = allPhotos.object(at: indexPath.item)
  592. itemData.asset = asset
  593. itemData.duration = asset.duration
  594. videoData.append(itemData)
  595. }
  596. if videoData.count > indexPath.item {
  597. let itemData = videoData[indexPath.item]
  598. cell.uploadData = itemData
  599. if itemData.image == nil, itemData.asset != nil {
  600. cell.representedAssetIdentifier = itemData.asset?.localIdentifier
  601. imageManager.requestImage(for: itemData.asset!, targetSize: itemSize, contentMode: .aspectFill, options: nil) {[weak self, weak cell] image, info in
  602. if info?.keys.contains("PHImageResultIsDegradedKey") ?? false, "\(info?["PHImageResultIsDegradedKey"] ?? "0")" == "0", cell?.representedAssetIdentifier == itemData.asset?.localIdentifier {
  603. if image != nil {
  604. itemData.image = image
  605. cell?.videoImageView.image = image
  606. } else if image == nil, info?.keys.contains("PHImageResultIsInCloudKey") ?? false {
  607. let option = PHImageRequestOptions()
  608. option.isNetworkAccessAllowed = true
  609. option.resizeMode = .fast
  610. self?.imageManager.requestImageData(for: itemData.asset!, options: option) {[weak cell] data, _, _, _ in
  611. if data != nil {
  612. let image = UIImage(data: data!)
  613. itemData.image = image
  614. cell?.videoImageView.image = image
  615. }
  616. }
  617. }
  618. }
  619. }
  620. }
  621. } else {
  622. cell.uploadData = PQUploadModel()
  623. }
  624. return cell
  625. } else {
  626. let cell = PQAssetCategoryCell.assetCategoryCell(collectionView: collectionView, indexPath: indexPath)
  627. let itemData = categoryData[indexPath.item]
  628. let asset = itemData.categoryList.object(at: 0)
  629. if itemData.image == nil {
  630. cell.representedAssetIdentifier = asset.localIdentifier
  631. imageManager.requestImage(for: asset, targetSize: itemSize, contentMode: .aspectFill, options: nil) {[weak cell] image, info in
  632. if info?.keys.contains("PHImageResultIsDegradedKey") ?? false, "\(info?["PHImageResultIsDegradedKey"] ?? "0")" == "0", cell?.representedAssetIdentifier == asset.localIdentifier {
  633. itemData.image = image
  634. cell?.uploadData = itemData
  635. }
  636. }
  637. } else {
  638. cell.uploadData = itemData
  639. }
  640. cell.isSelected = indexPath == catagerySelectedIndex
  641. return cell
  642. }
  643. }
  644. open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  645. if collectionView == self.collectionView {
  646. if videoData.count <= indexPath.item {
  647. return
  648. }
  649. let itemData = videoData[indexPath.item]
  650. if !isAssetImage, (itemData.asset?.duration ?? 0) < 5.0 {
  651. cShowHUB(superView: nil, msg: "请选择大于5s的视频")
  652. return
  653. }
  654. let ratio = Float(itemData.asset?.pixelWidth ?? 0) / Float(itemData.asset?.pixelHeight ?? 1)
  655. if ratio < 0.4 || ratio > 4.2 {
  656. cShowHUB(superView: nil, msg: "暂不支持该比例的素材")
  657. return
  658. }
  659. if lastSeletedIndex != nil, lastSeletedIndex != indexPath {
  660. let lastData = videoData[lastSeletedIndex!.item]
  661. lastData.isSelected = false
  662. collectionView.reloadItems(at: [lastSeletedIndex!])
  663. }
  664. lastSeletedIndex = indexPath
  665. if itemData.isSelected {
  666. itemData.isSelected = false
  667. selectedData = nil
  668. if !isAssetImage {
  669. avPlayer.pause()
  670. playBtn.isHidden = false
  671. sliderView.isHidden = true
  672. UIView.animate(withDuration: 0.5, animations: { [weak self] in
  673. self?.playerHeaderView.frame = CGRect(x: 0, y: -(self?.preViewSize.height ?? 0) - cDevice_iPhoneNavBarAndStatusBarHei, width: self?.preViewSize.width ?? 0, height: self?.preViewSize.height ?? 0)
  674. self?.sliderView.frame.origin.y = self?.playerHeaderView.frame.maxY ?? 0
  675. self?.playerHeaderView.center.x = self?.view.center.x ?? 0
  676. self?.collectionView.frame = CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei)
  677. }) { _ in
  678. }
  679. }
  680. } else {
  681. itemData.isSelected = true
  682. let itemData = videoData[indexPath.item]
  683. if !isAssetImage {
  684. selectedData = nil
  685. sliderView.setValue(0, animated: false)
  686. // 加载中...
  687. BFLoadingHUB.shared.showHUB(superView: playerHeaderView)
  688. avPlayer.pause()
  689. avPlayer.replaceCurrentItem(with: nil)
  690. playerLayer.removeFromSuperlayer()
  691. PQPHAssetVideoParaseUtil.parasToAVPlayerItem(phAsset: itemData.asset!, isHighQuality: true) { [weak self] playerItem, fileSize, info in
  692. if playerItem == nil || fileSize > maxUploadSize {
  693. if fileSize > maxUploadSize {
  694. cShowHUB(superView: nil, msg: "请选择小于10G的视频")
  695. } else {
  696. cShowHUB(superView: nil, msg: (info != nil && (info?.keys.contains("PHImageResultIsInCloudKey") ?? false) && "\(info?["PHImageResultIsInCloudKey"] ?? "1")" == "1") ? "暂不支持iCloud中的视频" : "此视频已损坏或已删除无法播放")
  697. }
  698. self?.videoData[(self?.lastSeletedIndex?.item)!].isSelected = false
  699. self?.collectionView.reloadItems(at: [(self?.lastSeletedIndex)!])
  700. self?.lastSeletedIndex = nil
  701. self?.selectedData = nil
  702. DispatchQueue.main.async { [weak self] in
  703. self?.sliderView.isHidden = true
  704. if (self?.playerHeaderView.frame.minY ?? 0.0) >= 0.0 {
  705. UIView.animate(withDuration: 0.5, animations: { [weak self] in
  706. self?.playerHeaderView.frame = CGRect(x: 0, y: -(self?.preViewSize.height ?? 0) - cDevice_iPhoneNavBarAndStatusBarHei, width: self?.preViewSize.width ?? 0, height: self?.preViewSize.height ?? 0)
  707. self?.sliderView.frame.origin.y = self?.playerHeaderView.frame.maxY ?? 0
  708. self?.playerHeaderView.center.x = self?.view.center.x ?? 0
  709. self?.collectionView.frame = CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei)
  710. }) { _ in
  711. }
  712. }
  713. }
  714. // 加载中...
  715. if self?.playerHeaderView != nil {
  716. BFLoadingHUB.shared.dismissHUB(superView: (self?.playerHeaderView)!)
  717. }
  718. return
  719. }
  720. DispatchQueue.main.async { [weak self] in
  721. self?.selectedData = itemData
  722. self?.selectedData?.localPath = (playerItem?.asset as! AVURLAsset).url.absoluteString
  723. self?.selectedData?.duration = (playerItem?.asset as! AVURLAsset).duration.seconds
  724. // 移除监听
  725. self?.removePlayerItemObserver()
  726. self?.playerItem = playerItem
  727. // 添加监听
  728. self?.addPlayerItemObserver()
  729. self?.playBtn.isHidden = true
  730. self?.avPlayer.replaceCurrentItem(with: playerItem)
  731. if self?.playerLayer.superlayer == nil {
  732. self?.playerHeaderView.layer.insertSublayer(self!.playerLayer, at: 0)
  733. }
  734. self?.avPlayer.play()
  735. // self?.playerHeaderView.image = itemData.image
  736. let tracks = (playerItem?.asset as? AVURLAsset)?.tracks(withMediaType: .video)
  737. if tracks != nil, (tracks?.count ?? 0) > 0 {
  738. let videoTrack = tracks?.first
  739. let transform = videoTrack?.preferredTransform
  740. let width: CGFloat = CGFloat(transform?.a ?? 0.0) * CGFloat(videoTrack?.naturalSize.width ?? 0.0) + CGFloat(transform?.c ?? 0.0) * CGFloat(videoTrack?.naturalSize.height ?? 0.0)
  741. let height: CGFloat = CGFloat(transform?.b ?? 0.0) * CGFloat(videoTrack?.naturalSize.width ?? 0.0) + CGFloat(transform?.d ?? 0.0) * CGFloat(videoTrack?.naturalSize.height ?? 0.0)
  742. self?.selectedData?.videoWidth = abs(width)
  743. self?.selectedData?.videoHeight = abs(height)
  744. } else {
  745. self?.selectedData?.videoWidth = CGFloat(self?.selectedData?.asset?.pixelWidth ?? 0)
  746. self?.selectedData?.videoHeight = CGFloat(self?.selectedData?.asset?.pixelHeight ?? 0)
  747. }
  748. self?.isPublishEnabled()
  749. self?.playerHeaderView.frame = CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: self?.preViewSize.width ?? 0, height: self?.preViewSize.height ?? 0)
  750. if self?.playerHeaderView != nil {
  751. BFLoadingHUB.shared.showHUB(superView: (self?.playerHeaderView)!)
  752. }
  753. self?.playerHeaderView.center.x = self?.view.center.x ?? 0
  754. self?.sliderView.frame.origin.y = self?.playerHeaderView.frame.maxY ?? 0
  755. self?.collectionView.frame = CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei + CGFloat(self?.preViewSize.height ?? 0) + cDefaultMargin * 1.5, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei - CGFloat(self?.preViewSize.height ?? 0) - cDefaultMargin * 1.5)
  756. self?.collectionView.scrollToItem(at: indexPath, at: .top, animated: true)
  757. self?.sliderView.isHidden = false
  758. self?.playBtn.frame.origin = CGPoint(x: (CGFloat(self?.preViewSize.width ?? 0) - CGFloat(cDefaultMargin * 5)) / 2, y: (CGFloat(self?.preViewSize.height ?? 0) - CGFloat(cDefaultMargin * 5)) / 2)
  759. self?.playerLayer.frame = self?.playerHeaderView.bounds ?? CGRect.zero
  760. }
  761. }
  762. } else {
  763. selectedData = itemData
  764. }
  765. }
  766. isPublishEnabled()
  767. collectionView.reloadItems(at: [indexPath])
  768. } else {
  769. videoData.removeAll()
  770. self.collectionView.setContentOffset(CGPoint.zero, animated: true)
  771. self.collectionView.reloadData()
  772. allPhotos = categoryData[indexPath.item].categoryList
  773. catagerySelectedIndex = indexPath
  774. selecteBtn.setTitle(categoryData[indexPath.item].title, for: .normal)
  775. selecteBtn.imagePosition(at: .right, space: cDefaultMargin / 2)
  776. if !isAssetImage {
  777. playBtn.isHidden = false
  778. sliderView.isHidden = true
  779. avPlayer.pause()
  780. if selectedData != nil {
  781. selectedData = nil
  782. UIView.animate(withDuration: 0.5, animations: { [weak self] in
  783. self?.playerHeaderView.frame = CGRect(x: 0, y: -(self?.preViewSize.height ?? 0) - cDevice_iPhoneNavBarAndStatusBarHei, width: self?.preViewSize.width ?? 0, height: self?.preViewSize.height ?? 0)
  784. self?.playerHeaderView.center.x = self?.view.center.x ?? 0
  785. self?.sliderView.frame.origin.y = self?.playerHeaderView.frame.maxY ?? 0
  786. self?.collectionView.frame = CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei)
  787. }) { _ in
  788. }
  789. }
  790. }
  791. lastSeletedIndex = nil
  792. isPublishEnabled()
  793. if allPhotos.count <= 0 {
  794. emptyRemindView.frame = CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei - cDevice_iPhoneTabBarHei)
  795. emptyRemindView.emptyData = emptyData
  796. emptyRemindView.isHidden = false
  797. } else {
  798. emptyRemindView.isHidden = true
  799. }
  800. self.collectionView.reloadData()
  801. cancelClick()
  802. }
  803. }
  804. open func collectionView(_ collectionView: UICollectionView, layout _: UICollectionViewLayout, sizeForItemAt _: IndexPath) -> CGSize {
  805. if collectionView == self.collectionView {
  806. return CGSize(width: (cScreenWidth - cDefaultMargin) / 3, height: (cScreenWidth - cDefaultMargin) / 3)
  807. }
  808. return CGSize(width: collectionView.frame.width, height: cDefaultMargin * 8)
  809. }
  810. open func collectionView(_ collectionView: UICollectionView, layout _: UICollectionViewLayout, minimumLineSpacingForSectionAt _: Int) -> CGFloat {
  811. if collectionView == self.collectionView {
  812. return cDefaultMargin / 2
  813. }
  814. return 0
  815. }
  816. open func collectionView(_ collectionView: UICollectionView, layout _: UICollectionViewLayout, minimumInteritemSpacingForSectionAt _: Int) -> CGFloat {
  817. if collectionView == self.collectionView {
  818. return cDefaultMargin / 2
  819. }
  820. return 0
  821. }
  822. open func scrollViewDidScroll(_: UIScrollView) {
  823. // 这里是不是有用的?
  824. // if bf_getCurrentViewController() is PQUploadController || bf_getCurrentViewController() is PQImageSelectedController {
  825. // if scrollView == collectionView {
  826. // updateCachedAssets()
  827. // } else {
  828. // BFLog(message: "contentOffset = \(scrollView.contentOffset),contentSize = \(scrollView.contentSize)")
  829. // if scrollView.contentOffset.y > ((scrollView.contentSize.height - scrollView.frame.height) + cDefaultMargin * 10) {
  830. // cancelClick()
  831. // }
  832. // }
  833. // }
  834. }
  835. }
  836. extension PQUploadController {
  837. /// 添加监听
  838. /// - Parameter playerItem: <#playerItem description#>
  839. /// - Returns: <#description#>
  840. func addPlayerItemObserver() {
  841. playerItem?.addObserver(self, forKeyPath: "status", options: .new, context: nil)
  842. }
  843. /// 移除监听
  844. /// - Returns: <#description#>
  845. func removePlayerItemObserver() {
  846. playerItem?.removeObserver(self, forKeyPath: "status")
  847. }
  848. open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
  849. if object is AVPlayerItem, keyPath == "status" {
  850. BFLog(message: "(object as! AVPlayerItem).status = \((object as! AVPlayerItem).status.rawValue)")
  851. BFLoadingHUB.shared.dismissHUB(superView: playerHeaderView)
  852. switch (object as! AVPlayerItem).status {
  853. case .unknown:
  854. break
  855. case .readyToPlay:
  856. break
  857. case .failed:
  858. break
  859. default:
  860. break
  861. }
  862. }
  863. }
  864. private func resetCachedAssets() {
  865. imageManager.stopCachingImagesForAllAssets()
  866. previousPreheatRect = .zero
  867. }
  868. private func updateCachedAssets() {
  869. if allPhotos != nil, allPhotos.count <= 0 {
  870. return
  871. }
  872. guard isViewLoaded, view.window != nil else { return }
  873. let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
  874. let preheatRect = visibleRect.insetBy(dx: 0, dy: -0.5 * visibleRect.height)
  875. let delta = abs(preheatRect.midY - previousPreheatRect.midY)
  876. guard delta > view.bounds.height / 3 else { return }
  877. let (addedRects, removedRects) = differencesBetweenRects(previousPreheatRect, preheatRect)
  878. let addedAssets = addedRects
  879. .flatMap { rect in collectionView.indexPathsForElements(in: rect) }
  880. .map { indexPath in allPhotos.object(at: indexPath.item) }
  881. let removedAssets = removedRects
  882. .flatMap { rect in collectionView.indexPathsForElements(in: rect) }
  883. .map { indexPath in allPhotos.object(at: indexPath.item) }
  884. imageManager.startCachingImages(for: addedAssets,
  885. targetSize: itemSize, contentMode: .aspectFill, options: nil)
  886. imageManager.stopCachingImages(for: removedAssets,
  887. targetSize: itemSize, contentMode: .aspectFill, options: nil)
  888. previousPreheatRect = preheatRect
  889. }
  890. private func differencesBetweenRects(_ old: CGRect, _ new: CGRect) -> (added: [CGRect], removed: [CGRect]) {
  891. if old.intersects(new) {
  892. var added = [CGRect]()
  893. if new.maxY > old.maxY {
  894. added += [CGRect(x: new.origin.x, y: old.maxY,
  895. width: new.width, height: new.maxY - old.maxY)]
  896. }
  897. if old.minY > new.minY {
  898. added += [CGRect(x: new.origin.x, y: new.minY,
  899. width: new.width, height: old.minY - new.minY)]
  900. }
  901. var removed = [CGRect]()
  902. if new.maxY < old.maxY {
  903. removed += [CGRect(x: new.origin.x, y: new.maxY,
  904. width: new.width, height: old.maxY - new.maxY)]
  905. }
  906. if old.minY < new.minY {
  907. removed += [CGRect(x: new.origin.x, y: old.minY,
  908. width: new.width, height: new.minY - old.minY)]
  909. }
  910. return (added, removed)
  911. } else {
  912. return ([new], [old])
  913. }
  914. }
  915. }
  916. extension PQUploadController: PHPhotoLibraryChangeObserver {
  917. public func photoLibraryDidChange(_ changeInstance: PHChange) {
  918. // Change notifications may be made on a background queue. Re-dispatch to the
  919. // main queue before acting on the change as we'll be updating the UI.
  920. DispatchQueue.main.sync { [weak self] in
  921. // Check each of the three top-level fetches for changes.
  922. if allPhotos != nil, changeInstance.changeDetails(for: allPhotos) != nil {
  923. self?.categoryData.removeAll()
  924. self?.loadPhotoData()
  925. }
  926. }
  927. }
  928. }