PQUploadController.swift 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  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 BFUIKit
  12. let playerHeaderH: CGFloat = cScreenWidth * (250 / 375)
  13. class PQUploadController: BFBaseViewController {
  14. // 最大的宽度
  15. var maxWidth: CGFloat = cScreenWidth
  16. // 最大的高度
  17. var maxHeight: CGFloat = adapterWidth(width: 300)
  18. // 画面比例
  19. var aspectRatio: aspectRatio?
  20. var preViewSize: CGSize {
  21. if aspectRatio == nil, (isMember(of: PQUploadController.self) && selectedData == nil) || (!isMember(of: PQUploadController.self) && uploadData == nil) {
  22. return CGSize(width: maxHeight, height: maxHeight)
  23. }
  24. if selectedData != nil {
  25. aspectRatio = .origin(width: CGFloat(videoData[lastSeletedIndex?.item ?? 0].videoWidth), height: CGFloat(videoData[lastSeletedIndex?.item ?? 0].videoHeight))
  26. } else if aspectRatio == nil {
  27. aspectRatio = .origin(width: CGFloat(uploadData?.videoWidth ?? maxHeight), height: CGFloat(uploadData?.videoHeight ?? maxHeight))
  28. }
  29. switch aspectRatio {
  30. case let .origin(width, height):
  31. var tempHeight: CGFloat = 0
  32. var tempWidth: CGFloat = 0
  33. if width > height {
  34. tempHeight = (maxWidth * height / width)
  35. tempWidth = maxWidth
  36. } else {
  37. tempHeight = maxHeight
  38. tempWidth = (maxHeight * width / height)
  39. }
  40. if tempHeight.isNaN || tempWidth.isNaN {
  41. return CGSize.zero
  42. } else {
  43. return CGSize(width: tempWidth, height: tempHeight)
  44. }
  45. case .oneToOne:
  46. if maxWidth > maxHeight {
  47. return CGSize(width: maxHeight, height: maxHeight)
  48. } else {
  49. return CGSize(width: maxWidth, height: maxWidth)
  50. }
  51. case .sixteenToNine:
  52. return CGSize(width: maxWidth, height: maxWidth * 9.0 / 16.0)
  53. case .nineToSixteen:
  54. return CGSize(width: maxHeight * 9.0 / 16.0, height: maxHeight)
  55. default:
  56. break
  57. }
  58. return CGSize(width: maxHeight, height: maxHeight)
  59. }
  60. // 上传入口类型
  61. var sourceType: videoUploadSourceType = .videoUpload
  62. // 制作视频项目Id
  63. var makeVideoProjectId: String?
  64. // 再创作数据
  65. var reCreateData: PQReCreateModel?
  66. // 视频创作埋点数据
  67. var eventTrackData: PQVideoMakeEventTrackModel?
  68. // 制作视频草稿Id
  69. var makeVideoDraftboxId: String?
  70. // 制作视频草稿结构化数据
  71. var makeVideoSdata: String?
  72. var isAssetImage: Bool = false // 是否请求的是图片
  73. var videoWidth: CGFloat = 0 // 视频宽
  74. var videoHeight: CGFloat = 0 // 视频高
  75. var isLoop: Bool = true // 是否循环播放
  76. var playerItem: AVPlayerItem? // 当前播放item
  77. var videoOutput: AVPlayerItemVideoOutput?
  78. let itemSize = CGSize(width: ((cScreenWidth - cDefaultMargin) / 3) * UIScreen.main.scale, height: ((cScreenWidth - cDefaultMargin) / 3) * UIScreen.main.scale)
  79. var categoryH: CGFloat = cDefaultMargin * 40 // 相簿高度
  80. var videoData: [PQUploadModel] = Array<PQUploadModel>.init()
  81. var categoryData: [PQUploadModel] = Array<PQUploadModel>.init()
  82. // 当前选中的数据
  83. var selectedData: PQUploadModel?
  84. // 确定上传的数据
  85. var uploadData: PQUploadModel?
  86. var catagerySelectedIndex: IndexPath = IndexPath(item: 0, section: 0) // 更多图库选择
  87. var isJumpToAuthorization: Bool = false // 是否跳到授权
  88. var lastSeletedIndex: IndexPath? // 上次选中的index
  89. lazy var imageManager: PHCachingImageManager = {
  90. (PHCachingImageManager.default() as? PHCachingImageManager) ?? PHCachingImageManager()
  91. }()
  92. var allPhotos: PHFetchResult<PHAsset>!
  93. var previousPreheatRect = CGRect.zero
  94. var smartAlbums: PHFetchResult<PHAssetCollection>!
  95. var userCollections: PHFetchResult<PHCollection>!
  96. let sectionLocalizedTitles = ["", NSLocalizedString("Smart Albums", comment: ""), NSLocalizedString("Albums", comment: "")]
  97. lazy var fetchOptions: PHFetchOptions = {
  98. let fetchOptions = PHFetchOptions()
  99. fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
  100. fetchOptions.predicate = NSPredicate(format: "mediaType = %d", !isAssetImage ? PHAssetMediaType.video.rawValue : PHAssetMediaType.image.rawValue)
  101. return fetchOptions
  102. }()
  103. lazy var imagesOptions: PHImageRequestOptions = {
  104. let imagesOptions = PHImageRequestOptions()
  105. imagesOptions.isSynchronous = true
  106. imagesOptions.isNetworkAccessAllowed = false
  107. imagesOptions.deliveryMode = .opportunistic
  108. imagesOptions.resizeMode = .fast
  109. imagesOptions.progressHandler = { _, _, _, info in
  110. BFLog(message: "progressHandler = \(info)")
  111. }
  112. return imagesOptions
  113. }()
  114. lazy var singleImageOptions: PHImageRequestOptions = {
  115. let singleImageOptions = PHImageRequestOptions()
  116. singleImageOptions.isSynchronous = true
  117. singleImageOptions.isNetworkAccessAllowed = false
  118. singleImageOptions.deliveryMode = .highQualityFormat
  119. return singleImageOptions
  120. }()
  121. lazy var backBtn: UIButton = {
  122. let backBtn = UIButton(type: .custom)
  123. backBtn.frame = CGRect(x: 0, y: cDevice_iPhoneStatusBarHei, width: cDefaultMargin * 4, height: cDefaultMargin * 4)
  124. backBtn.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: -5, right: 0)
  125. backBtn.setImage(UIImage.moduleImage(named: "icon_blanc_back", moduleName: "BFFramework",isAssets: false)?.withRenderingMode(.alwaysTemplate), for: .normal)
  126. backBtn.addTarget(self, action: #selector(backBtnClick), for: .touchUpInside)
  127. backBtn.imageView?.tintColor = PQBFConfig.shared.styleTitleColor
  128. return backBtn
  129. }()
  130. var anthorEmptyData: PQEmptyModel? = {
  131. let anthorEmptyData = PQEmptyModel()
  132. anthorEmptyData.title = "开始上传"
  133. anthorEmptyData.summary = "要开始上传视频,请先授予相册使用权限"
  134. anthorEmptyData.emptyImageName = "icon_authorError"
  135. return anthorEmptyData
  136. }()
  137. var emptyData: PQEmptyModel? = {
  138. let emptyData = PQEmptyModel()
  139. emptyData.title = "哦呜~ 你没有可上传的视频~"
  140. emptyData.emptyImageName = "video_empty"
  141. emptyData.netDisRefreshBgColor = UIColor.hexColor(hexadecimal: "#FA6400")
  142. emptyData.netDisTitle = "内容加载失败"
  143. emptyData.netDisTitleColor = UIColor.hexColor(hexadecimal: "#333333")
  144. emptyData.netemptyDisImage = UIImage.init(named: "empty_netDis_icon")
  145. emptyData.netDisRefreshTitle = NSMutableAttributedString(string: "重新加载", attributes: [.font: UIFont.systemFont(ofSize: 16, weight: .medium), .foregroundColor: UIColor.white])
  146. return emptyData
  147. }()
  148. lazy var emptyRemindView: BFEmptyRemindView = {
  149. let remindView = BFEmptyRemindView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei))
  150. remindView.isHidden = true
  151. remindView.emptyData = anthorEmptyData
  152. view.addSubview(remindView)
  153. remindView.fullRefreshBloc = { [weak self] _, _ in
  154. self?.isJumpToAuthorization = true
  155. if self?.emptyRemindView.refreshBtn.currentTitle == "授予权限" {
  156. openAppSetting()
  157. }
  158. }
  159. return remindView
  160. }()
  161. lazy var collectionView: UICollectionView = {
  162. let layout = UICollectionViewFlowLayout()
  163. layout.sectionInset = UIEdgeInsets.zero
  164. let collectionView = UICollectionView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei), collectionViewLayout: layout)
  165. collectionView.showsVerticalScrollIndicator = false
  166. collectionView.register(PQSelecteVideoItemCell.self, forCellWithReuseIdentifier: "PQSelecteVideoItemCell")
  167. collectionView.delegate = self
  168. collectionView.dataSource = self
  169. if #available(iOS 11.0, *) {
  170. collectionView.contentInsetAdjustmentBehavior = .never
  171. } else {
  172. automaticallyAdjustsScrollViewInsets = false
  173. }
  174. collectionView.backgroundColor = .clear
  175. return collectionView
  176. }()
  177. lazy var categoryCollectionView: UICollectionView = {
  178. let layout = UICollectionViewFlowLayout()
  179. layout.sectionInset = UIEdgeInsets.zero
  180. let categoryCollectionView = UICollectionView(frame: CGRect(x: 0, y: -categoryH, width: cScreenWidth, height: categoryH), collectionViewLayout: layout)
  181. categoryCollectionView.showsVerticalScrollIndicator = false
  182. categoryCollectionView.register(PQAssetCategoryCell.self, forCellWithReuseIdentifier: "PQAssetCategoryCell")
  183. categoryCollectionView.delegate = self
  184. categoryCollectionView.dataSource = self
  185. if #available(iOS 11.0, *) {
  186. categoryCollectionView.contentInsetAdjustmentBehavior = .never
  187. } else {
  188. automaticallyAdjustsScrollViewInsets = false
  189. }
  190. categoryCollectionView.backgroundColor = PQBFConfig.shared.editCoverimageSelectedbackgroundColor
  191. return categoryCollectionView
  192. }()
  193. lazy var categoryView: UIView = {
  194. let categoryView = UIView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei))
  195. categoryView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
  196. let bgView = UIView(frame: CGRect(x: 0, y: categoryH, width: categoryView.frame.width, height: categoryView.frame.height - categoryH))
  197. let ges = UITapGestureRecognizer(target: self, action: #selector(cancelClick))
  198. bgView.addGestureRecognizer(ges)
  199. categoryView.addSubview(bgView)
  200. categoryView.isHidden = true
  201. return categoryView
  202. }()
  203. lazy var playBtn: UIButton = {
  204. let playBtn = UIButton(type: .custom)
  205. playBtn.frame = CGRect(x: (preViewSize.width - cDefaultMargin * 5) / 2, y: (preViewSize.height - cDefaultMargin * 5) / 2, width: cDefaultMargin * 5, height: cDefaultMargin * 5)
  206. playBtn.setImage(UIImage(named: "icon_video_play_big"), for: .normal)
  207. playBtn.tag = 4
  208. playBtn.isHidden = true
  209. playBtn.isUserInteractionEnabled = false
  210. // playBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  211. return playBtn
  212. }()
  213. var playerHeaderView: UIImageView = {
  214. let playerHeaderView = UIImageView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: 0))
  215. playerHeaderView.isUserInteractionEnabled = true
  216. playerHeaderView.contentMode = .scaleAspectFit
  217. playerHeaderView.clipsToBounds = true
  218. playerHeaderView.backgroundColor = UIColor.yellow
  219. return playerHeaderView
  220. }()
  221. lazy var selecteBtn: UIButton = {
  222. 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))
  223. selecteBtn.titleLabel?.lineBreakMode = .byTruncatingTail
  224. selecteBtn.setTitle("全部", for: .normal)
  225. selecteBtn.setImage(UIImage.moduleImage(named: "icon_uploadVideo_more", moduleName: "BFFramework",isAssets: false)?.withRenderingMode(.alwaysTemplate), for: .normal)
  226. selecteBtn.setTitleColor( PQBFConfig.shared.styleTitleColor, for: .normal)
  227. selecteBtn.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
  228. selecteBtn.tag = 2
  229. selecteBtn.imagePosition(at: PQButtonImageEdgeInsetsStyle.right, space: cDefaultMargin / 2)
  230. selecteBtn.imageView?.tintColor = PQBFConfig.shared.styleTitleColor
  231. selecteBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  232. return selecteBtn
  233. }()
  234. lazy var deleteBtn: UIButton = {
  235. let deleteBtn = UIButton(frame: CGRect(x: cDefaultMargin, y: 0, width: cDefaultMargin * 4, height: cDevice_iPhoneTabBarHei))
  236. deleteBtn.setImage(UIImage.moduleImage(named: "icon_blanc_back", moduleName: "BFFramework",isAssets: false)?.withRenderingMode(.alwaysTemplate), for: .normal)
  237. deleteBtn.tag = 1
  238. deleteBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  239. deleteBtn.imageView?.tintColor = PQBFConfig.shared.styleTitleColor
  240. return deleteBtn
  241. }()
  242. lazy var nextBtn: UIButton = {
  243. let nextBtn = UIButton(frame: CGRect(x: 0, y: (cDevice_iPhoneTabBarHei - cDefaultMargin * 3) / 2, width: cDefaultMargin * 6, height: cDefaultMargin * 3))
  244. nextBtn.addCorner(corner: 3)
  245. nextBtn.setTitle("下一步", for: .normal)
  246. nextBtn.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .medium)
  247. nextBtn.tag = 3
  248. nextBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  249. nextBtn.backgroundColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
  250. nextBtn.setTitleColor(.white, for: .normal)
  251. return nextBtn
  252. }()
  253. lazy var bottomView: UIView = {
  254. let bottomView = UIView(frame: CGRect(x: 0, y: cDefaultMargin * 2, width: cScreenWidth, height: cDevice_iPhoneNavBarHei))
  255. bottomView.addSubview(selecteBtn)
  256. bottomView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
  257. selecteBtn.center.y = nextBtn.center.y
  258. bottomView.addSubview(deleteBtn)
  259. bottomView.addSubview(nextBtn)
  260. nextBtn.frame.origin.x = bottomView.frame.width - nextBtn.frame.width - cDefaultMargin * 2
  261. return bottomView
  262. }()
  263. override func viewDidLoad() {
  264. super.viewDidLoad()
  265. view.backgroundColor = PQBFConfig.shared.editCoverimageSelectedbackgroundColor
  266. navHeadImageView?.backgroundColor = PQBFConfig.shared.editCoverimageSelectedbackgroundColor
  267. lineView?.backgroundColor = PQBFConfig.shared.editCoverimageSelectedbackgroundColor
  268. addSubViews()
  269. bottomView.backgroundColor = PQBFConfig.shared.editCoverimageSelectedbackgroundColor
  270. loadLocalData()
  271. }
  272. deinit {
  273. // PHPhotoLibrary.shared().unregisterChangeObserver(self)
  274. }
  275. override func viewDidDisappear(_ animated: Bool) {
  276. super.viewDidDisappear(animated)
  277. }
  278. override func viewWillAppear(_ animated: Bool) {
  279. super.viewDidAppear(animated)
  280. addPlayerItemObserver()
  281. }
  282. override func viewWillDisappear(_ animated: Bool) {
  283. super.viewWillDisappear(animated)
  284. removePlayerItemObserver()
  285. }
  286. func addSubViews() {
  287. view.addSubview(collectionView)
  288. navHeadImageView?.addSubview(bottomView)
  289. PQNotification.addObserver(self, selector: #selector(didBecomeActiveNotification), name: UIApplication.didBecomeActiveNotification, object: nil)
  290. }
  291. func loadLocalData() {
  292. let authStatus = PHPhotoLibrary.authorizationStatus()
  293. if authStatus == .notDetermined {
  294. // 第一次触发授权 alert
  295. PHPhotoLibrary.requestAuthorization { [weak self] (status: PHAuthorizationStatus) -> Void in
  296. if status == .authorized {
  297. if (self?.allPhotos == nil) || (self?.allPhotos.count ?? 0) <= 0 {
  298. self?.loadPhotoData()
  299. }
  300. if self?.backBtn != nil {
  301. self?.backBtn.removeFromSuperview()
  302. }
  303. } else {
  304. DispatchQueue.main.async { [weak self] in
  305. self?.emptyRemindView.isHidden = false
  306. self?.emptyRemindView.refreshBtn.isHidden = false
  307. self?.emptyRemindView.refreshBtn.setTitle("授予权限", for: .normal)
  308. if self?.backBtn.superview == nil {
  309. self?.view.addSubview((self?.backBtn)!)
  310. } else {
  311. self?.backBtn.isHidden = false
  312. }
  313. }
  314. }
  315. }
  316. } else if authStatus == .authorized {
  317. if allPhotos == nil || allPhotos.count <= 0 {
  318. loadPhotoData()
  319. }
  320. if backBtn.superview != nil {
  321. backBtn.removeFromSuperview()
  322. }
  323. } else {
  324. emptyRemindView.isHidden = false
  325. emptyRemindView.refreshBtn.isHidden = false
  326. emptyRemindView.refreshBtn.setTitle("授予权限", for: .normal)
  327. if backBtn.superview == nil {
  328. view.addSubview(backBtn)
  329. } else {
  330. backBtn.isHidden = false
  331. }
  332. }
  333. }
  334. func loadPhotoData() {
  335. DispatchQueue.main.async { [weak self] in
  336. BFLoadingHUB.shared.showHUB(superView: self!.view, isVerticality: false)
  337. }
  338. DispatchQueue.global().async { [weak self] in
  339. self?.allPhotos = PHAsset.fetchAssets(with: self?.fetchOptions)
  340. DispatchQueue.main.async { [weak self] in
  341. if self?.view != nil {
  342. BFLoadingHUB.shared.dismissHUB(superView: self!.view)
  343. }
  344. self?.collectionView.reloadData()
  345. if (self?.allPhotos.count ?? 0) <= 0 {
  346. self?.emptyRemindView.frame = CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei - cDevice_iPhoneTabBarHei)
  347. self?.emptyRemindView.emptyData = self?.emptyData
  348. self?.emptyRemindView.isHidden = false
  349. } else {
  350. self?.emptyRemindView.isHidden = true
  351. }
  352. if (self?.allPhotos.count ?? 0) > 0 {
  353. let tempData = PQUploadModel()
  354. tempData.title = "全部"
  355. tempData.categoryList = self!.allPhotos
  356. self?.categoryData.insert(tempData, at: 0)
  357. }
  358. self?.updateCachedAssets()
  359. }
  360. }
  361. PHPhotoLibrary.shared().register(self)
  362. DispatchQueue.global().async { [weak self] in
  363. self?.smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil)
  364. self?.smartAlbums?.enumerateObjects { [weak self] assCollection, _, _ in
  365. if assCollection.localizedTitle != "最近删除" {
  366. self?.convertCollection(collection: assCollection)
  367. }
  368. }
  369. self?.userCollections = PHCollectionList.fetchTopLevelUserCollections(with: nil)
  370. self?.userCollections.enumerateObjects { assCollection, index, point in
  371. BFLog(message: "userCollections == \(assCollection),index = \(index),point = \(point)")
  372. if assCollection is PHAssetCollection {
  373. if assCollection.localizedTitle != "最近删除" {
  374. self?.convertCollection(collection: assCollection as? PHAssetCollection)
  375. }
  376. }
  377. }
  378. }
  379. }
  380. // 转化处理获取到的相簿
  381. func convertCollection(collection: PHAssetCollection?) {
  382. if collection == nil {
  383. return
  384. }
  385. DispatchQueue.global().async { [weak self] in
  386. let assetsFetchResult = PHAsset.fetchAssets(in: collection!, options: self?.fetchOptions)
  387. if assetsFetchResult.count > 0 {
  388. let tempData = PQUploadModel()
  389. tempData.title = collection?.localizedTitle
  390. tempData.categoryList = assetsFetchResult
  391. if tempData.categoryList.count > 0 {
  392. self?.categoryData.append(tempData)
  393. }
  394. BFLog(message: "assetsFetchResult = \(assetsFetchResult)")
  395. }
  396. }
  397. }
  398. @objc func btnClick(sender: UIButton) {
  399. switch sender.tag {
  400. case 1: // 返回
  401. navigationController?.popViewController(animated: true)
  402. case 2: // 筛选
  403. showCollects()
  404. case 3: // 下一步
  405. if selectedData == nil {
  406. cShowHUB(superView: nil, msg: isAssetImage ? "请选择图片" : "请选择视频")
  407. return
  408. }
  409. imageManager.requestImage(for: (selectedData?.asset)!, targetSize: itemSize, contentMode: .aspectFill, options: nil) { [weak self] image, _ in
  410. self?.selectedData?.image = image
  411. let vc = PQImageCropVC(image: (self?.selectedData?.image)!, aspectWidth: self?.videoWidth ?? 0.0, aspectHeight: self?.videoHeight ?? 0.0)
  412. vc.uploadData = self?.selectedData
  413. self?.navigationController?.pushViewController(vc, animated: true)
  414. }
  415. default:
  416. break
  417. }
  418. }
  419. @objc func showCollects() {
  420. if categoryData.count <= 0 {
  421. return
  422. }
  423. categoryCollectionView.reloadData()
  424. if categoryView.superview == nil {
  425. view.insertSubview(categoryView, belowSubview: navHeadImageView!)
  426. categoryView.addSubview(categoryCollectionView)
  427. showCategoryView()
  428. return
  429. }
  430. if categoryView.isHidden {
  431. showCategoryView()
  432. } else {
  433. cancelClick()
  434. }
  435. }
  436. @objc func showCategoryView() {
  437. categoryView.isHidden = false
  438. categoryView.alpha = 0
  439. view.bringSubviewToFront(categoryView)
  440. view.bringSubviewToFront(bottomView)
  441. UIView.animate(withDuration: 0.3, animations: {
  442. self.categoryCollectionView.frame = CGRect(x: 0, y: 0, width: cScreenWidth, height: self.categoryH)
  443. self.categoryView.alpha = 1
  444. }) { _ in
  445. }
  446. }
  447. @objc func cancelClick() {
  448. UIView.animate(withDuration: 0.3, animations: {
  449. self.categoryCollectionView.frame = CGRect(x: 0, y: -self.categoryH, width: cScreenWidth, height: self.categoryH)
  450. self.categoryView.alpha = 0
  451. }) { _ in
  452. self.categoryView.isHidden = true
  453. }
  454. }
  455. @objc func didBecomeActiveNotification() {
  456. if isJumpToAuthorization {
  457. loadLocalData()
  458. isJumpToAuthorization = false
  459. }
  460. }
  461. }
  462. extension PQUploadController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UIScrollViewDelegate {
  463. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection _: Int) -> Int {
  464. if collectionView == self.collectionView {
  465. return allPhotos == nil ? videoData.count : allPhotos.count
  466. }
  467. return categoryData.count
  468. }
  469. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  470. if collectionView == self.collectionView {
  471. let cell = PQSelecteVideoItemCell.selecteVideoItemCell(collectionView: collectionView, indexPath: indexPath)
  472. if videoData.count <= indexPath.item, allPhotos != nil {
  473. let itemData = PQUploadModel()
  474. // itemData.image = UIImage.init(named: "cut_place")
  475. let asset = allPhotos.object(at: indexPath.item)
  476. itemData.asset = asset
  477. itemData.duration = asset.duration
  478. videoData.append(itemData)
  479. }
  480. if videoData.count > indexPath.item {
  481. let itemData = videoData[indexPath.item]
  482. cell.uploadData = itemData
  483. if itemData.image == nil, itemData.asset != nil {
  484. cell.representedAssetIdentifier = itemData.asset?.localIdentifier
  485. imageManager.requestImage(for: itemData.asset!, targetSize: itemSize, contentMode: .aspectFill, options: nil) { image, info in
  486. if info?.keys.contains("PHImageResultIsDegradedKey") ?? false, "\(info?["PHImageResultIsDegradedKey"] ?? "0")" == "0", cell.representedAssetIdentifier == itemData.asset?.localIdentifier {
  487. if image != nil {
  488. itemData.image = image
  489. cell.videoImageView.image = image
  490. } else if image == nil, info?.keys.contains("PHImageResultIsInCloudKey") ?? false {
  491. let option = PHImageRequestOptions()
  492. option.isNetworkAccessAllowed = true
  493. option.resizeMode = .fast
  494. self.imageManager.requestImageData(for: itemData.asset!, options: option) { data, _, _, _ in
  495. if data != nil {
  496. let image = UIImage(data: data!)
  497. itemData.image = image
  498. cell.videoImageView.image = image
  499. }
  500. }
  501. }
  502. }
  503. }
  504. }
  505. } else {
  506. cell.uploadData = PQUploadModel()
  507. }
  508. return cell
  509. } else {
  510. let cell = PQAssetCategoryCell.assetCategoryCell(collectionView: collectionView, indexPath: indexPath)
  511. let itemData = categoryData[indexPath.item]
  512. let asset = itemData.categoryList.object(at: 0)
  513. if itemData.image == nil {
  514. cell.representedAssetIdentifier = asset.localIdentifier
  515. imageManager.requestImage(for: asset, targetSize: itemSize, contentMode: .aspectFill, options: nil) { image, info in
  516. if info?.keys.contains("PHImageResultIsDegradedKey") ?? false, "\(info?["PHImageResultIsDegradedKey"] ?? "0")" == "0", cell.representedAssetIdentifier == asset.localIdentifier {
  517. itemData.image = image
  518. cell.uploadData = itemData
  519. }
  520. }
  521. } else {
  522. cell.uploadData = itemData
  523. }
  524. cell.isSelected = indexPath == catagerySelectedIndex
  525. return cell
  526. }
  527. }
  528. func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  529. if collectionView == self.collectionView {
  530. if videoData.count <= indexPath.item {
  531. return
  532. }
  533. let itemData = videoData[indexPath.item]
  534. let ratio = Float(itemData.asset?.pixelWidth ?? 0) / Float(itemData.asset?.pixelHeight ?? 1)
  535. if ratio < 0.4 || ratio > 4.2 {
  536. cShowHUB(superView: nil, msg: "暂不支持该比例的素材")
  537. return
  538. }
  539. if lastSeletedIndex != nil, lastSeletedIndex != indexPath {
  540. let lastData = videoData[lastSeletedIndex!.item]
  541. lastData.isSelected = false
  542. collectionView.reloadItems(at: [lastSeletedIndex!])
  543. }
  544. lastSeletedIndex = indexPath
  545. if itemData.isSelected {
  546. itemData.isSelected = false
  547. selectedData = nil
  548. } else {
  549. itemData.isSelected = true
  550. let itemData = videoData[indexPath.item]
  551. selectedData = itemData
  552. }
  553. collectionView.reloadItems(at: [indexPath])
  554. } else {
  555. videoData.removeAll()
  556. self.collectionView.setContentOffset(CGPoint.zero, animated: true)
  557. self.collectionView.reloadData()
  558. allPhotos = categoryData[indexPath.item].categoryList
  559. catagerySelectedIndex = indexPath
  560. selecteBtn.setTitle(categoryData[indexPath.item].title, for: .normal)
  561. selecteBtn.imagePosition(at: PQButtonImageEdgeInsetsStyle.right, space: cDefaultMargin / 2)
  562. lastSeletedIndex = nil
  563. if allPhotos.count <= 0 {
  564. emptyRemindView.frame = CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: cScreenWidth, height: cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei - cDevice_iPhoneTabBarHei)
  565. emptyRemindView.emptyData = emptyData
  566. emptyRemindView.isHidden = false
  567. } else {
  568. emptyRemindView.isHidden = true
  569. }
  570. self.collectionView.reloadData()
  571. cancelClick()
  572. }
  573. }
  574. func collectionView(_ collectionView: UICollectionView, layout _: UICollectionViewLayout, sizeForItemAt _: IndexPath) -> CGSize {
  575. if collectionView == self.collectionView {
  576. return CGSize(width: (cScreenWidth - cDefaultMargin) / 3, height: (cScreenWidth - cDefaultMargin) / 3)
  577. }
  578. return CGSize(width: collectionView.frame.width, height: cDefaultMargin * 8)
  579. }
  580. func collectionView(_ collectionView: UICollectionView, layout _: UICollectionViewLayout, minimumLineSpacingForSectionAt _: Int) -> CGFloat {
  581. if collectionView == self.collectionView {
  582. return cDefaultMargin / 2
  583. }
  584. return 0
  585. }
  586. func collectionView(_ collectionView: UICollectionView, layout _: UICollectionViewLayout, minimumInteritemSpacingForSectionAt _: Int) -> CGFloat {
  587. if collectionView == self.collectionView {
  588. return cDefaultMargin / 2
  589. }
  590. return 0
  591. }
  592. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  593. //这里是不是有用的?
  594. // if currentController() is PQUploadController || currentController() is PQImageSelectedController {
  595. // if scrollView == collectionView {
  596. // updateCachedAssets()
  597. // } else {
  598. // BFLog(message: "contentOffset = \(scrollView.contentOffset),contentSize = \(scrollView.contentSize)")
  599. // if scrollView.contentOffset.y > ((scrollView.contentSize.height - scrollView.frame.height) + cDefaultMargin * 10) {
  600. // cancelClick()
  601. // }
  602. // }
  603. // }
  604. }
  605. }
  606. extension PQUploadController: PHPhotoLibraryChangeObserver {
  607. func photoLibraryDidChange(_ changeInstance: PHChange) {
  608. // Change notifications may be made on a background queue. Re-dispatch to the
  609. // main queue before acting on the change as we'll be updating the UI.
  610. DispatchQueue.main.sync { [weak self] in
  611. // Check each of the three top-level fetches for changes.
  612. if allPhotos != nil, changeInstance.changeDetails(for: allPhotos) != nil {
  613. self?.categoryData.removeAll()
  614. self?.loadPhotoData()
  615. }
  616. }
  617. }
  618. }
  619. extension PQUploadController {
  620. /// 添加监听
  621. /// - Parameter playerItem: <#playerItem description#>
  622. /// - Returns: <#description#>
  623. func addPlayerItemObserver() {
  624. playerItem?.addObserver(self, forKeyPath: "status", options: .new, context: nil)
  625. }
  626. /// 移除监听
  627. /// - Returns: <#description#>
  628. func removePlayerItemObserver() {
  629. playerItem?.removeObserver(self, forKeyPath: "status")
  630. }
  631. override func observeValue(forKeyPath keyPath: String?, of object: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
  632. if object is AVPlayerItem, keyPath == "status" {
  633. BFLog(message: "(object as! AVPlayerItem).status = \((object as! AVPlayerItem).status.rawValue)")
  634. BFLoadingHUB.shared.dismissHUB(superView: playerHeaderView)
  635. switch (object as! AVPlayerItem).status {
  636. case .unknown:
  637. break
  638. case .readyToPlay:
  639. break
  640. case .failed:
  641. break
  642. default:
  643. break
  644. }
  645. }
  646. }
  647. private func resetCachedAssets() {
  648. imageManager.stopCachingImagesForAllAssets()
  649. previousPreheatRect = .zero
  650. }
  651. private func updateCachedAssets() {
  652. if allPhotos != nil && allPhotos.count <= 0 {
  653. return
  654. }
  655. guard isViewLoaded, view.window != nil else { return }
  656. let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
  657. let preheatRect = visibleRect.insetBy(dx: 0, dy: -0.5 * visibleRect.height)
  658. let delta = abs(preheatRect.midY - previousPreheatRect.midY)
  659. guard delta > view.bounds.height / 3 else { return }
  660. let (addedRects, removedRects) = differencesBetweenRects(previousPreheatRect, preheatRect)
  661. let addedAssets = addedRects
  662. .flatMap { rect in collectionView.indexPathsForElements(in: rect) }
  663. .map { indexPath in allPhotos.object(at: indexPath.item) }
  664. let removedAssets = removedRects
  665. .flatMap { rect in collectionView.indexPathsForElements(in: rect) }
  666. .map { indexPath in allPhotos.object(at: indexPath.item) }
  667. imageManager.startCachingImages(for: addedAssets,
  668. targetSize: itemSize, contentMode: .aspectFill, options: nil)
  669. imageManager.stopCachingImages(for: removedAssets,
  670. targetSize: itemSize, contentMode: .aspectFill, options: nil)
  671. previousPreheatRect = preheatRect
  672. }
  673. private func differencesBetweenRects(_ old: CGRect, _ new: CGRect) -> (added: [CGRect], removed: [CGRect]) {
  674. if old.intersects(new) {
  675. var added = [CGRect]()
  676. if new.maxY > old.maxY {
  677. added += [CGRect(x: new.origin.x, y: old.maxY,
  678. width: new.width, height: new.maxY - old.maxY)]
  679. }
  680. if old.minY > new.minY {
  681. added += [CGRect(x: new.origin.x, y: new.minY,
  682. width: new.width, height: old.minY - new.minY)]
  683. }
  684. var removed = [CGRect]()
  685. if new.maxY < old.maxY {
  686. removed += [CGRect(x: new.origin.x, y: new.maxY,
  687. width: new.width, height: old.maxY - new.maxY)]
  688. }
  689. if old.minY < new.minY {
  690. removed += [CGRect(x: new.origin.x, y: old.minY,
  691. width: new.width, height: new.minY - old.minY)]
  692. }
  693. return (added, removed)
  694. } else {
  695. return ([new], [old])
  696. }
  697. }
  698. }