Here is my volume control for my audio player app:
import UIKit
import MediaPlayer
class UIVolumeSlider: UISlider {
private let keyVolume = "outputVolume"
func activate(){
updatePositionForSystemVolume()
guard let view = superview else { return }
let volumeView = MPVolumeView(frame: .zero)
volumeView.alpha = 0.000001
view.addSubview(volumeView)
try? AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
AVAudioSession.sharedInstance().addObserver(self, forKeyPath: keyVolume, options: .new, context: nil)
addTarget(self, action: #selector(valueChanged), for: .valueChanged)
}
func deactivate(){
AVAudioSession.sharedInstance().removeObserver(self, forKeyPath: keyVolume)
removeTarget(self, action: nil, for: .valueChanged)
superview?.subviews.first(where: {$0 is MPVolumeView})?.removeFromSuperview()
}
func updatePositionForSystemVolume(){
try? AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
value = AVAudioSession.sharedInstance().outputVolume
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == keyVolume, let newVal = change?[.newKey] as? Float {
setValue(newVal, animated: true)
}
}
@objc private func valueChanged(){
guard let superview = superview else {return}
guard let volumeView = superview.subviews.first(where: {$0 is MPVolumeView}) as? MPVolumeView else { return }
guard let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider else { return }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
slider.value = self.value
}
}
}
How to connect:
- Set UIVolumeSlider class in Identity Inspector on storyboard:
- Connect instance to class:
@IBOutlet private var volumeSlider:UIVolumeSlider!
- In viewDidLoad method:
volumeSlider.updatePositionForSystemVolume()
- Activate and deactivate it in didAppear and willDisappear:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
volumeSlider.activate()
}
override func viewWillDisappear(_ animated: Bool) {
volumeSlider.deactivate()
super.viewWillDisappear(animated)
}
setVolume:
being deprecated. You app could be rejected. – Behalf