INVideoExportController.swift 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. //
  2. // INVideoExportController.swift
  3. // Introduce
  4. //
  5. // Created by 胡志强 on 2021/11/29.
  6. //
  7. import BFAnalyzeKit
  8. import BFCommonKit
  9. import BFRecordScreenKit
  10. import BFUIKit
  11. import Foundation
  12. import Photos
  13. import SwiftUI
  14. import UIKit
  15. class INVideoExportController: BFBaseViewController {
  16. var avplayerTimeObserver: NSKeyValueObservation?
  17. let backV = UIView()
  18. var hasExportAll = false
  19. var hasExportOnly = false
  20. var hasSaveAll = false
  21. var hasSaveOnly = false
  22. var saveAllUlr = URL(fileURLWithPath: "aaa")
  23. var saveOnlyUlr = URL(fileURLWithPath: "aaa")
  24. var playViewFrame: CGRect = CGRect.zero
  25. var isExporting = false {
  26. didSet {
  27. if isExporting {
  28. avplayer.pause()
  29. }
  30. }
  31. }
  32. // 预览播放进度
  33. var sliderView: BFVideoPlayerSliderView?
  34. // 合成进度指示条
  35. lazy var progressView: UIView = {
  36. let v = UIView()
  37. v.backgroundColor = ThemeStyleColor
  38. return v
  39. }()
  40. // 合成进度百分比lable
  41. lazy var progressL: UILabel = {
  42. let la = UILabel()
  43. la.textColor = .white
  44. la.textAlignment = .center
  45. la.text = "0%"
  46. la.font = UIFont.systemFont(ofSize: 28)
  47. la.shadowColor = .black
  48. la.shadowOffset = CGSize(width: 1, height: 1)
  49. return la
  50. }()
  51. lazy var saveAllBtn: UIButton = {
  52. let btn = UIButton()
  53. btn.setImage(UIImage(named: "export_saveall_n"), for: .normal)
  54. btn.setImage(UIImage(named: "export_saveall_h"), for: .selected)
  55. btn.addTarget(self, action: #selector(saveAllAction(btn:)), for: .touchUpInside)
  56. return btn
  57. }()
  58. lazy var saveOnlyBtn: UIButton = {
  59. let btn = UIButton()
  60. btn.setImage(UIImage(named: "export_saveonly_n"), for: .normal)
  61. btn.setImage(UIImage(named: "export_saveonly_h"), for: .selected)
  62. btn.addTarget(self, action: #selector(saveOnlyAction(btn:)), for: .touchUpInside)
  63. return btn
  64. }()
  65. lazy var completeBtn: UIButton = {
  66. let btn = UIButton()
  67. btn.setTitle("完成", for: .normal)
  68. // btn.setTitleColor(.white, for: .normal)
  69. btn.setTitleColor(UIColor.hexColor(hexadecimal: "#B1B1B1"), for: .normal)
  70. btn.setTitleColor(.gray, for: .disabled)
  71. btn.addTarget(self, action: #selector(completeAction), for: .touchUpInside)
  72. return btn
  73. }()
  74. lazy var saveToPhotoBtn: UIButton = {
  75. let btn = UIButton()
  76. btn.setTitle(" 保存到相册", for: .normal)
  77. btn.setTitleColor(.white, for: .normal)
  78. btn.setTitleColor(.lightGray, for: .disabled)
  79. btn.setImage(UIImage(named: "export_btn"), for: .normal)
  80. btn.backgroundColor = ThemeStyleColor
  81. btn.addCorner(roundingCorners: .allCorners, corner: 8)
  82. btn.addTarget(self, action: #selector(saveToPhotoNow), for: .touchUpInside)
  83. return btn
  84. }()
  85. lazy var bottomView: UIView = {
  86. let vv = UIView()
  87. vv.backgroundColor = .black
  88. // vv.isHidden = true
  89. return vv
  90. }()
  91. lazy var errorView : UIView = {
  92. let backV = UIView()
  93. backV.backgroundColor = .black
  94. backV.isHidden = true
  95. let titleL = UILabel()
  96. titleL.text = "合成失败"
  97. titleL.font = UIFont.systemFont(ofSize: 36)
  98. titleL.textColor = .white
  99. titleL.textAlignment = .center
  100. backV.addSubview(titleL)
  101. let subTitleL = UILabel()
  102. subTitleL.text = "请重新尝试"
  103. subTitleL.tag = 33000
  104. subTitleL.font = UIFont.systemFont(ofSize: 36)
  105. subTitleL.textColor = UIColor.hexColor(hexadecimal: "#A6A6A6")
  106. subTitleL.textAlignment = .center
  107. backV.addSubview(subTitleL)
  108. let retryBtn = UIButton()
  109. retryBtn.backgroundColor = ThemeStyleColor
  110. retryBtn.addCorner(corner:5)
  111. retryBtn.addTarget(self, action: #selector(retryAction), for: .touchUpInside)
  112. backV.addSubview(retryBtn)
  113. retryBtn.snp.makeConstraints { make in
  114. make.width.equalTo(245)
  115. make.height.equalTo(50)
  116. make.center.equalToSuperview()
  117. }
  118. subTitleL.snp.makeConstraints { make in
  119. make.left.right.equalToSuperview()
  120. make.height.equalTo(40)
  121. make.bottom.equalTo(retryBtn.snp.top).offset(30)
  122. }
  123. titleL.snp.makeConstraints { make in
  124. make.left.right.equalToSuperview()
  125. make.height.equalTo(70)
  126. make.bottom.equalTo(subTitleL.snp.top)
  127. }
  128. return backV
  129. }()
  130. var playerLayer: AVPlayerLayer!
  131. lazy var avplayer: AVPlayer = {
  132. let avplayer = AVPlayer()
  133. avplayerTimeObserver = avplayer.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 100), queue: DispatchQueue.global()) { [weak self, weak avplayer] _ in
  134. // 进度监控
  135. if let item = avplayer?.currentItem, !(self?.sliderView?.isDragingProgressSlder ?? false) {
  136. DispatchQueue.main.async { [weak self, weak avplayer] in
  137. if avplayer?.currentItem?.status != .readyToPlay {
  138. self?.sliderView?.progress = 0
  139. }else{
  140. self?.sliderView?.progress = CMTimeGetSeconds(item.currentTime()) / CMTimeGetSeconds(item.duration)
  141. }
  142. }
  143. }
  144. } as? NSKeyValueObservation
  145. NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avplayer.currentItem, queue: .main) { [weak avplayer, weak self] _ in
  146. avplayer?.seek(to: CMTime.zero)
  147. self?.sliderView?.playEnd()
  148. self?.changeToOriginalFrame()
  149. }
  150. return avplayer
  151. }()
  152. lazy var export: BFRecordExport = {
  153. let export = BFRecordExport()
  154. export.progress = { [weak self] progress in
  155. DispatchQueue.main.async { [weak self] in
  156. if let sself = self {
  157. // let width = export.data?.first?.width ?? 0
  158. sself.progressL.text = String(format: "%d%%", Int(progress * 100))
  159. sself.progressView.snp.updateConstraints { make in
  160. make.width.equalTo(cScreenWidth * CGFloat(progress))
  161. }
  162. }
  163. }
  164. }
  165. export.exportCompletion = { [weak self] error, url in
  166. DispatchQueue.main.async { [weak self] in
  167. guard let sself = self else {
  168. return
  169. }
  170. UIApplication.shared.isIdleTimerDisabled = false
  171. sself.isExporting = false
  172. // sself.bottomView.isHidden = false
  173. sself.saveAllBtn.isEnabled = true
  174. sself.saveOnlyBtn.isEnabled = true
  175. sself.saveToPhotoBtn.isEnabled = true
  176. sself.completeBtn.isEnabled = true
  177. sself.sliderView?.isHidden = false
  178. sself.progressView.isHidden = true
  179. sself.progressL.isHidden = true
  180. if let fileUrl = url {
  181. let item = AVPlayerItem(url: fileUrl)
  182. sself.avplayer.replaceCurrentItem(with: item)
  183. sself.avplayer.play()
  184. if sself.saveAllBtn.isSelected {
  185. sself.saveAllUlr = fileUrl
  186. sself.hasExportAll = true
  187. }
  188. if sself.saveOnlyBtn.isSelected {
  189. sself.saveOnlyUlr = fileUrl
  190. sself.hasExportOnly = true
  191. }
  192. // 添加播放进度视图
  193. self?.addVideoSliderView()
  194. }else {
  195. if let l = sself.errorView.viewWithTag(33000) as? UILabel, let err = error as NSError? {
  196. switch err.code {
  197. case ExportError.FileNotExist.rawValue :
  198. l.text = "原视频/图片文件被删除"
  199. case ExportError.DiskNoSpace.rawValue :
  200. l.text = "手机存储空间不足"
  201. case ExportError.VoiceLost.rawValue :
  202. l.text = "手机存储空间不足"
  203. default:
  204. l.text = "请重新尝试"
  205. }
  206. }
  207. sself.errorView.isHidden = false
  208. }
  209. }
  210. // 合成成功上报
  211. BFEventTrackAdaptor.baseReportUpload(businessType: nil, objectType: .ot_composeSuccess, pageSource: .sp_composePage, commonParams: commonParams())
  212. }
  213. return export
  214. }()
  215. deinit {
  216. avplayerTimeObserver?.invalidate()
  217. NotificationCenter.default.removeObserver(self)
  218. }
  219. override func viewWillAppear(_ animated: Bool) {
  220. super.viewWillAppear(animated)
  221. PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
  222. PQNotification.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
  223. //进入活跃状态
  224. PQNotification.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
  225. //进入非活跃状态
  226. PQNotification.addObserver(self, selector: #selector(willResignActive), name: UIApplication.willResignActiveNotification, object: nil)
  227. showNavigation()
  228. // 曝光上报
  229. BFEventTrackAdaptor.baseReportUpload(businessType: .bt_pageView, objectType: nil, pageSource: .sp_composePage, commonParams: commonParams())
  230. }
  231. @objc func didBecomeActive(){
  232. BFLog(message: "进入活跃状态")
  233. if let sbtn = sliderView?.viewWithTag(1) as? UIButton ,!isExporting {
  234. sbtn.isSelected = false
  235. avplayer.play()
  236. }
  237. }
  238. @objc func willResignActive(){
  239. BFLog(message: "进入非活跃状态")
  240. if let sbtn = sliderView?.viewWithTag(1) as? UIButton , !isExporting{
  241. sbtn.isSelected = true
  242. avplayer.pause()
  243. }
  244. }
  245. @objc func enterBackground() {
  246. BFLog(message: "进入到后台")
  247. if isExporting {
  248. self.export.cancelExport()
  249. }else{
  250. if let sbtn = sliderView?.viewWithTag(1) as? UIButton {
  251. sbtn.isSelected = true
  252. avplayer.pause()
  253. }
  254. }
  255. }
  256. @objc func willEnterForeground() {
  257. BFLog(message: "进入到前台")
  258. export.startExprot(synthesisAll: saveAllBtn.isSelected)
  259. //重启播放器
  260. if let sbtn = sliderView?.viewWithTag(1) as? UIButton {
  261. sbtn.isSelected = false
  262. avplayer.play()
  263. }
  264. }
  265. override func viewWillDisappear(_ animated: Bool) {
  266. super.viewWillDisappear(animated)
  267. PQNotification.removeObserver(self)
  268. UIApplication.shared.isIdleTimerDisabled = false
  269. }
  270. override func backBtnClick() {
  271. if isExporting {
  272. whetherCancelExport {
  273. self.export.cancelExport()
  274. super.backBtnClick()
  275. }
  276. return
  277. }
  278. if !(hasExportAll || hasExportOnly) {}
  279. export.cancelExport()
  280. super.backBtnClick()
  281. }
  282. override func viewDidLoad() {
  283. super.viewDidLoad()
  284. view.backgroundColor = .black
  285. navHeadImageView?.backgroundColor = .black
  286. leftButton(image: nil, imageName: nil, tintColor: UIColor.white)
  287. // backV.frame = CGRect(x: 0, y: navHeadImageView?.bottomY ?? 0, width: cScreenWidth, height: cScreenWidth)
  288. backV.backgroundColor = .black
  289. // backV.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(play)))
  290. addSubviews()
  291. // export.startExprot()
  292. // 默认保留录音合成
  293. saveAllBtn.isSelected = true
  294. DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
  295. self?.exportNow()
  296. }
  297. }
  298. func addSubviews() {
  299. view.addSubview(bottomView)
  300. view.addSubview(backV)
  301. playerLayer = AVPlayerLayer(player: avplayer)
  302. backV.layer.addSublayer(playerLayer)
  303. backV.addSubview(progressView)
  304. backV.addSubview(progressL)
  305. backV.addSubview(errorView)
  306. bottomView.addSubview(saveAllBtn)
  307. bottomView.addSubview(saveOnlyBtn)
  308. bottomView.addSubview(saveToPhotoBtn)
  309. bottomView.addSubview(completeBtn)
  310. progressView.snp.makeConstraints { make in
  311. make.left.top.height.equalToSuperview()
  312. make.width.equalTo(0)
  313. }
  314. progressL.snp.makeConstraints { make in
  315. make.center.width.equalToSuperview()
  316. make.height.equalTo(28)
  317. }
  318. bottomView.snp.makeConstraints { make in
  319. make.left.right.equalToSuperview()
  320. if #available(iOS 11.0, *) {
  321. make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom)
  322. } else {
  323. make.bottom.equalToSuperview()
  324. }
  325. }
  326. completeBtn.snp.makeConstraints { make in
  327. make.left.equalTo(16)
  328. make.right.equalTo(-16)
  329. make.bottom.equalTo(-1 * cSafeAreaHeight - 3)
  330. make.height.equalTo(20)
  331. }
  332. saveToPhotoBtn.snp.makeConstraints { make in
  333. make.left.right.equalTo(completeBtn)
  334. make.height.equalTo(50)
  335. make.bottom.equalTo(completeBtn.snp.top).offset(-10)
  336. }
  337. saveAllBtn.snp.makeConstraints { make in
  338. make.left.equalTo(completeBtn)
  339. make.bottom.equalTo(saveToPhotoBtn.snp.top).offset(-21)
  340. make.height.equalTo(70)
  341. make.top.equalTo(28)
  342. }
  343. saveOnlyBtn.snp.makeConstraints { make in
  344. make.left.equalTo(saveAllBtn.snp.right).offset(16)
  345. make.top.height.width.equalTo(saveAllBtn)
  346. make.right.equalTo(completeBtn)
  347. }
  348. DispatchQueue.main.asyncAfter(deadline: .now() + 0) { [weak self] in
  349. self?.backV.frame = CGRect(x: 0, y: self?.navHeadImageView?.frame.maxY ?? 0, width: self?.view.width ?? 0, height: (self?.bottomView.frame.minY ?? 0) - (self?.navHeadImageView?.frame.maxY ?? 0))
  350. self?.playerLayer.frame = self!.backV.bounds
  351. }
  352. }
  353. /// 添加播放进度视图
  354. func addVideoSliderView() {
  355. if sliderView == nil {
  356. sliderView = BFVideoPlayerSliderView(frame: CGRect(x: 0, y: backV.frame.height - 60, width: backV.frame.width, height: 50))
  357. sliderView?.valueChangeBloc = { [weak self] sender in
  358. let cmtime = CMTime(value: CMTimeValue(Float64(sender.value) * Float64(self?.avplayer.currentItem?.asset.duration.seconds ?? 0) * 1000.0), timescale: CMTimeScale(1000.0))
  359. BFLog(message: "cmtime == \(cmtime),\(cmtime.seconds)")
  360. self?.avplayer.seek(to: cmtime, toleranceBefore: CMTime(seconds: 1, preferredTimescale: 1000), toleranceAfter: CMTime(seconds: 1, preferredTimescale: 1000))
  361. }
  362. sliderView?.btnClickBloc = { [weak self] sender in
  363. // 按钮点击
  364. if sender.tag == 1 {
  365. self?.play(sender: sender)
  366. } else if sender.tag == 2 {
  367. if sender.isSelected {
  368. self?.changeToFullScreen()
  369. } else {
  370. self?.changeToOriginalFrame()
  371. }
  372. }
  373. }
  374. }
  375. sliderView?.duration = avplayer.currentItem?.asset.duration.seconds ?? 0
  376. if sliderView?.superview == nil {
  377. backV.addSubview(sliderView!)
  378. }
  379. }
  380. // MARK: - 按钮事件
  381. @objc func retryAction(){
  382. exportNow()
  383. }
  384. func exportNow() {
  385. if isExporting {
  386. cShowHUB(superView: nil, msg: "正在合成中。。。")
  387. return
  388. }
  389. isExporting = true
  390. // bottomView.isHidden = true
  391. saveAllBtn.isEnabled = false
  392. saveOnlyBtn.isEnabled = false
  393. saveToPhotoBtn.isEnabled = false
  394. completeBtn.isEnabled = false
  395. progressView.isHidden = false
  396. progressL.isHidden = false
  397. sliderView?.isHidden = true
  398. export.startExprot(synthesisAll: saveAllBtn.isSelected)
  399. UIApplication.shared.isIdleTimerDisabled = true
  400. // 开始合成上报
  401. BFEventTrackAdaptor.baseReportUpload(businessType: nil, objectType: .ot_startCompose, pageSource: .sp_composePage, commonParams: commonParams())
  402. }
  403. @objc func saveToPhotoNow() {
  404. if (saveAllBtn.isSelected && hasSaveAll) || saveOnlyBtn.isSelected && hasSaveOnly {
  405. cShowHUB(superView: nil, msg: "已保存过了")
  406. return
  407. }
  408. if let url = (avplayer.currentItem?.asset as? AVURLAsset)?.url {
  409. PHPhotoLibrary.shared().performChanges {
  410. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)
  411. } completionHandler: { [weak self] isFinished, _ in
  412. guard let sself = self else {
  413. return
  414. }
  415. if isFinished {
  416. DispatchQueue.main.async {
  417. cShowHUB(superView: nil, msg: "保存成功")
  418. }
  419. if sself.saveAllBtn.isSelected {
  420. sself.hasSaveAll = true
  421. } else if sself.saveOnlyBtn.isSelected {
  422. sself.hasSaveOnly = true
  423. }
  424. }
  425. }
  426. }
  427. // 点击保存至相册上报
  428. BFEventTrackAdaptor.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_saveToAblum, pageSource: .sp_composePage, extParams: saveAllBtn.isSelected ? ["saveAll": true] : ["saveRecord": true], commonParams: commonParams())
  429. }
  430. @objc func saveAllAction(btn: UIButton) {
  431. if btn.isSelected {
  432. return
  433. }
  434. btn.isSelected = true
  435. saveOnlyBtn.isSelected = false
  436. avplayer.pause()
  437. if !hasExportAll {
  438. exportNow()
  439. } else {
  440. avplayer.pause()
  441. let item = AVPlayerItem(url: saveAllUlr)
  442. avplayer.replaceCurrentItem(with: item)
  443. if let sbtn = sliderView?.viewWithTag(1) as? UIButton {
  444. sbtn.isSelected = false
  445. avplayer.play()
  446. sliderView?.duration = avplayer.currentItem?.asset.duration.seconds ?? 0
  447. }
  448. }
  449. }
  450. @objc func saveOnlyAction(btn: UIButton) {
  451. if btn.isSelected {
  452. return
  453. }
  454. btn.isSelected = true
  455. saveAllBtn.isSelected = false
  456. avplayer.pause()
  457. if !hasExportOnly {
  458. exportNow()
  459. } else {
  460. avplayer.pause()
  461. let item = AVPlayerItem(url: saveOnlyUlr)
  462. avplayer.replaceCurrentItem(with: item)
  463. if let sbtn = sliderView?.viewWithTag(1) as? UIButton {
  464. sbtn.isSelected = false
  465. avplayer.play()
  466. sliderView?.duration = avplayer.currentItem?.asset.duration.seconds ?? 0
  467. }
  468. }
  469. }
  470. @objc func completeAction() {
  471. // MARK: 删除所有录制资源. 现在放在了选择相册展示时清理cache
  472. if (!hasSaveOnly && saveOnlyUlr.absoluteString != "aaa")
  473. || (!hasSaveAll && saveAllUlr.absoluteString != "aaa")
  474. {
  475. let remindData = BFBaseModel()
  476. remindData.summary = "合成的视频尚未保存到相册"
  477. let alertV = BFRemindView(frame: view.bounds)
  478. alertV.isBanned = true
  479. alertV.confirmBtn.setTitle("不保存", for: .normal)
  480. alertV.cancelBtn.setTitle("确认保存", for: .normal)
  481. alertV.remindData = remindData
  482. alertV.remindBlock = { [weak self] item, _ in
  483. guard let sself = self else {
  484. return
  485. }
  486. if item.tag == 1 { // 确定返回到上一层
  487. if sself.hasExportOnly {
  488. PHPhotoLibrary.shared().performChanges {
  489. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: sself.saveOnlyUlr)
  490. } completionHandler: { _, _ in
  491. }
  492. }
  493. if sself.hasExportAll {
  494. PHPhotoLibrary.shared().performChanges {
  495. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: sself.saveAllUlr)
  496. } completionHandler: { _, _ in
  497. }
  498. }
  499. }
  500. sself.export.cancelExport()
  501. sself.navigationController?.popToRootViewController(animated: true)
  502. }
  503. UIApplication.shared.keyWindow?.addSubview(alertV)
  504. } else {
  505. export.cancelExport()
  506. navigationController?.popToRootViewController(animated: true)
  507. }
  508. }
  509. @objc func play(sender: UIButton) {
  510. if isExporting {
  511. avplayer.pause()
  512. return
  513. }
  514. if avplayer.currentItem != nil {
  515. if avplayer.timeControlStatus == .playing || sender.isSelected {
  516. avplayer.pause()
  517. } else if avplayer.timeControlStatus == .paused {
  518. avplayer.play()
  519. }
  520. }
  521. }
  522. func whetherCancelExport(comfirm: (() -> Void)?) {
  523. let remindData = BFBaseModel()
  524. remindData.summary = "正在合成中,是否取消?"
  525. let alertV = BFRemindView(frame: view.bounds)
  526. alertV.isBanned = true
  527. alertV.confirmBtn.setTitle("继续合成", for: .normal)
  528. alertV.cancelBtn.setTitle("取消合成", for: .normal)
  529. alertV.remindData = remindData
  530. alertV.remindBlock = { [weak self] item, _ in
  531. guard let sself = self else {
  532. return
  533. }
  534. if item.tag == 1 { // 确定返回到上一层
  535. sself.export.cancelExport()
  536. comfirm?()
  537. }
  538. }
  539. UIApplication.shared.keyWindow?.addSubview(alertV)
  540. }
  541. }
  542. extension INVideoExportController {
  543. @objc func changeToOriginalFrame() {
  544. if !(sliderView?.isFullScreen ?? false) {
  545. isHiddenStatus = false // (0.0, 64.0, 375.0, 401.0)
  546. navHeadImageView?.isHidden = false
  547. backV.frame = CGRect(x: 0, y: navHeadImageView?.frame.maxY ?? 0, width: view.width, height: bottomView.frame.minY - (navHeadImageView?.frame.maxY ?? 0))
  548. playerLayer.frame = CGRect(x: 0, y: 0, width: view.width, height: bottomView.frame.minY - (navHeadImageView?.frame.maxY ?? 0))
  549. sliderView?.frame = CGRect(x: 0, y: backV.frame.height - 60, width: view.frame.width, height: 50)
  550. UIView.animate(withDuration: 0.2, animations: { [weak self] in
  551. self?.changeOrientation(orientation: .portrait)
  552. self?.backV.center = CGPoint(x: (self?.backV.width ?? 0) / 2, y: (self?.backV.height ?? 0) / 2 + (self?.navHeadImageView?.frame.maxY ?? 0))
  553. }) { [weak self] _ in
  554. }
  555. }
  556. }
  557. func changeToFullScreen() {
  558. if sliderView?.isFullScreen ?? false {
  559. isHiddenStatus = true
  560. navHeadImageView?.isHidden = true
  561. backV.frame = CGRect(x: 0, y: 0, width: view.frame.height, height: view.frame.width)
  562. playerLayer.frame = backV.bounds
  563. sliderView?.frame = CGRect(x: 0, y: backV.frame.height - 60, width: backV.frame.width, height: 50)
  564. UIView.animate(withDuration: 0.2, animations: { [weak self] in
  565. let orientation = UIDevice.current.orientation
  566. if orientation == .landscapeRight {
  567. self?.changeOrientation(orientation: .landscapeLeft)
  568. } else {
  569. self?.changeOrientation(orientation: .landscapeRight)
  570. }
  571. self?.backV.center = CGPoint(x: (self?.view.frame.width ?? 0) / 2, y: (self?.view.frame.height ?? 0) / 2)
  572. }) { [weak self] _ in
  573. }
  574. }
  575. }
  576. func changeOrientation(orientation: UIInterfaceOrientation) {
  577. UIView.animate(withDuration: 0.2, animations: { [weak self] in
  578. self?.backV.transform = self?.transformRotation(orientation: orientation) as! CGAffineTransform
  579. }) { [weak self] _ in
  580. }
  581. }
  582. @objc func transformRotation(orientation: UIInterfaceOrientation) -> CGAffineTransform {
  583. if orientation == .portrait {
  584. return .identity
  585. } else if orientation == .landscapeLeft {
  586. return CGAffineTransform(rotationAngle: -CGFloat.pi / 2)
  587. } else if orientation == .landscapeRight {
  588. return CGAffineTransform(rotationAngle: CGFloat.pi / 2)
  589. }
  590. return .identity
  591. }
  592. }