AudioPlayerView.swift 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. //
  2. // SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
  3. // SPDX-License-Identifier: GPL-3.0-or-later
  4. //
  5. import UIKit
  6. protocol AudioPlayerViewDelegate: AnyObject {
  7. func audioPlayerPlayButtonPressed()
  8. func audioPlayerPauseButtonPressed()
  9. func audioPlayerProgressChanged(progress: CGFloat)
  10. }
  11. class AudioPlayerView: UIView {
  12. @IBOutlet var contentView: UIView!
  13. @IBOutlet weak var playPauseButton: UIButton!
  14. @IBOutlet weak var slider: UISlider!
  15. @IBOutlet weak var durationLabel: UILabel!
  16. var isPlaying: Bool = false
  17. public weak var delegate: AudioPlayerViewDelegate?
  18. override init(frame: CGRect) {
  19. super.init(frame: frame)
  20. commonInit()
  21. }
  22. required init?(coder aDecoder: NSCoder) {
  23. super.init(coder: aDecoder)
  24. commonInit()
  25. }
  26. func commonInit() {
  27. Bundle.main.loadNibNamed("AudioPlayerView", owner: self, options: nil)
  28. contentView.frame = self.bounds
  29. contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  30. // Play/Pause button
  31. playPauseButton.addAction(for: .touchUpInside) { [unowned self] in
  32. if isPlaying {
  33. delegate?.audioPlayerPauseButtonPressed()
  34. } else {
  35. delegate?.audioPlayerPlayButtonPressed()
  36. }
  37. }
  38. // Slider
  39. let thumbImage = UIImage(systemName: "circle.fill")?
  40. .withConfiguration(UIImage.SymbolConfiguration(pointSize: 16))
  41. .withTintColor(.label, renderingMode: .alwaysOriginal)
  42. slider.setThumbImage(thumbImage, for: .normal)
  43. slider.semanticContentAttribute = .forceLeftToRight
  44. slider.addAction(for: .valueChanged) { [unowned self] in
  45. delegate?.audioPlayerProgressChanged(progress: CGFloat(slider.value))
  46. }
  47. // Duration label
  48. hideDurationLabel()
  49. backgroundColor = .secondarySystemBackground
  50. layer.cornerRadius = 8.0
  51. layer.masksToBounds = true
  52. self.addSubview(contentView)
  53. }
  54. func setPlayerProgress(_ progress: CGFloat, isPlaying playing: Bool, maximumValue maxValue: CGFloat) {
  55. setPlayPauseButton(playing: playing)
  56. slider.isEnabled = true
  57. slider.value = Float(progress)
  58. slider.maximumValue = Float(maxValue)
  59. setDurationLabel(progress: progress, duration: maxValue)
  60. slider.setNeedsLayout()
  61. }
  62. func resetPlayer() {
  63. setPlayPauseButton(playing: false)
  64. slider.isEnabled = false
  65. slider.value = 0
  66. hideDurationLabel()
  67. slider.setNeedsLayout()
  68. }
  69. func setPlayPauseButton(playing: Bool) {
  70. isPlaying = playing
  71. if isPlaying {
  72. playPauseButton.setImage(UIImage(systemName: "pause.fill"), for: .normal)
  73. } else {
  74. playPauseButton.setImage(UIImage(systemName: "play.fill"), for: .normal)
  75. }
  76. }
  77. func setDurationLabel(progress: CGFloat, duration: CGFloat) {
  78. let dateComponentsFormatter = DateComponentsFormatter()
  79. dateComponentsFormatter.allowedUnits = [.minute, .second]
  80. dateComponentsFormatter.zeroFormattingBehavior = []
  81. let progressTime = dateComponentsFormatter.string(from: TimeInterval(progress)) ?? "0:00"
  82. let durationTime = dateComponentsFormatter.string(from: TimeInterval(duration)) ?? "0:00"
  83. let playerTimeString = "\(progressTime)".withTextColor(.label).withFont(.systemFont(ofSize: 13, weight: .medium))
  84. playerTimeString.append(" / \(durationTime)".withTextColor(.secondaryLabel).withFont(.systemFont(ofSize: 13)))
  85. durationLabel.attributedText = playerTimeString
  86. }
  87. func hideDurationLabel() {
  88. durationLabel.text = ""
  89. }
  90. }