|
@@ -4,68 +4,202 @@
|
|
//
|
|
//
|
|
// Created by 100Years on 2025/3/26.
|
|
// Created by 100Years on 2025/3/26.
|
|
//
|
|
//
|
|
-
|
|
|
|
|
|
+import AVFoundation
|
|
class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelegate {
|
|
class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelegate {
|
|
-
|
|
|
|
|
|
+ //#################################### 数据区域 ####################################//
|
|
var titleName:String = "Edit".localized
|
|
var titleName:String = "Edit".localized
|
|
- override func createView() {
|
|
|
|
|
|
+
|
|
|
|
+ var ringModel: TSRingModel
|
|
|
|
+ var editOriginalURL:URL
|
|
|
|
+
|
|
|
|
+ init(ringModel: TSRingModel,editOriginalURL:URL) {
|
|
|
|
+ self.ringModel = ringModel
|
|
|
|
+ self.editOriginalURL = editOriginalURL
|
|
|
|
+ super.init()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @MainActor required init?(coder: NSCoder) {
|
|
|
|
+ fatalError("init(coder:) has not been implemented")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ //#################################### TSCustomStackView ####################################//
|
|
|
|
+ lazy var cusStackView: TSCustomStackView = {
|
|
|
|
+ let cusStackView = TSCustomStackView(axis: .vertical,spacing: 0)
|
|
|
|
+ return cusStackView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ //#################################### 名字 ####################################//
|
|
|
|
+ lazy var nameLabel: UILabel = {
|
|
|
|
+ let nameLabel = UILabel.createLabel(text: ringModel.title, font: .font(size: 14),textColor: .white.withAlphaComponent(0.8),textAlignment: .center)
|
|
|
|
+ return nameLabel
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ weak var nameInputTextField: UITextField?
|
|
|
|
+ lazy var nameButton: UIButton = {
|
|
|
|
+ let nameButton = UIButton.createButton(image: UIImage(named: "edit_field")) { [weak self] in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+
|
|
|
|
+ let alertVC = UIAlertController(title: nil, message: "Ringtone Name".localized, preferredStyle: .alert)
|
|
|
|
+ alertVC.addTextField { textField in
|
|
|
|
+ textField.placeholder = "input name".localized
|
|
|
|
+ textField.font = UIFont.systemFont(ofSize: 16)
|
|
|
|
+ textField.text = self.ringModel.title
|
|
|
|
+ self.nameInputTextField = textField
|
|
|
|
+ }
|
|
|
|
+ let ok = UIAlertAction(title: "OK".localized, style: .default) { [weak self] _ in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ nameLabel.text = nameInputTextField?.text
|
|
|
|
+
|
|
|
|
+ if let text = nameLabel.text{
|
|
|
|
+ ringModel.title = text
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ let cancel = UIAlertAction(title: "Cancel".localized, style: .cancel)
|
|
|
|
+ alertVC.addAction(cancel)
|
|
|
|
+ alertVC.addAction(ok)
|
|
|
|
+ present(alertVC, animated: true) {
|
|
|
|
+ self.nameInputTextField?.becomeFirstResponder()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ return nameButton
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ lazy var nameView: UIView = {
|
|
|
|
+ let nameView = UIView()
|
|
|
|
|
|
- setPageTitle(titleName)
|
|
|
|
|
|
+ let nameContentView = UIView()
|
|
|
|
+ nameView.addSubview(nameContentView)
|
|
|
|
+ nameContentView.snp.makeConstraints { make in
|
|
|
|
+ make.center.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ nameContentView.addSubview(nameLabel)
|
|
|
|
+ nameContentView.addSubview(nameButton)
|
|
|
|
|
|
|
|
+ nameLabel.snp.makeConstraints { make in
|
|
|
|
+ make.leading.equalToSuperview()
|
|
|
|
+ make.width.greaterThanOrEqualTo(220*kDesignScale)
|
|
|
|
+ make.height.equalTo(23)
|
|
|
|
+ make.top.bottom.equalTo(0)
|
|
|
|
+ }
|
|
|
|
+ nameButton.snp.makeConstraints { make in
|
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
|
+ make.leading.equalTo(nameLabel.snp.trailing)
|
|
|
|
+ make.width.height.equalTo(23)
|
|
|
|
+ make.top.bottom.equalTo(0)
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ return nameView
|
|
|
|
+ }()
|
|
|
|
|
|
|
|
+ //#################################### track编辑视频 ####################################//\
|
|
|
|
+ let trackContentViewLeft:CGFloat = 24.0
|
|
|
|
+ let trackViewH:CGFloat = 210.0
|
|
|
|
+ lazy var trackViewW:CGFloat = k_ScreenWidth - trackContentViewLeft * 2
|
|
|
|
+ lazy var trackView: ZHWaveformView = {
|
|
|
|
+ let waveform = ZHWaveformView(frame: CGRect(x: 0, y: 0, width: trackViewW, height:trackViewH),fileURL: editOriginalURL)
|
|
|
|
+ waveform.backgroundColor = .clear
|
|
|
|
+ waveform.beginningPartColor = .white.withAlphaComponent(0.2) // color
|
|
|
|
+ waveform.endPartColor = .white.withAlphaComponent(0.2)
|
|
|
|
+ waveform.wavesColor = "#7E57F4".uiColor
|
|
|
|
+ waveform.trackScale = 0.2// 0 ~ 1
|
|
|
|
+ waveform.waveformDelegate = self
|
|
|
|
+ waveform.croppedDelegate = self
|
|
|
|
+
|
|
|
|
+ return waveform
|
|
|
|
+ }()
|
|
|
|
|
|
- var nameLabel: UILabel!
|
|
|
|
- var nameButton: UIButton!
|
|
|
|
-
|
|
|
|
- var trackContentView: UIView!
|
|
|
|
- var trackBgView: UIView!
|
|
|
|
-
|
|
|
|
- var timeLabel: UILabel!
|
|
|
|
- var cutButton: UIButton!
|
|
|
|
- var playButton: UIButton!
|
|
|
|
- var undoButton: UIButton!
|
|
|
|
-
|
|
|
|
- var fadeinSlider: UISlider!
|
|
|
|
- var fadeinTimeLabel: UILabel!
|
|
|
|
- var fadeoutSlider: UISlider!
|
|
|
|
- var fadeoutTimeLabel: UILabel!
|
|
|
|
-
|
|
|
|
- var doneButton: UIButton!
|
|
|
|
-
|
|
|
|
- var dragLeftView: UIView!
|
|
|
|
- var startTimeLabel: UILabel!
|
|
|
|
- var dragRightView: UIView!
|
|
|
|
- var endTimeLabel: UILabel!
|
|
|
|
- var progressLine: UIView!
|
|
|
|
- weak var nameInputTextField: UITextField?
|
|
|
|
-
|
|
|
|
- var trackView: ZHWaveformView?
|
|
|
|
-
|
|
|
|
- private lazy var player = TSBusinessAudioPlayer()
|
|
|
|
- private lazy var audioTool = AudioTool()
|
|
|
|
-
|
|
|
|
- var ringModel: TSRingModel?
|
|
|
|
- lazy var operationCache: [TSRingModel] = []
|
|
|
|
- var tempMp3Path: String?
|
|
|
|
- var needClearTemp : Bool = false
|
|
|
|
- override func viewDidLoad() {
|
|
|
|
- super.viewDidLoad()
|
|
|
|
-
|
|
|
|
- view.backgroundColor = .clear
|
|
|
|
-// saveButton.set { [weak self] in
|
|
|
|
-// self?.saveButtonClick()
|
|
|
|
-// }
|
|
|
|
|
|
+ lazy var trackContentView: UIView = {
|
|
|
|
+ let trackContentView = UIView(frame: CGRectMake(24, 0, k_ScreenWidth-48, trackViewH+12))
|
|
|
|
|
|
- if let ring = ringModel {
|
|
|
|
- operationCache.append(ring)
|
|
|
|
|
|
+ trackContentView.addSubview(trackView)
|
|
|
|
+ trackView.snp.makeConstraints { make in
|
|
|
|
+ make.top.centerX.equalToSuperview()
|
|
|
|
+ make.width.equalTo(trackView.width)
|
|
|
|
+ make.height.equalTo(trackView.height)
|
|
}
|
|
}
|
|
-
|
|
|
|
-
|
|
|
|
- setupUI()
|
|
|
|
-
|
|
|
|
|
|
+ return trackContentView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ func creatDragView()->UIView{
|
|
|
|
+ let bgView = UIView()
|
|
|
|
+
|
|
|
|
+ let view1 = UIView.creatColor(color: .white)
|
|
|
|
+ bgView.addSubview(view1)
|
|
|
|
+ let view2 = UIView.creatColor(color: .white)
|
|
|
|
+ view2.cornerRadius = 6
|
|
|
|
+ bgView.addSubview(view2)
|
|
|
|
+
|
|
|
|
+ view1.snp.makeConstraints { make in
|
|
|
|
+ make.width.equalTo(2)
|
|
|
|
+ make.height.equalTo(trackViewH)
|
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
|
+ make.top.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ view2.snp.makeConstraints { make in
|
|
|
|
+ make.top.equalTo(view1.snp.bottom)
|
|
|
|
+ make.width.height.equalTo(12)
|
|
|
|
+ make.bottom.equalToSuperview()
|
|
|
|
+ make.leading.equalTo(12)
|
|
|
|
+ make.trailing.equalTo(-12)
|
|
|
|
+ }
|
|
|
|
+ return bgView
|
|
|
|
+ }
|
|
|
|
+ lazy var dragLeftView: UIView = creatDragView()
|
|
|
|
+ lazy var dragRightView: UIView = creatDragView()
|
|
|
|
+ var progressLine: UIView = UIView.creatColor(color: .themeColor)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ lazy var startTimeLabel: UILabel = {
|
|
|
|
+ let label = UILabel.createLabel(text: "00:00s",font: .font(size: 14.0),textColor: .lesserText)
|
|
|
|
+ return label
|
|
|
|
+ }()
|
|
|
|
+ var endTimeLabel: UILabel = {
|
|
|
|
+ let label = UILabel.createLabel(text: "10:00s",font: .font(size: 14.0),textColor: .lesserText)
|
|
|
|
+ return label
|
|
|
|
+ }()
|
|
|
|
+ var timeLabel: UILabel = {
|
|
|
|
+ let label = UILabel.createLabel(text: "00:00",font: .font(size: 26.0),textColor: .lesserText)
|
|
|
|
+ return label
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ lazy var timeView: UIView = {
|
|
|
|
+ let timeView = UIView()
|
|
|
|
+
|
|
|
|
+ timeView.addSubview(startTimeLabel)
|
|
|
|
+ timeView.addSubview(endTimeLabel)
|
|
|
|
+ timeView.addSubview(timeLabel)
|
|
|
|
+
|
|
|
|
+ startTimeLabel.snp.makeConstraints { make in
|
|
|
|
+ make.leading.equalTo(16)
|
|
|
|
+ make.top.equalTo(12)
|
|
|
|
+ make.height.equalTo(20)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ endTimeLabel.snp.makeConstraints { make in
|
|
|
|
+ make.trailing.equalTo(-16)
|
|
|
|
+ make.top.equalTo(12)
|
|
|
|
+ make.height.equalTo(20)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ timeLabel.snp.makeConstraints { make in
|
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
|
+ make.top.equalTo(45)
|
|
|
|
+ make.height.equalTo(26)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return timeView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ lazy var trackBgView: UIView = {
|
|
|
|
+ let trackBgView = UIView()
|
|
|
|
+
|
|
let leftPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.leftPanRecognizer(sender:)))
|
|
let leftPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.leftPanRecognizer(sender:)))
|
|
dragLeftView.addGestureRecognizer(leftPanRecognizer)
|
|
dragLeftView.addGestureRecognizer(leftPanRecognizer)
|
|
dragLeftView.isUserInteractionEnabled = true
|
|
dragLeftView.isUserInteractionEnabled = true
|
|
@@ -73,8 +207,289 @@ class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelega
|
|
let rightPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.rightPanRecognizer(sender:)))
|
|
let rightPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.rightPanRecognizer(sender:)))
|
|
dragRightView.addGestureRecognizer(rightPanRecognizer)
|
|
dragRightView.addGestureRecognizer(rightPanRecognizer)
|
|
dragRightView.isUserInteractionEnabled = true
|
|
dragRightView.isUserInteractionEnabled = true
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+ let gradientImageView = UIImageView.createImageView(imageName: "trackBg_gradient",contentMode: .scaleToFill)
|
|
|
|
+ trackContentView.addSubview(gradientImageView)
|
|
|
|
+ gradientImageView.snp.makeConstraints { make in
|
|
|
|
+ make.leading.top.trailing.equalToSuperview()
|
|
|
|
+ make.height.equalTo(trackViewH)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ trackBgView.addSubview(trackContentView)
|
|
|
|
+ trackContentView.snp.makeConstraints { make in
|
|
|
|
+ make.top.equalToSuperview()
|
|
|
|
+ make.leading.equalTo(trackContentView.x)
|
|
|
|
+ make.trailing.equalTo(-trackContentView.x)
|
|
|
|
+ make.height.equalTo(trackViewH)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ trackBgView.addSubview(dragLeftView)
|
|
|
|
+ dragLeftView.snp.makeConstraints { make in
|
|
|
|
+ make.top.leading.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ trackBgView.addSubview(dragRightView)
|
|
|
|
+ dragRightView.snp.makeConstraints { make in
|
|
|
|
+ make.top.equalToSuperview()
|
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
+ progressLine.isHidden = true
|
|
|
|
+ trackBgView.addSubview(progressLine)
|
|
|
|
+ progressLine.snp.makeConstraints { make in
|
|
|
|
+ make.top.leading.equalToSuperview()
|
|
|
|
+ make.width.equalTo(1)
|
|
|
|
+ make.height.equalTo(trackViewH)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ trackBgView.addSubview(timeView)
|
|
|
|
+ timeView.snp.makeConstraints { make in
|
|
|
|
+ make.top.equalTo(trackContentView.bottom)
|
|
|
|
+ make.trailing.leading.equalToSuperview()
|
|
|
|
+ make.height.equalTo(71)
|
|
|
|
+ make.bottom.equalTo(0)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return trackBgView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ //#################################### paly按钮 ####################################//
|
|
|
|
+
|
|
|
|
+ lazy var playButtonView: UIView = {
|
|
|
|
+ let playButtonView = UIView(frame: CGRectMake(0, 0, k_ScreenWidth, 18+80+18))
|
|
|
|
+
|
|
|
|
+ let playButtonBg = UIView()
|
|
|
|
+ playButtonBg.backgroundColor = .white.withAlphaComponent(0.1)
|
|
|
|
+ playButtonBg.cornerRadius = 40
|
|
|
|
+ playButtonView.addSubview(playButtonBg)
|
|
|
|
+ playButtonBg.snp.makeConstraints { make in
|
|
|
|
+ make.width.height.equalTo(80)
|
|
|
|
+ make.center.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ playButtonBg.addSubview(playButton)
|
|
|
|
+ playButton.snp.makeConstraints { make in
|
|
|
|
+ make.width.height.equalTo(60)
|
|
|
|
+ make.center.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return playButtonView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ lazy var playButton: UIButton = {//editaudio_play //editaudio_pause
|
|
|
|
+ let playButton = UIButton.createButton(image: UIImage(named: "editaudio_play")){ [weak self] in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ startPlay()
|
|
|
|
+ }
|
|
|
|
+ playButton.setImage(UIImage(named: "editaudio_pause"), for: .selected)
|
|
|
|
+ return playButton
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ //#################################### Fade in ####################################//
|
|
|
|
+ lazy var fadeinSliderView: TSEditAudioSliderView = {
|
|
|
|
+ let fadeinSliderView = TSEditAudioSliderView()
|
|
|
|
+ fadeinSliderView.leftLabel.text = "Fade in".localized
|
|
|
|
+ fadeinSliderView.rightLabel.text = "0s"
|
|
|
|
+ fadeinSliderView.slider.maximumValue = 5
|
|
|
|
+// fadeinSliderView.slider.value = 3
|
|
|
|
+ return fadeinSliderView
|
|
|
|
+ }()
|
|
|
|
+ lazy var fadeinSlider: UISlider = {
|
|
|
|
+ let slider = fadeinSliderView.slider
|
|
|
|
+ slider.addTarget(self, action: #selector(sliderBeginTap(_:)), for: .touchDown)
|
|
|
|
+ slider.addTarget(self, action: #selector(sliderEndTap(_:)), for: .touchUpInside)
|
|
|
|
+ slider.addTarget(self, action: #selector(sliderValueChanged(_:)), for: .valueChanged)
|
|
|
|
+ return slider
|
|
|
|
+ }()
|
|
|
|
+ //#################################### Fade out ####################################//
|
|
|
|
+
|
|
|
|
+ lazy var fadeoutSliderView: TSEditAudioSliderView = {
|
|
|
|
+ let fadeoutSliderView = TSEditAudioSliderView()
|
|
|
|
+ fadeoutSliderView.leftLabel.text = "Fade out".localized
|
|
|
|
+ fadeoutSliderView.rightLabel.text = "0s"
|
|
|
|
+ fadeoutSliderView.slider.maximumValue = 5
|
|
|
|
+// fadeoutSliderView.slider.value = 3
|
|
|
|
+ return fadeoutSliderView
|
|
|
|
+ }()
|
|
|
|
+ lazy var fadeoutSlider: UISlider = {
|
|
|
|
+ let slider = fadeoutSliderView.slider
|
|
|
|
+ slider.addTarget(self, action: #selector(sliderBeginTap(_:)), for: .touchDown)
|
|
|
|
+ slider.addTarget(self, action: #selector(sliderEndTap(_:)), for: .touchUpInside)
|
|
|
|
+ slider.addTarget(self, action: #selector(sliderValueChanged(_:)), for: .valueChanged)
|
|
|
|
+ return slider
|
|
|
|
+ }()
|
|
|
|
+ //#################################### Output Volume ####################################//
|
|
|
|
+ lazy var outputVolumeSliderView: TSEditAudioSliderView = {
|
|
|
|
+ let outputVolumeSliderView = TSEditAudioSliderView()
|
|
|
|
+ outputVolumeSliderView.leftLabel.text = "Output Volume".localized
|
|
|
|
+ outputVolumeSliderView.rightLabel.text = "100%"
|
|
|
|
+ outputVolumeSliderView.slider.minimumValue = 1.0
|
|
|
|
+ outputVolumeSliderView.slider.maximumValue = 2.0
|
|
|
|
+// outputVolumeSliderView.slider.value = 1.5
|
|
|
|
+ return outputVolumeSliderView
|
|
|
|
+ }()
|
|
|
|
+ lazy var outputVolumeSlider: UISlider = {
|
|
|
|
+ let slider = outputVolumeSliderView.slider
|
|
|
|
+ slider.addTarget(self, action: #selector(sliderBeginTap(_:)), for: .touchDown)
|
|
|
|
+ slider.addTarget(self, action: #selector(sliderEndTap(_:)), for: .touchUpInside)
|
|
|
|
+ slider.addTarget(self, action: #selector(sliderValueChanged(_:)), for: .valueChanged)
|
|
|
|
+ return slider
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ //#################################### 底部保存按钮等 ####################################//
|
|
|
|
+ lazy var cutButton: TSAppBtnView = { //保存
|
|
|
|
+ let cutButton = TSAppBtnView()
|
|
|
|
+ cutButton.setUpButton(style: .normalBorder, btnFrame: CGRectMake(0, 0, 98*kDesignScale, 48)) { [weak self] in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ saveButtonClick()
|
|
|
|
+ }
|
|
|
|
+ cutButton.button.setTitle("Save".localized, for: .normal)
|
|
|
|
+ return cutButton
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ lazy var doneButton: TSAppBtnView = { // set
|
|
|
|
+ let doneButton = TSAppBtnView()
|
|
|
|
+ doneButton.setUpButton(style: .normalSet, btnFrame: CGRectMake(0, 0, 233*kDesignScale, 48)) { [weak self] in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ setButtonClick()
|
|
|
|
+ }
|
|
|
|
+ doneButton.button.setTitle("Set Now".localized, for: .normal)
|
|
|
|
+ return doneButton
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ var undoButton: UIButton!//暂时没用
|
|
|
|
+
|
|
|
|
+ lazy var bottomBtnView: UIView = {
|
|
|
|
+ let bottomBtnView = UIView()
|
|
|
|
+
|
|
|
|
+ bottomBtnView.addSubview(cutButton)
|
|
|
|
+ bottomBtnView.addSubview(doneButton)
|
|
|
|
+
|
|
|
|
+ cutButton.snp.makeConstraints { make in
|
|
|
|
+ make.top.equalTo(16)
|
|
|
|
+ make.bottom.equalTo(-16)
|
|
|
|
+ make.leading.equalTo(16)
|
|
|
|
+ make.size.equalTo(cutButton.button.size)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ doneButton.snp.makeConstraints { make in
|
|
|
|
+ make.top.equalTo(16)
|
|
|
|
+ make.bottom.equalTo(-16)
|
|
|
|
+ make.trailing.equalTo(-16)
|
|
|
|
+ make.size.equalTo(doneButton.button.size)
|
|
|
|
+ }
|
|
|
|
+ return bottomBtnView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ //#################################### viewDidLoad####################################//
|
|
|
|
+ override func createData() {
|
|
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ override func createView() {
|
|
|
|
+ addNormalNavBarView()
|
|
|
|
+ setPageTitle(titleName)
|
|
|
|
+
|
|
|
|
+ contentView.addSubview(bottomBtnView)
|
|
|
|
+ bottomBtnView.snp.makeConstraints { make in
|
|
|
|
+ make.leading.trailing.equalTo(0)
|
|
|
|
+ make.bottom.equalTo(-k_Height_safeAreaInsetsBottom())
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ contentView.addSubview(cusStackView)
|
|
|
|
+ cusStackView.snp.makeConstraints { make in
|
|
|
|
+ make.top.leading.bottom.trailing.equalTo(0)
|
|
|
|
+ make.bottom.equalTo(bottomBtnView.snp.top)
|
|
|
|
+ }
|
|
|
|
+ setUpCusStackView()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func setUpCusStackView() {
|
|
|
|
+ cusStackView.addSubviewToStack(nameView)
|
|
|
|
+ nameView.snp.makeConstraints { make in
|
|
|
|
+ make.height.equalTo(56)
|
|
|
|
+ make.width.equalTo(k_ScreenWidth)
|
|
|
|
+ }
|
|
|
|
+ cusStackView.addSubviewToStack(trackBgView)
|
|
|
|
+ trackBgView.snp.makeConstraints { make in
|
|
|
|
+ make.height.equalTo(300)
|
|
|
|
+ make.width.equalTo(k_ScreenWidth)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cusStackView.addSubviewToStack(playButtonView)
|
|
|
|
+ playButtonView.snp.makeConstraints { make in
|
|
|
|
+ make.height.equalTo(playButtonView.height)
|
|
|
|
+ make.width.equalTo(playButtonView.width)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cusStackView.addSubviewToStack(fadeinSliderView)
|
|
|
|
+ fadeinSliderView.snp.makeConstraints { make in
|
|
|
|
+ make.height.equalTo(fadeinSliderView.height)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let spaceView = UIView()
|
|
|
|
+ cusStackView.addSubviewToStack(spaceView)
|
|
|
|
+ spaceView.snp.makeConstraints { make in
|
|
|
|
+ make.height.equalTo(8)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cusStackView.addSubviewToStack(fadeoutSliderView)
|
|
|
|
+ fadeoutSliderView.snp.makeConstraints { make in
|
|
|
|
+ make.height.equalTo(fadeinSliderView.height)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let spaceView1 = UIView()
|
|
|
|
+ cusStackView.addSubviewToStack(spaceView1)
|
|
|
|
+ spaceView1.snp.makeConstraints { make in
|
|
|
|
+ make.height.equalTo(8)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cusStackView.addSubviewToStack(outputVolumeSliderView)
|
|
|
|
+ outputVolumeSliderView.snp.makeConstraints { make in
|
|
|
|
+ make.height.equalTo(fadeinSliderView.height)
|
|
|
|
+ make.bottom.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _ = fadeinSlider
|
|
|
|
+ _ = fadeoutSlider
|
|
|
|
+ _ = outputVolumeSlider
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ override func dealThings() {
|
|
|
|
+ operationCache.append(ringModel)
|
|
|
|
+ reloadTrackView()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private lazy var player:TSBusinessAudioPlayer = {
|
|
|
|
+ let player = TSBusinessAudioPlayer()
|
|
|
|
+
|
|
|
|
+ player.currentTimeChangedHandle = { [weak self] current,total in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ handlePlayer(progressChanged: current, total: total)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ player.stateChangedHandle = { [weak self] state in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ handlePlayer(state: state)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return player
|
|
|
|
+ }()
|
|
|
|
+ private lazy var audioTool = AudioTool()
|
|
|
|
+ private var maxVolume:Float{
|
|
|
|
+ return outputVolume * 0.5
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ lazy var operationCache: [TSRingModel] = []
|
|
|
|
+ var tempMp3Path: String?
|
|
|
|
+ var needClearTemp : Bool = false
|
|
|
|
|
|
override func viewDidDisappear(_ animated: Bool) {
|
|
override func viewDidDisappear(_ animated: Bool) {
|
|
super.viewDidDisappear(animated)
|
|
super.viewDidDisappear(animated)
|
|
@@ -83,7 +498,7 @@ class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelega
|
|
deleteTempFiles()
|
|
deleteTempFiles()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func deleteTempFiles() {
|
|
func deleteTempFiles() {
|
|
if let tempMp3Path = tempMp3Path {
|
|
if let tempMp3Path = tempMp3Path {
|
|
let mp3Dir = NSString(string: tempMp3Path).deletingLastPathComponent
|
|
let mp3Dir = NSString(string: tempMp3Path).deletingLastPathComponent
|
|
@@ -97,191 +512,184 @@ class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelega
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
super.viewWillDisappear(animated)
|
|
|
|
+ // 恢复右滑返回手势
|
|
|
|
+ navigationController?.interactivePopGestureRecognizer?.isEnabled = true
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
super.viewWillAppear(animated)
|
|
- }
|
|
|
|
-
|
|
|
|
- lazy var previousBounds: CGRect = .zero
|
|
|
|
- override func viewDidLayoutSubviews() {
|
|
|
|
- super.viewDidLayoutSubviews()
|
|
|
|
|
|
+ // 禁用右滑返回手势
|
|
|
|
+ navigationController?.interactivePopGestureRecognizer?.isEnabled = false
|
|
|
|
|
|
- if previousBounds == .zero {
|
|
|
|
- previousBounds = view.bounds
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- self.reloadTrackView()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ // lazy var previousBounds: CGRect = .zero
|
|
|
|
+ // override func viewDidLayoutSubviews() {
|
|
|
|
+ // super.viewDidLayoutSubviews()
|
|
|
|
+ //
|
|
|
|
+ // if previousBounds == .zero {
|
|
|
|
+ // previousBounds = view.bounds
|
|
|
|
+ // DispatchQueue.main.async {
|
|
|
|
+ // self.reloadTrackView()
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
|
|
+
|
|
deinit {
|
|
deinit {
|
|
dePrint("RingEditViewController 销毁了")
|
|
dePrint("RingEditViewController 销毁了")
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func reloadTrackView() {
|
|
func reloadTrackView() {
|
|
- guard let current = ringModel else {
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- var ringFilePath: String?
|
|
|
|
- if let fileName = current.fileName,
|
|
|
|
- let filePath = RingDownloadManager.shared.filePath(with: fileName),
|
|
|
|
- FileManager.default.fileExists(atPath: filePath) {
|
|
|
|
- ringFilePath = filePath
|
|
|
|
- } else if let filePath = RingDownloadManager.shared.filePath(for: current.fileUrl),
|
|
|
|
- FileManager.default.fileExists(atPath: filePath) {
|
|
|
|
- ringFilePath = filePath
|
|
|
|
- }
|
|
|
|
- guard let ringFilePath = ringFilePath else {
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tempMp3Path = ringFilePath
|
|
|
|
|
|
+
|
|
|
|
+ tempMp3Path = editOriginalURL.path
|
|
|
|
|
|
- trackView?.removeFromSuperview()
|
|
|
|
startCropRate = 0
|
|
startCropRate = 0
|
|
endCropRate = 1.0
|
|
endCropRate = 1.0
|
|
dragLeftView.centerX = trackContentView.x
|
|
dragLeftView.centerX = trackContentView.x
|
|
dragRightView.centerX = trackContentView.frame.maxX
|
|
dragRightView.centerX = trackContentView.frame.maxX
|
|
-
|
|
|
|
- let url = URL(fileURLWithPath: ringFilePath)
|
|
|
|
-
|
|
|
|
- let waveform = ZHWaveformView(
|
|
|
|
- frame: CGRect(x: 0, y: 20, width: trackContentView.width, height: trackContentView.height - 40),
|
|
|
|
- fileURL: url
|
|
|
|
- )
|
|
|
|
- waveform.backgroundColor = .clear
|
|
|
|
-
|
|
|
|
- // color
|
|
|
|
- waveform.beginningPartColor = .white.withAlphaComponent(0.2)
|
|
|
|
- waveform.endPartColor = .white.withAlphaComponent(0.2)
|
|
|
|
- waveform.wavesColor = "#55A0E9".uiColor
|
|
|
|
-
|
|
|
|
- // 0 ~ 1
|
|
|
|
- waveform.trackScale = 0.2
|
|
|
|
-
|
|
|
|
- waveform.waveformDelegate = self
|
|
|
|
- waveform.croppedDelegate = self
|
|
|
|
-
|
|
|
|
- trackContentView.insertSubview(waveform, at: 0)
|
|
|
|
- trackView = waveform
|
|
|
|
-
|
|
|
|
- timeLabel.text = current.duration.mmss
|
|
|
|
- undoButton.isEnabled = operationCache.count > 1
|
|
|
|
- cutButton.isEnabled = false
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ timeLabel.text = ringModel.duration.mmss
|
|
|
|
+// undoButton.isEna bled = operationCache.count > 1
|
|
|
|
+ cutButton.setBtnEnabled(isEnabled: false)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func setupUI() {
|
|
func setupUI() {
|
|
- nameLabel.text = ringModel?.title ?? ""
|
|
|
|
-
|
|
|
|
- doneButton.titleLabel?.numberOfLines = 2
|
|
|
|
- doneButton.titleLabel?.textAlignment = .center
|
|
|
|
- doneButton.setTitle("Export to GarageBand\nSet Ringtones".localized, for: .normal)
|
|
|
|
- doneButton.setGradient(colors: AppTheme.gradientColors, index: 0)
|
|
|
|
-
|
|
|
|
- fadeinSlider.setThumbImage(UIImage(named: "ic-ring-edit-slider"), for: .normal)
|
|
|
|
- fadeinSlider.addTarget(self, action: #selector(sliderBeginTap(_:)), for: .touchDown)
|
|
|
|
- fadeinSlider.addTarget(self, action: #selector(sliderEndTap(_:)), for: .touchUpInside)
|
|
|
|
- fadeinSlider.addTarget(self, action: #selector(sliderValueChanged(_:)), for: .valueChanged)
|
|
|
|
-
|
|
|
|
- fadeoutSlider.setThumbImage(UIImage(named: "ic-ring-edit-slider"), for: .normal)
|
|
|
|
- fadeoutSlider.addTarget(self, action: #selector(sliderBeginTap(_:)), for: .touchDown)
|
|
|
|
- fadeoutSlider.addTarget(self, action: #selector(sliderEndTap(_:)), for: .touchUpInside)
|
|
|
|
- fadeoutSlider.addTarget(self, action: #selector(sliderValueChanged(_:)), for: .valueChanged)
|
|
|
|
|
|
+
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ override func navBarClickLeftAction() {
|
|
|
|
+ TSCustomAlertController.show(in: self, config: TSCustomAlertController.AlertConfig(
|
|
|
|
+ message: "As you leave, the changes you have made will be lost.".localized,
|
|
|
|
+ messageColor: .red,
|
|
|
|
+ messageFont: .systemFont(ofSize: 15, weight: .medium),
|
|
|
|
+
|
|
|
|
+ cancelTitle: "Leave".localized,
|
|
|
|
+ cancelColor: .textAssist,
|
|
|
|
+
|
|
|
|
+ confirmTitle: "Stay".localized,
|
|
|
|
+ confirmColor: .themeColor,
|
|
|
|
+
|
|
|
|
+ cancelAction: {
|
|
|
|
+ print("用户点击了Leave")
|
|
|
|
+ super.navBarClickLeftAction()
|
|
|
|
+ },
|
|
|
|
+ confirmAction: {
|
|
|
|
+ print("用户点击了Stay")
|
|
|
|
+ }
|
|
|
|
+ ))
|
|
|
|
+ }
|
|
|
|
+ //#################################### UI 储存属性 ####################################//
|
|
lazy var fadeInDuration: Int = 0 {
|
|
lazy var fadeInDuration: Int = 0 {
|
|
didSet {
|
|
didSet {
|
|
- fadeinTimeLabel.text = "\(fadeInDuration)s"
|
|
|
|
|
|
+ fadeinSliderView.rightLabel.text = "\(fadeInDuration)s"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
lazy var fadeOutDuration: Int = 0 {
|
|
lazy var fadeOutDuration: Int = 0 {
|
|
didSet {
|
|
didSet {
|
|
- fadeoutTimeLabel.text = "\(fadeOutDuration)s"
|
|
|
|
|
|
+ fadeoutSliderView.rightLabel.text = "\(fadeOutDuration)s"
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ lazy var outputVolume: Float = 1.0 {
|
|
|
|
+ didSet {
|
|
|
|
+ outputVolumeSliderView.rightLabel.text = "\(Int(outputVolume*100.0))%"
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ lazy var previousLeftX: CGFloat = 0
|
|
|
|
+ lazy var startCropRate: CGFloat = 0 {
|
|
|
|
+ didSet {
|
|
|
|
+ let time = startDuration
|
|
|
|
+ cutButton.setBtnEnabled(isEnabled: true)
|
|
|
|
+
|
|
|
|
+ // print("---start: \(time)")
|
|
|
|
+ startTimeLabel.text = time.mmss
|
|
|
|
+ timeLabel.text = (endDuration - startDuration).mmss
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // 拖拽时被暂停,拖拽结束后,继续播放
|
|
|
|
+ lazy var isSuspendByAction = false
|
|
|
|
+ lazy var endCropRate: CGFloat = 1.0 {
|
|
|
|
+ didSet {
|
|
|
|
+ cutButton.setBtnEnabled(isEnabled: true)
|
|
|
|
+ let time = endDuration
|
|
|
|
+ endTimeLabel.text = time.mmss
|
|
|
|
+ timeLabel.text = (endDuration - startDuration).mmss
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ lazy var previousRightX: CGFloat = 0
|
|
|
|
+}
|
|
|
|
|
|
|
|
+
|
|
|
|
+extension TSEditAudioVideoBaseVC {
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
@objc func sliderBeginTap(_ slider: UISlider) {
|
|
@objc func sliderBeginTap(_ slider: UISlider) {
|
|
suspendPlay()
|
|
suspendPlay()
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
@objc func sliderEndTap(_ slider: UISlider) {
|
|
@objc func sliderEndTap(_ slider: UISlider) {
|
|
autoPlayAfterMove()
|
|
autoPlayAfterMove()
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
@objc func sliderValueChanged(_ slider: UISlider) {
|
|
@objc func sliderValueChanged(_ slider: UISlider) {
|
|
-// print("---\(slider.value)")
|
|
|
|
- if player.state == .playing {
|
|
|
|
|
|
+ // print("---\(slider.value)")
|
|
|
|
+ if player.isPlaying {
|
|
player.pause()
|
|
player.pause()
|
|
}
|
|
}
|
|
if slider == fadeinSlider {
|
|
if slider == fadeinSlider {
|
|
fadeInDuration = Int(slider.value.rounded(.toNearestOrEven))
|
|
fadeInDuration = Int(slider.value.rounded(.toNearestOrEven))
|
|
} else if slider == fadeoutSlider {
|
|
} else if slider == fadeoutSlider {
|
|
fadeOutDuration = Int(slider.value.rounded(.toNearestOrEven))
|
|
fadeOutDuration = Int(slider.value.rounded(.toNearestOrEven))
|
|
|
|
+ } else if slider == outputVolumeSlider {
|
|
|
|
+ outputVolume = slider.value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- lazy var startCropRate: CGFloat = 0 {
|
|
|
|
- didSet {
|
|
|
|
- let time = startDuration
|
|
|
|
- cutButton.isEnabled = true
|
|
|
|
-
|
|
|
|
-// print("---start: \(time)")
|
|
|
|
- startTimeLabel.text = time.mmss
|
|
|
|
- timeLabel.text = (endDuration - startDuration).mmss
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+
|
|
// 裁剪起始时间
|
|
// 裁剪起始时间
|
|
var startDuration: Double {
|
|
var startDuration: Double {
|
|
- guard let model = ringModel else {
|
|
|
|
- return 0
|
|
|
|
- }
|
|
|
|
- return startCropRate * model.duration
|
|
|
|
|
|
+ return startCropRate * CGFloat(ringModel.duration)
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
|
|
- lazy var previousLeftX: CGFloat = 0
|
|
|
|
-
|
|
|
|
|
|
+
|
|
var startMinCenterX: CGFloat {
|
|
var startMinCenterX: CGFloat {
|
|
return trackContentView.x
|
|
return trackContentView.x
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
var startMaxCenterX: CGFloat {
|
|
var startMaxCenterX: CGFloat {
|
|
- guard let model = ringModel else {
|
|
|
|
- return 0
|
|
|
|
- }
|
|
|
|
- let ratio = (endCropRate * model.duration - 10) / model.duration
|
|
|
|
|
|
+ let ratio = (endCropRate * CGFloat(ringModel.duration) - 10) / CGFloat(ringModel.duration)
|
|
return ratio * trackContentView.width + trackContentView.x
|
|
return ratio * trackContentView.width + trackContentView.x
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
var endMinCenterX: CGFloat {
|
|
var endMinCenterX: CGFloat {
|
|
- guard let model = ringModel else {
|
|
|
|
- return 0
|
|
|
|
- }
|
|
|
|
- let ratio = (startCropRate * model.duration + 10) / model.duration
|
|
|
|
|
|
+ let ratio = (startCropRate * CGFloat(ringModel.duration) + 10) / CGFloat(ringModel.duration)
|
|
return ratio * trackContentView.width + trackContentView.x
|
|
return ratio * trackContentView.width + trackContentView.x
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
var endMaxCenterX: CGFloat {
|
|
var endMaxCenterX: CGFloat {
|
|
return trackContentView.frame.maxX
|
|
return trackContentView.frame.maxX
|
|
}
|
|
}
|
|
|
|
|
|
- // 拖拽时被暂停,拖拽结束后,继续播放
|
|
|
|
- lazy var isSuspendByAction = false
|
|
|
|
@objc private func leftPanRecognizer(sender: UIPanGestureRecognizer) {
|
|
@objc private func leftPanRecognizer(sender: UIPanGestureRecognizer) {
|
|
|
|
+
|
|
|
|
+ dePrint("leftPanRecognizer=\(sender)")
|
|
let limitMinCenterX: CGFloat = startMinCenterX
|
|
let limitMinCenterX: CGFloat = startMinCenterX
|
|
let limitMaxCenterX: CGFloat = startMaxCenterX
|
|
let limitMaxCenterX: CGFloat = startMaxCenterX
|
|
guard limitMaxCenterX > limitMinCenterX else {
|
|
guard limitMaxCenterX > limitMinCenterX else {
|
|
if sender.state == .began {
|
|
if sender.state == .began {
|
|
- THUD.toast("No less than 10s".localized())
|
|
|
|
|
|
+ dePrint("rightPanRecognizer No less than 10s")
|
|
|
|
+ TSToastShared.showToast(text:"No less than 10s".localized)
|
|
}
|
|
}
|
|
return
|
|
return
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
if sender.state == .began {
|
|
if sender.state == .began {
|
|
suspendPlay()
|
|
suspendPlay()
|
|
} else if sender.state == .changed {
|
|
} else if sender.state == .changed {
|
|
@@ -293,17 +701,17 @@ class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelega
|
|
center.x < limitMaxCenterX else {
|
|
center.x < limitMaxCenterX else {
|
|
// 越界,拖不动了, 最少10s
|
|
// 越界,拖不动了, 最少10s
|
|
if endDuration - startDuration < 11 {
|
|
if endDuration - startDuration < 11 {
|
|
- THUD.toast("No less than 10s".localized())
|
|
|
|
|
|
+ TSToastShared.showToast(text:"No less than 10s".localized)
|
|
}
|
|
}
|
|
return
|
|
return
|
|
}
|
|
}
|
|
dragLeftView.center = center
|
|
dragLeftView.center = center
|
|
-
|
|
|
|
|
|
+
|
|
} else if sender.state == .ended || sender.state == .failed {
|
|
} else if sender.state == .ended || sender.state == .failed {
|
|
previousLeftX = dragLeftView.center.x
|
|
previousLeftX = dragLeftView.center.x
|
|
autoPlayAfterMove()
|
|
autoPlayAfterMove()
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// 边界校验
|
|
// 边界校验
|
|
if dragLeftView.centerX < limitMinCenterX {
|
|
if dragLeftView.centerX < limitMinCenterX {
|
|
dragLeftView.centerX = limitMinCenterX
|
|
dragLeftView.centerX = limitMinCenterX
|
|
@@ -311,43 +719,32 @@ class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelega
|
|
if dragLeftView.centerX > limitMaxCenterX {
|
|
if dragLeftView.centerX > limitMaxCenterX {
|
|
dragLeftView.centerX = limitMaxCenterX
|
|
dragLeftView.centerX = limitMaxCenterX
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
let position = dragLeftView.centerX - trackContentView.x
|
|
let position = dragLeftView.centerX - trackContentView.x
|
|
- trackView?.updateLeftCroppedPosition(position)
|
|
|
|
|
|
+ trackView.updateLeftCroppedPosition(position)
|
|
startCropRate = position / trackContentView.width
|
|
startCropRate = position / trackContentView.width
|
|
progressLine.centerX = dragLeftView.centerX
|
|
progressLine.centerX = dragLeftView.centerX
|
|
}
|
|
}
|
|
-
|
|
|
|
- lazy var endCropRate: CGFloat = 1.0 {
|
|
|
|
- didSet {
|
|
|
|
- cutButton.isEnabled = true
|
|
|
|
-
|
|
|
|
- let time = endDuration
|
|
|
|
-// print("---end: \(time)")
|
|
|
|
- endTimeLabel.text = time.mmss
|
|
|
|
- timeLabel.text = (endDuration - startDuration).mmss
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
|
|
var endDuration: Double {
|
|
var endDuration: Double {
|
|
- guard let model = ringModel else {
|
|
|
|
- return 0
|
|
|
|
- }
|
|
|
|
- return endCropRate * model.duration
|
|
|
|
|
|
+ return endCropRate * CGFloat(CGFloat(ringModel.duration))
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
|
|
- lazy var previousRightX: CGFloat = 0
|
|
|
|
@objc private func rightPanRecognizer(sender: UIPanGestureRecognizer) {
|
|
@objc private func rightPanRecognizer(sender: UIPanGestureRecognizer) {
|
|
|
|
+ dePrint("rightPanRecognizer=\(sender)")
|
|
let limitMinCenterX: CGFloat = endMinCenterX
|
|
let limitMinCenterX: CGFloat = endMinCenterX
|
|
let limitMaxCenterX: CGFloat = endMaxCenterX
|
|
let limitMaxCenterX: CGFloat = endMaxCenterX
|
|
guard limitMaxCenterX > limitMinCenterX else {
|
|
guard limitMaxCenterX > limitMinCenterX else {
|
|
if sender.state == .began {
|
|
if sender.state == .began {
|
|
|
|
+ dePrint("rightPanRecognizer No less than 10s")
|
|
// 越界,拖不动了
|
|
// 越界,拖不动了
|
|
- THUD.toast("No less than 10s".localized())
|
|
|
|
|
|
+ TSToastShared.showToast(text:"No less than 10s".localized)
|
|
}
|
|
}
|
|
return
|
|
return
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
if sender.state == .began {
|
|
if sender.state == .began {
|
|
suspendPlay()
|
|
suspendPlay()
|
|
} else if sender.state == .changed {
|
|
} else if sender.state == .changed {
|
|
@@ -357,7 +754,7 @@ class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelega
|
|
guard center.x > limitMinCenterX, center.x < limitMaxCenterX else {
|
|
guard center.x > limitMinCenterX, center.x < limitMaxCenterX else {
|
|
// 越界,拖不动了, 最少10s
|
|
// 越界,拖不动了, 最少10s
|
|
if endDuration - startDuration < 11 {
|
|
if endDuration - startDuration < 11 {
|
|
- THUD.toast("No less than 10s".localized())
|
|
|
|
|
|
+ TSToastShared.showToast(text:"No less than 10s".localized)
|
|
}
|
|
}
|
|
return
|
|
return
|
|
}
|
|
}
|
|
@@ -366,7 +763,7 @@ class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelega
|
|
previousRightX = dragRightView.centerX
|
|
previousRightX = dragRightView.centerX
|
|
autoPlayAfterMove()
|
|
autoPlayAfterMove()
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// 边界校验
|
|
// 边界校验
|
|
if dragRightView.centerX > limitMaxCenterX {
|
|
if dragRightView.centerX > limitMaxCenterX {
|
|
dragRightView.centerX = limitMaxCenterX
|
|
dragRightView.centerX = limitMaxCenterX
|
|
@@ -374,228 +771,218 @@ class TSEditAudioVideoBaseVC: TSBaseVC , ZHCroppedDelegate, ZHWaveformViewDelega
|
|
if dragRightView.centerX < limitMinCenterX {
|
|
if dragRightView.centerX < limitMinCenterX {
|
|
dragRightView.centerX = limitMinCenterX
|
|
dragRightView.centerX = limitMinCenterX
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
let position = dragRightView.centerX - trackContentView.x
|
|
let position = dragRightView.centerX - trackContentView.x
|
|
- trackView?.updateRightCroppedPosition(position)
|
|
|
|
|
|
+ trackView.updateRightCroppedPosition(position)
|
|
endCropRate = position / trackContentView.width
|
|
endCropRate = position / trackContentView.width
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
@IBAction func buttonClick(_ sender: UIButton) {
|
|
@IBAction func buttonClick(_ sender: UIButton) {
|
|
- guard let model = ringModel else { return }
|
|
|
|
-
|
|
|
|
- switch sender {
|
|
|
|
- case playButton:
|
|
|
|
- startPlay()
|
|
|
|
- case undoButton:
|
|
|
|
- player.pause()
|
|
|
|
- operationCache.removeLast()
|
|
|
|
- ringModel = operationCache.last
|
|
|
|
- reloadTrackView()
|
|
|
|
- case cutButton:
|
|
|
|
- startCutAudio { [weak self] newModel, errMsg in
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- if let newModel = newModel {
|
|
|
|
- self?.operationCache.append(newModel)
|
|
|
|
- self?.ringModel = newModel
|
|
|
|
- self?.reloadTrackView()
|
|
|
|
- } else {
|
|
|
|
- THUD.toast(errMsg ?? "Sorry, Edit Failure".localized())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- case doneButton:
|
|
|
|
- player.pause()
|
|
|
|
- startCutAudio { [weak self] result, errMsg in
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- if let ringModel = result {
|
|
|
|
- THUD.showLoading()
|
|
|
|
- RingDownloadManager.shared.shareBand(with: ringModel) { _ in
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- THUD.hide()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- THUD.toast(errMsg ?? "Sorry, Edit Failure".localized())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- case nameButton:
|
|
|
|
- let alertVC = UIAlertController(title: nil, message: "Ringtone Name".localized(), preferredStyle: .alert)
|
|
|
|
- alertVC.addTextField { textField in
|
|
|
|
- textField.placeholder = "input name".localized()
|
|
|
|
- textField.font = UIFont.systemFont(ofSize: 16)
|
|
|
|
- textField.text = self.ringModel?.title
|
|
|
|
- self.nameInputTextField = textField
|
|
|
|
- }
|
|
|
|
- let ok = UIAlertAction(title: "OK".localized(), style: .default) { [weak self] _ in
|
|
|
|
- self?.ringModel?.title = self?.nameInputTextField?.text
|
|
|
|
- self?.nameLabel.text = self?.ringModel?.title
|
|
|
|
- }
|
|
|
|
- let cancel = UIAlertAction(title: "Cancel".localized(), style: .cancel)
|
|
|
|
- alertVC.addAction(cancel)
|
|
|
|
- alertVC.addAction(ok)
|
|
|
|
- present(alertVC, animated: true) {
|
|
|
|
- self.nameInputTextField?.becomeFirstResponder()
|
|
|
|
- }
|
|
|
|
- default:
|
|
|
|
- break
|
|
|
|
- }
|
|
|
|
|
|
+ let model = ringModel
|
|
|
|
+
|
|
|
|
+ // switch sender {
|
|
|
|
+ // case playButton:
|
|
|
|
+ // startPlay()
|
|
|
|
+ // case undoButton:
|
|
|
|
+ // player.pause()
|
|
|
|
+ // operationCache.removeLast()
|
|
|
|
+ // ringModel = operationCache.last
|
|
|
|
+ // reloadTrackView()
|
|
|
|
+ // case cutButton:
|
|
|
|
+ // startCutAudio { [weak self] newModel, errMsg in
|
|
|
|
+ // DispatchQueue.main.async {
|
|
|
|
+ // if let newModel = newModel {
|
|
|
|
+ // self?.operationCache.append(newModel)
|
|
|
|
+ // self?.ringModel = newModel
|
|
|
|
+ // self?.reloadTrackView()
|
|
|
|
+ // } else {
|
|
|
|
+ // TSToastShared.showToast(text:errMsg ?? "Sorry, Edit Failure".localized)
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
|
|
+ // case doneButton:
|
|
|
|
+ // player.pause()
|
|
|
|
+ // startCutAudio { [weak self] result, errMsg in
|
|
|
|
+ // guard let self = self else { return }
|
|
|
|
+ // DispatchQueue.main.async {
|
|
|
|
+ // if let ringModel = result {
|
|
|
|
+ // TSToastShared.showLoading(containerView: self.view)
|
|
|
|
+ //// RingDownloadManager.shared.shareBand(with: ringModel) { _ in
|
|
|
|
+ //// DispatchQueue.main.async {
|
|
|
|
+ //// TSToastShared.hideLoading()
|
|
|
|
+ //// }
|
|
|
|
+ //// }
|
|
|
|
+ // } else {
|
|
|
|
+ // TSToastShared.showToast(text:errMsg ?? "Sorry, Edit Failure".localized)
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ // default:
|
|
|
|
+ // break
|
|
|
|
+ // }
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// 拖拽,暂停
|
|
// 拖拽,暂停
|
|
func suspendPlay() {
|
|
func suspendPlay() {
|
|
- if player.state == .playing {
|
|
|
|
|
|
+ if player.isPlaying {
|
|
isSuspendByAction = true
|
|
isSuspendByAction = true
|
|
player.pause()
|
|
player.pause()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// 停止拖拽,继续播放
|
|
// 停止拖拽,继续播放
|
|
func autoPlayAfterMove() {
|
|
func autoPlayAfterMove() {
|
|
-// if isSuspendByAction {
|
|
|
|
startPlay()
|
|
startPlay()
|
|
isSuspendByAction = false
|
|
isSuspendByAction = false
|
|
-// }
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func startPlay() {
|
|
func startPlay() {
|
|
- guard let model = ringModel else { return }
|
|
|
|
-
|
|
|
|
|
|
+ let model = ringModel
|
|
|
|
+
|
|
playButton.isSelected = !playButton.isSelected
|
|
playButton.isSelected = !playButton.isSelected
|
|
- if player.state == .playing {
|
|
|
|
|
|
+ if player.isPlaying {
|
|
player.pause()
|
|
player.pause()
|
|
} else {
|
|
} else {
|
|
- if let playURL = model.playURL {
|
|
|
|
- player.play(playURL)
|
|
|
|
- DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
|
|
- self.player.seek(self.startDuration)
|
|
|
|
|
|
+
|
|
|
|
+ player.playUrlString(model.audioUrl,localURL: editOriginalURL)
|
|
|
|
+// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
|
|
+ self.player.seek(to:self.startDuration)
|
|
|
|
+// }
|
|
|
|
+ player.setVolume(volume: fadeInDuration > 0 ? 0 : maxVolume)
|
|
|
|
+
|
|
|
|
+ if UIApplication.getSystemVolume() < 0.1 {
|
|
|
|
+ TSToastShared.showToast(text:"Please turn up the volume".localized)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func saveButtonClick() {
|
|
|
|
+ player.pause()
|
|
|
|
+ // 保存音频
|
|
|
|
+ startCutAudio { result, errMsg,savePath in
|
|
|
|
+ if let ringModel = result {
|
|
|
|
+ // 裁剪的音频,使用本地文件播放,清空网络url
|
|
|
|
+ TSMineRintoneHistory.shared.saveModel(model: ringModel)
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ self.pop()
|
|
|
|
+ if let window = WindowHelper.getKeyWindow() {
|
|
|
|
+ kSaveSuccesswShared.show(atView:window,text: "Saved in “My Ringtone”".localized) { [weak self] in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ if let vc = WindowHelper.getCurrentViewController(){
|
|
|
|
+ kPushVC(target: vc, modelVC: TSRingDownVC())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- player.set(volum: fadeInDuration > 0 ? 0 : 1)
|
|
|
|
-
|
|
|
|
- if UIApplication.getSystemVolume() < 0.1 {
|
|
|
|
- THUD.toast("Please turn up the volume".localized(), shake: true)
|
|
|
|
|
|
+ } else {
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ TSToastShared.showToast(text: errMsg ?? "Sorry, Save Failure".localized)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- // 裁剪
|
|
|
|
- func startCutAudio(completion: ((RingModel?, String?) -> Void)?) {
|
|
|
|
- guard let model = ringModel,
|
|
|
|
- let filePath = RingDownloadManager.shared.ringFilePath(for: model),
|
|
|
|
- let copyModel = model.copy() as? RingModel else {
|
|
|
|
- completion?(nil, nil)
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- let url = URL(fileURLWithPath: filePath)
|
|
|
|
- let asset = AVAsset(url: url)
|
|
|
|
-
|
|
|
|
- let formatter = DateFormatter()
|
|
|
|
- formatter.dateFormat = "yyyyMMddHHmmss"
|
|
|
|
-
|
|
|
|
- var editName = model.title ?? formatter.string(from: Date())
|
|
|
|
- editName = sanitizeFilePath(editName)
|
|
|
|
- if !url.pathExtension.isEmpty {
|
|
|
|
- editName.append(".\(url.pathExtension)")
|
|
|
|
|
|
+
|
|
|
|
+ func setButtonClick() {
|
|
|
|
+ player.pause()
|
|
|
|
+ // 保存音频
|
|
|
|
+ startCutAudio { result, errMsg,savePath in
|
|
|
|
+ if let ringModel = result,let savePath = savePath {
|
|
|
|
+ // 裁剪的音频,使用本地文件播放,清空网络url
|
|
|
|
+ TSMineRintoneHistory.shared.saveModel(model: ringModel)
|
|
|
|
+ _ = kPurchaseToolShared.kshareBand(needVip: false, vc: self, fileURL:savePath, fileName: ringModel.title){ success in
|
|
|
|
+ if success {
|
|
|
|
+ TSMineRintoneHistory.shared.saveModel(model: ringModel)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ TSToastShared.showToast(text: errMsg ?? "Sorry, Save Failure".localized)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
- guard let savePath = RingDownloadManager.shared.getCopyToPath(with: editName) else {
|
|
|
|
- completion?(nil, nil)
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 裁剪
|
|
|
|
+ func startCutAudio(completion: ((TSRingModel?, String?,URL?) -> Void)?) {
|
|
|
|
+ guard let copyModel = ringModel.copy() as? TSRingModel else {
|
|
|
|
+ completion?(nil, nil, nil)
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ let savePath = TSDownloadManager.generateRingSaveLocalURL(name: copyModel.title)
|
|
|
|
|
|
- copyModel.duration = endDuration - startDuration
|
|
|
|
- THUD.showLoading()
|
|
|
|
- audioTool.startTansformAudio(url: url.path, from: startDuration, to: endDuration, fadeIn: Double(fadeInDuration), fadeOut: Double(fadeOutDuration), savePath: savePath) { filePath, errMsg in
|
|
|
|
|
|
+ copyModel.duration = Int(endDuration - startDuration)
|
|
|
|
+ TSRingLoadingView.shared.showWindow()
|
|
|
|
+ audioTool.startTansformAudio(url:editOriginalURL.path, from: startDuration, to: endDuration, fadeIn: Double(fadeInDuration), fadeOut: Double(fadeOutDuration),addVolume: Double(outputVolume) ,savePath: savePath.path) { filePath, errMsg in
|
|
DispatchQueue.main.async {
|
|
DispatchQueue.main.async {
|
|
- THUD.hide()
|
|
|
|
|
|
+ TSRingLoadingView.shared.remove()
|
|
}
|
|
}
|
|
if let filePath = filePath {
|
|
if let filePath = filePath {
|
|
let url = URL(fileURLWithPath: filePath)
|
|
let url = URL(fileURLWithPath: filePath)
|
|
- copyModel.title = url.deletingPathExtension().lastPathComponent.removingPercentEncoding
|
|
|
|
- copyModel.fileName = url.lastPathComponent
|
|
|
|
- copyModel.size = FileManager.default.getFileSize(url) ?? copyModel.size
|
|
|
|
- completion?(copyModel, errMsg)
|
|
|
|
|
|
+ copyModel.documentPath = filePath.documentLastURLString
|
|
|
|
+
|
|
|
|
+ if let fileInfo = TSBusinessAudioPlayer.getAudioFileInfo(path: url.path) {
|
|
|
|
+ if let size = fileInfo.sizeInBytes {
|
|
|
|
+ copyModel.size = Int(size)
|
|
|
|
+ }
|
|
|
|
+ if let duration = fileInfo.durationInSeconds {
|
|
|
|
+ copyModel.duration = Int(duration)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ completion?(copyModel, errMsg, savePath)
|
|
} else {
|
|
} else {
|
|
- completion?(nil, errMsg)
|
|
|
|
|
|
+ completion?(nil, errMsg, nil)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+extension TSEditAudioVideoBaseVC {
|
|
|
|
+ func handlePlayer(state: TSBusinessAudioPlayer.PlayerState) {
|
|
|
|
+ playButton.isSelected = player.isPlaying
|
|
|
|
+ progressLine.isHidden = !playButton.isSelected
|
|
|
|
+ if player.currentPlayerState == .play {
|
|
|
|
+ progressLine.centerX = dragLeftView.centerX
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- func saveButtonClick() {
|
|
|
|
- guard let current = ringModel else { return }
|
|
|
|
-// guard operationCache.count > 1 else {
|
|
|
|
-// // 只有原音频,意味着未进行裁剪操作,提示
|
|
|
|
-// let alertVC = UIAlertController(title: nil, message: "No audio can be saved. Whether to save the original audio?", preferredStyle: .alert)
|
|
|
|
-// alertVC.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
|
|
|
-// alertVC.addAction(UIAlertAction(title: "Save", style: .default, handler: { [weak self] _ in
|
|
|
|
-// RingDownloadManager.shared.saveEdited(ring: current)
|
|
|
|
-// self?.dismiss(animated: true)
|
|
|
|
-// }))
|
|
|
|
-// present(alertVC, animated: true)
|
|
|
|
-// return
|
|
|
|
-// }
|
|
|
|
|
|
+ func handlePlayer(progressChanged current: Double, total: Double) {
|
|
|
|
+ let range = endDuration - startDuration
|
|
|
|
+ let rangeWidth = trackContentView.width * range / total
|
|
|
|
+ let progress = max(0, current - startDuration) / range
|
|
|
|
|
|
- player.pause()
|
|
|
|
- let dismissHandler: (() -> Void)? = { [weak self] in
|
|
|
|
- self?.dismiss(animated: true, completion: {
|
|
|
|
- /// 编辑完也需要小红点提示
|
|
|
|
- RingDownloadManager.shared.hasUnreadRing = true
|
|
|
|
- NotificationCenter.default.post(name: .downloadUnreadStateChanged, object: nil)
|
|
|
|
|
|
+ if player.isPlaying {
|
|
|
|
+ progressLine.centerX = dragLeftView.centerX + progress * rangeWidth
|
|
|
|
+ }
|
|
|
|
|
|
- if let _ = UIApplication.topViewController as? MineRingsViewController {
|
|
|
|
- THUD.toast("Saved successfully".localized(), shake: true)
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- SaveSuccessTipsView.show(at: UIApplication.rootViewController?.topController.view) {
|
|
|
|
- guard let topVC = UIApplication.topViewController else {
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- let mineVC = MineRingsViewController()
|
|
|
|
- mineVC.currentType = .edited
|
|
|
|
- topVC.navigationController?.pushViewController(mineVC, animated: true)
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
|
|
+ if current >= endDuration {
|
|
|
|
+ player.pause()
|
|
}
|
|
}
|
|
|
|
|
|
-// // 未编辑, 不需要裁剪
|
|
|
|
-// guard startCropRate != 0 || endCropRate != 1.0
|
|
|
|
-// || fadeInDuration != 0 || fadeOutDuration != 0 else {
|
|
|
|
-// // 修改名称,直接保存
|
|
|
|
-// RingDownloadManager.shared.saveEdited(ring: current)
|
|
|
|
-// dismissHandler?()
|
|
|
|
-// return
|
|
|
|
-// }
|
|
|
|
|
|
+ // 淡入
|
|
|
|
+ if fadeInDuration > 0, current - startDuration < Double(fadeInDuration) {
|
|
|
|
+ let fadeProgress = Float(current - startDuration) / Float(fadeInDuration)
|
|
|
|
+ let newVolume = min(fadeProgress * maxVolume * 100, 100.0)
|
|
|
|
+ print("---volume: \(newVolume)")
|
|
|
|
+ player.setVolume(volume:newVolume / 100)
|
|
|
|
+ }
|
|
|
|
|
|
- // 保存音频
|
|
|
|
- startCutAudio { result, errMsg in
|
|
|
|
- if let ringModel = result {
|
|
|
|
- // 裁剪的音频,使用本地文件播放,清空网络url
|
|
|
|
- ringModel.fileUrl = nil
|
|
|
|
- RingDownloadManager.shared.saveEdited(ring: ringModel)
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- dismissHandler?()
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- THUD.toast(errMsg ?? "Sorry, Save Failure".localized())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ // 淡出
|
|
|
|
+ if fadeOutDuration > 0, current > (endDuration - Double(fadeOutDuration)) {
|
|
|
|
+ let fadeProgress = Float(endDuration - current) / Float(fadeOutDuration)
|
|
|
|
+ let newVolume = max(fadeProgress * maxVolume * 100, 0.0)
|
|
|
|
+ print("---volume: \(newVolume)")
|
|
|
|
+ player.setVolume(volume: newVolume / 100)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- override func navigationBarItemClick(_ type: NavigationBarAction) {
|
|
|
|
- if case .back = type {
|
|
|
|
- guard operationCache.count <= 1 else {
|
|
|
|
- let alertVC = UIAlertController(title: nil, message: "As you leave, any changes you have made will not be saved.".localized(), preferredStyle: .alert)
|
|
|
|
- alertVC.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel))
|
|
|
|
- alertVC.addAction(UIAlertAction(title: "Leave".localized(), style: .default, handler: { [weak self] _ in
|
|
|
|
- self?.dismiss(animated: true)
|
|
|
|
- }))
|
|
|
|
- present(alertVC, animated: true)
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- dismiss(animated: true)
|
|
|
|
- }
|
|
|
|
|
|
+ func sanitizeFilePath(_ path: String) -> String {
|
|
|
|
+ let illegalFileNameCharacters = CharacterSet(charactersIn: "/\\?%*|\"<>: ")
|
|
|
|
+ let sanitizedPath = path.components(separatedBy: illegalFileNameCharacters).joined(separator: "_")
|
|
|
|
+ return sanitizedPath
|
|
}
|
|
}
|
|
}
|
|
}
|