PQStuckPointMaterialDetailController.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. //
  2. // PQStuckPointMaterialDetailController.swift
  3. // PQSpeed
  4. //
  5. // Created by SanW on 2021/4/27.
  6. // Copyright © 2021 BytesFlow. All rights reserved.
  7. //
  8. import Kingfisher
  9. import KingfisherWebP
  10. import UIKit
  11. class PQStuckPointMaterialDetailController: PQBaseViewController {
  12. var materialData: PQEditVisionTrackMaterialsModel?
  13. // 点击的回调
  14. var materialDetailClickHandle: ((_ isMaterialSelected: Bool?, _ materialData: PQEditVisionTrackMaterialsModel?) -> Void)?
  15. var isMaterialSelected: Bool?
  16. var margin: CGFloat = 35 // 左右间距
  17. // 最大的宽度
  18. var maxWidth: CGFloat = cScreenWidth
  19. // 最大的高度
  20. var maxHeight: CGFloat = cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei - cDevice_iPhoneStatusBarHei
  21. // 预览区域宽度
  22. var preViewWidth: CGFloat = cScreenWidth
  23. // 预览区域高度
  24. var preViewHeight: CGFloat = cScreenHeigth - cDevice_iPhoneNavBarAndStatusBarHei - cDevice_iPhoneStatusBarHei
  25. lazy var preImageView: UIImageView = {
  26. let preImageView = UIImageView(frame: CGRect(x: (maxWidth - preViewWidth) / 2, y: cDevice_iPhoneNavBarAndStatusBarHei + (maxHeight - preViewHeight) / 2, width: preViewWidth, height: preViewHeight))
  27. preImageView.center.x = view.center.x
  28. preImageView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
  29. preImageView.contentMode = .scaleAspectFit
  30. preImageView.isUserInteractionEnabled = true
  31. preImageView.clipsToBounds = true
  32. // preImageView.image = UIImage.init(named: "cut_place")
  33. return preImageView
  34. }()
  35. lazy var choseTitleBtn: UIButton = {
  36. let choseTitleBtn = UIButton(type: .custom)
  37. choseTitleBtn.frame = CGRect(x: cScreenWidth - cDefaultMargin * 6, y: 0, width: cDefaultMargin * 5, height: cDefaultMargin * 3)
  38. choseTitleBtn.setTitleColor(UIColor.white, for: .normal)
  39. choseTitleBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .medium)
  40. choseTitleBtn.setTitle("取消", for: .selected)
  41. choseTitleBtn.setTitle("选择", for: .normal)
  42. choseTitleBtn.tag = 2
  43. choseTitleBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  44. return choseTitleBtn
  45. }()
  46. lazy var choseBtn: UIButton = {
  47. let choseBtn = UIButton(type: .custom)
  48. choseBtn.frame = CGRect(x: choseTitleBtn.frame.minX - cDefaultMargin * 3, y: 0, width: cDefaultMargin * 3, height: cDefaultMargin * 3)
  49. choseBtn.setBackgroundImage(UIImage().BF_Image(named: "videomk_chose_nomal"), for: .normal)
  50. choseBtn.setTitleColor(UIColor.white, for: .normal)
  51. choseBtn.titleLabel?.font = UIFont.systemFont(ofSize: 12)
  52. choseBtn.tag = 1
  53. choseBtn.addCorner(corner: 15)
  54. choseBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  55. return choseBtn
  56. }()
  57. lazy var changeModeBtn: UIButton = {
  58. let changeModeBtn = UIButton(type: .custom)
  59. changeModeBtn.setImage(UIImage().BF_Image(named: "videomk_changeMode"), for: .normal)
  60. changeModeBtn.frame = CGRect(x: 15, y: preImageView.frame.height - cDefaultMargin * 4 - 15, width: cDefaultMargin * 4, height: cDefaultMargin * 4)
  61. changeModeBtn.tag = 3
  62. changeModeBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  63. return changeModeBtn
  64. }()
  65. lazy var gifRemindLab: UILabel = {
  66. let gifRemindLab = UILabel(frame: CGRect(x: (preImageView.frame.width - cDefaultMargin * 4) / 2, y: preImageView.frame.height - cDefaultMargin * 3 - 15, width: cDefaultMargin * 4, height: cDefaultMargin * 3))
  67. gifRemindLab.textAlignment = .center
  68. gifRemindLab.font = UIFont.systemFont(ofSize: 18)
  69. gifRemindLab.textColor = UIColor.white
  70. gifRemindLab.text = "GIF"
  71. return gifRemindLab
  72. }()
  73. lazy var playBtn: UIButton = {
  74. let playBtn = UIButton(type: .custom)
  75. playBtn.frame = CGRect(x: (preImageView.frame.width - cDefaultMargin * 5) / 2, y: (preImageView.frame.height - cDefaultMargin * 5) / 2, width: cDefaultMargin * 5, height: cDefaultMargin * 5)
  76. playBtn.setImage(UIImage().BF_Image(named: "icon_video_play"), for: .normal)
  77. playBtn.isUserInteractionEnabled = false
  78. playBtn.isHidden = true
  79. // playBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
  80. return playBtn
  81. }()
  82. lazy var avPlayer: AVPlayer = {
  83. let avPlayer = AVPlayer()
  84. NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem, queue: .main) { [weak self] notify in
  85. BFLog(message: "AVPlayerItemDidPlayToEndTime = \(notify)")
  86. avPlayer.seek(to: CMTime(value: CMTimeValue((self?.materialData?.model_in ?? 0) * playerTimescale), timescale: CMTimeScale(playerTimescale)))
  87. self?.playBtn.isHidden = false
  88. }
  89. NotificationCenter.default.addObserver(forName: .AVPlayerItemNewErrorLogEntry, object: avPlayer.currentItem, queue: .main) { notify in
  90. BFLog(message: "AVPlayerItemNewErrorLogEntry = \(notify)")
  91. }
  92. NotificationCenter.default.addObserver(forName: .AVPlayerItemFailedToPlayToEndTime, object: avPlayer.currentItem, queue: .main) { notify in
  93. BFLog(message: "AVPlayerItemFailedToPlayToEndTime = \(notify)")
  94. }
  95. NotificationCenter.default.addObserver(forName: .AVPlayerItemPlaybackStalled, object: avPlayer.currentItem, queue: .main) { notify in
  96. BFLog(message: "AVPlayerItemPlaybackStalled = \(notify)")
  97. }
  98. avPlayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: CMTimeScale(playerTimescale)), queue: .main) { [weak self] cmTime in
  99. // let progress = CMTimeGetSeconds(avPlayer.currentItem?.currentTime() ?? CMTime.zero) / CMTimeGetSeconds(avPlayer.currentItem?.duration ?? CMTime.zero)
  100. let delta = CMTimeGetSeconds(cmTime)
  101. let start = CGFloat(delta - (self?.materialData?.model_in ?? 0.0))
  102. var endTime: Float64 = self?.materialData?.out ?? 0
  103. if endTime <= 0 {
  104. endTime = self?.materialData?.duration ?? 0
  105. }
  106. let total = CGFloat(endTime - (self?.materialData?.model_in ?? 0.0))
  107. let progress = start / total
  108. if progress >= 1 {
  109. self?.playBtn.isHidden = false
  110. }
  111. BFLog(message: "progress = \(progress)")
  112. }
  113. return avPlayer
  114. }()
  115. lazy var playerLayer: AVPlayerLayer = {
  116. let playerLayer = AVPlayerLayer(player: avPlayer)
  117. playerLayer.frame = CGRect(origin: CGPoint.zero, size: preImageView.frame.size)
  118. return playerLayer
  119. }()
  120. override func viewDidLoad() {
  121. super.viewDidLoad()
  122. leftButton(image: "icon_detail_back", tintColor: PQBFConfig.shared.styleTitleColor)
  123. navHeadImageView?.backgroundColor = UIColor.clear
  124. let width = CGFloat(materialData?.width ?? Float(preViewWidth))
  125. let height = CGFloat(materialData?.height ?? Float(preViewHeight))
  126. if width >= height {
  127. preViewWidth = maxWidth
  128. preViewHeight = preViewWidth * height / width
  129. } else {
  130. preViewHeight = maxHeight
  131. preViewWidth = preViewHeight * width / height
  132. }
  133. lineView?.removeFromSuperview()
  134. view.addSubview(preImageView)
  135. // preImageView.addSubview(changeModeBtn)
  136. if materialData?.type == StickerType.GIF.rawValue {
  137. preImageView.addSubview(gifRemindLab)
  138. setImageToPreImageView()
  139. } else if materialData?.type == StickerType.IMAGE.rawValue {
  140. setImageToPreImageView()
  141. } else if materialData?.type == StickerType.VIDEO.rawValue {
  142. preImageView.addSubview(playBtn)
  143. preImageView.layer.insertSublayer(playerLayer, at: 0)
  144. let ges = UITapGestureRecognizer(target: self, action: #selector(playPreVideo))
  145. preImageView.addGestureRecognizer(ges)
  146. // 设置播放内容
  147. setPlayerItem()
  148. }
  149. // updateSelectedState()
  150. isMaterialSelected = materialData?.isSelected
  151. }
  152. deinit {
  153. if materialData?.type == StickerType.VIDEO.rawValue {
  154. avPlayer.replaceCurrentItem(with: nil)
  155. PQNotification.removeObserver(self)
  156. }
  157. }
  158. /// 设置图片
  159. /// - Returns: <#description#>
  160. func setImageToPreImageView() {
  161. if materialData?.asset != nil {
  162. if materialData?.originalData == nil {
  163. PQPHAssetVideoParaseUtil.requestAssetOringinImage(asset: (materialData?.asset)!) { [weak self] _, data, image, _ in
  164. if image == nil {
  165. BFLog(message: "图片数据为空!!!!!")
  166. }
  167. // 1,图片,gif的原文件数据,和视频的封面
  168. var newImage: UIImage?
  169. if image != nil {
  170. newImage = UIImage.nx_fixOrientation(image, isFront: false).nx_scaleWithMaxLength(maxLength: 1920)
  171. }
  172. self?.materialData?.coverImageUI = newImage
  173. self?.materialData?.originalData = data
  174. // if isGIF {
  175. // self?.materialData?.type = StickerType.GIF.rawValue
  176. // if self?.gifRemindLab.superview == nil {
  177. // self?.preImageView.addSubview(self!.gifRemindLab)
  178. // }
  179. // self?.preImageView.displayGIF(data: data)
  180. // } else {
  181. self?.preImageView.image = newImage
  182. // }
  183. }
  184. } else {
  185. setNomarlImageToPreImageView()
  186. }
  187. } else {
  188. setNomarlImageToPreImageView()
  189. }
  190. }
  191. func setNomarlImageToPreImageView() {
  192. if materialData?.type == StickerType.VIDEO.rawValue {
  193. return
  194. }
  195. if materialData?.type == StickerType.GIF.rawValue && materialData?.originalData != nil {
  196. preImageView.displayGIF(data: materialData?.originalData)
  197. } else if preImageView.image != nil {
  198. preImageView.image = preImageView.image
  199. } else if preImageView.image == nil && materialData?.coverImageUI != nil {
  200. if materialData?.type == StickerType.GIF.rawValue {
  201. preImageView.displayGIF(data: kf_imageCacheData(originUrl: materialData?.netResCoverImageURL ?? ""))
  202. } else {
  203. preImageView.image = materialData?.coverImageUI
  204. }
  205. } else if materialData?.coverImageUI != nil {
  206. preImageView.image = materialData?.coverImageUI
  207. } else if materialData?.locationPath.count ?? 0 > 0 {
  208. if materialData?.type == StickerType.GIF.rawValue {
  209. let fileData: Data = try! Data(contentsOf: URL(fileURLWithPath: documensDirectory + (materialData?.locationPath)!))
  210. materialData?.originalData = fileData
  211. preImageView.displayGIF(data: materialData?.originalData)
  212. } else {
  213. var coverImage = UIImage(contentsOfFile: documensDirectory + (materialData?.locationPath)!)
  214. if coverImage == nil {
  215. // 有可能是 WEBP
  216. let fileData: Data = try! Data(contentsOf: URL(fileURLWithPath: documensDirectory + (materialData?.locationPath)!))
  217. if fileData.count != 0, fileData.isWebPFormat {
  218. coverImage = WebPProcessor.default.process(item: ImageProcessItem.data(fileData), options: KingfisherParsedOptionsInfo([.onlyLoadFirstFrame, .scaleFactor(1)]))
  219. }
  220. }
  221. preImageView.image = coverImage
  222. materialData?.coverImageUI = coverImage
  223. }
  224. } else if (materialData?.netResCoverImageURL != nil && ((materialData?.netResCoverImageURL?.count ?? 0) > 0)) || (materialData?.materialUrl != nil && ((materialData?.materialUrl.count ?? 0) > 0)) {
  225. var url = materialData?.materialUrl
  226. if url == nil || (url?.count ?? 0) <= 0 {
  227. url = materialData?.netResCoverImageURL
  228. }
  229. ImageDownloader.default.downloadImage(with: URL(string: url ?? "")!, options: nil) { [weak self] result in
  230. switch result {
  231. case let .success(imageLoading):
  232. self?.materialData?.originalData = imageLoading.originalData
  233. self?.materialData?.coverImageUI = imageLoading.image
  234. if self?.materialData?.type == StickerType.GIF.rawValue {
  235. self?.preImageView.displayGIF(data: imageLoading.originalData)
  236. } else {
  237. self?.preImageView.image = imageLoading.image
  238. }
  239. let imageOrientation = UIImage.nx_fixOrientation(imageLoading.image, isFront: false).nx_scaleWithMaxLength(maxLength: 1024)
  240. // 下载器的原地址
  241. let diskCachePath = ImageCache.default.cachePath(forKey: url!)
  242. let imageCacheName = url!.md5
  243. let imageCachePath = downloadImagesDirectory + imageCacheName
  244. if !directoryIsExists(dicPath: downloadImagesDirectory) {
  245. BFLog(message: "文件夹不存在 \(downloadImagesDirectory)")
  246. createDirectory(path: downloadImagesDirectory)
  247. }
  248. if self?.materialData?.type != StickerType.GIF.rawValue {
  249. try! imageOrientation.pngData()?.write(to: URL(fileURLWithPath: imageCachePath))
  250. } else {
  251. try! imageLoading.originalData.write(to: URL(fileURLWithPath: imageCachePath))
  252. }
  253. if !FileManager.default.fileExists(atPath: imageCachePath) {
  254. try! FileManager.default.copyItem(atPath: diskCachePath, toPath: downloadImagesDirectory + imageCacheName)
  255. }
  256. if self?.materialData?.type != StickerType.VIDEO.rawValue {
  257. self?.materialData?.locationPath = imageCachePath.replacingOccurrences(of: documensDirectory, with: "")
  258. }
  259. case let .failure(error):
  260. BFLog(message: "下载图片失败:\(error.localizedDescription)")
  261. }
  262. }
  263. }
  264. }
  265. /// 预览视频
  266. @objc func playPreVideo() {
  267. playBtn.isHidden = !playBtn.isHidden
  268. if avPlayer.currentItem == nil {
  269. setPlayerItem()
  270. }
  271. if playBtn.isHidden {
  272. if playerLayer.superlayer == nil {
  273. preImageView.layer.insertSublayer(playerLayer, at: 0)
  274. }
  275. avPlayer.play()
  276. } else {
  277. avPlayer.pause()
  278. }
  279. }
  280. /// 设置播放item
  281. /// - Returns: <#description#>
  282. func setPlayerItem() {
  283. if materialData?.locationPath.count ?? 0 > 0 {
  284. if materialData?.locationPath.contains("mobile/Media/DCIM") ?? false {
  285. addPlayItem(playerItem: AVPlayerItem(url: URL(fileURLWithPath: materialData!.locationPath)))
  286. } else {
  287. addPlayItem(playerItem: AVPlayerItem(url: URL(fileURLWithPath: documensDirectory + materialData!.locationPath)))
  288. }
  289. } else if materialData?.locationPath.count == 0 && materialData?.asset != nil {
  290. PQLoadingHUB.shared.showHUB()
  291. PQPHAssetVideoParaseUtil.parasToAVPlayerItem(phAsset: (materialData?.asset)!) { [weak self] playerItem, _, _ in
  292. PQLoadingHUB.shared.dismissHUB()
  293. // self?.materialData?.locationPath = (playerItem?.asset as? AVURLAsset)?.url.absoluteString.replacingOccurrences(of: "file:///", with: "") ?? ""
  294. self?.addPlayItem(playerItem: playerItem)
  295. }
  296. } else if isValidURL(url: materialData?.netResUrl) {
  297. addPlayItem(playerItem: AVPlayerItem(url: URL(string: materialData?.netResUrl ?? "")!))
  298. }
  299. }
  300. /// 添加播放
  301. /// - Parameter playerItem: <#playerItem description#>
  302. func addPlayItem(playerItem: AVPlayerItem?) {
  303. avPlayer.replaceCurrentItem(with: playerItem)
  304. avPlayer.play()
  305. let startTime = materialData?.model_in ?? 0
  306. var endTime = materialData?.out ?? 0
  307. if endTime <= 0 {
  308. endTime = Float64(materialData?.duration ?? 0.0)
  309. }
  310. avPlayer.seek(to: CMTime(value: CMTimeValue(startTime * playerTimescale), timescale: CMTimeScale(playerTimescale)))
  311. avPlayer.currentItem?.forwardPlaybackEndTime = CMTime(value: CMTimeValue(endTime * playerTimescale), timescale: CMTimeScale(playerTimescale))
  312. }
  313. /// 按钮点击事件
  314. /// - Parameter sender: <#sender description#>
  315. @objc func btnClick(sender: UIButton) {
  316. switch sender.tag {
  317. case 1, 2:
  318. let ratio = (materialData?.width ?? 0) / (materialData?.height ?? 1)
  319. if ratio < 0.4 || ratio > 4.2 {
  320. cShowHUB(superView: nil, msg: "暂不支持该比例的素材")
  321. return
  322. }
  323. materialData?.isSelected = !(materialData?.isSelected ?? false)
  324. // updateSelectedState()
  325. default:
  326. break
  327. }
  328. }
  329. /// 更新选中状态
  330. func updateSelectedState() {
  331. if materialData?.isSelected ?? false {
  332. choseBtn.setBackgroundImage(nil, for: .normal)
  333. choseBtn.backgroundColor = UIColor.hexColor(hexadecimal: "#EE0051")
  334. if materialData?.selectedIndex == 0 {
  335. choseBtn.setTitle("1", for: .normal)
  336. } else {
  337. choseBtn.setTitle("\(materialData?.selectedIndex ?? 1)", for: .normal)
  338. }
  339. choseTitleBtn.isSelected = true
  340. } else {
  341. choseBtn.setBackgroundImage(UIImage().BF_Image(named: "videomk_chose_nomal"), for: .normal)
  342. choseBtn.backgroundColor = UIColor.clear
  343. choseBtn.setTitle(nil, for: .normal)
  344. choseTitleBtn.isSelected = false
  345. }
  346. if materialData?.originalData == nil {
  347. materialData?.originalData = kf_imageCacheData(originUrl: materialData?.netResCoverImageURL ?? "")
  348. if materialData?.originalData != nil {
  349. if materialData?.type == StickerType.GIF.rawValue && materialData?.originalData != nil && (materialData?.duration ?? 0.0) <= 0 {
  350. PQPHAssetVideoParaseUtil.parasGIFImage(data: (materialData?.originalData)!) { [weak self] _, _, duration in
  351. self?.materialData?.duration = duration ?? 0.0
  352. }
  353. }
  354. }
  355. }
  356. }
  357. override func backBtnClick() {
  358. if materialDetailClickHandle != nil {
  359. materialDetailClickHandle!(isMaterialSelected, materialData)
  360. }
  361. super.backBtnClick()
  362. }
  363. }