-This was my Question: This is an audio player, I removed all the other lines for you to be easy in reading. The problem is in MPVolumeView. When user swipes all the way to maximum the button of the slider hovers over the connectivity button. When user swipes the button of the slider all the way to minimum the button of the slider doesn't move to the end.
-Dear DonMag, I am really thankful to you, It works! and HOW! I am adding screenshots. I believe your answer will be helpful to a lot of self tights.
import UIKit
import AVFoundation
import MediaPlayer
import AVKit
class AudioPlayerViewControllerQ1: UIViewController {
@IBOutlet var holder: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if holder.subviews.count == 0 {
}
let volumeView = MPVolumeView(frame: CGRect(x: 20,
y: holder.frame.size.height - 80,
width: holder.frame.size.width-40,
height: 30))
holder.addSubview(volumeView)
}
private func setupView() {
setupConstraints()
}
private func setupConstraints() {
NSLayoutConstraint.activate([
holder.leadingAnchor.constraint(equalTo: view.leadingAnchor),
holder.trailingAnchor.constraint(equalTo: view.trailingAnchor),
holder.topAnchor.constraint(equalTo: view.topAnchor),
holder.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIApplication.shared.isIdleTimerDisabled = true
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
UIApplication.shared.isIdleTimerDisabled = false
}
}
CodePudding user response:
After quick research and experimentation -- it appears MPVolumeView
is rather buggy :(
When instantiated, if the current device volume is greater than 0
, the thumb will be offset on the x-axis. The higher the volume, the larger the offset.
Also, it doesn't play well at all with auto-layout constraints.
We can get around this by subclassing MPVolumeView
and "fixing" the slider rect:
class MyVolumeView: MPVolumeView {
override func volumeSliderRect(forBounds bounds: CGRect) -> CGRect {
// this will avoid the thumb x-offset issue
// while keeping the route button vertically aligned
return bounds.insetBy(dx: 12.0, dy: 0.0).offsetBy(dx: -12.0, dy: -5.0)
}
}
Then, to correct the problems with the vertical layout, we will want to offset the Y position when we set its frame.
Here's a quick example of one way to do that. I've embedded MyVolumeView
in a "container" view, and used a property observer to update the frame whenever the container view's bounds changes:
class AudioPlayerViewControllerQ1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// let's give the view a background color so we can easily see its frame
view.backgroundColor = .systemYellow
// assuming "holder" view has buttons and other controls
// along with the MyVolumeView near the bottom
let holder = UIView()
holder.backgroundColor = .darkGray
holder.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(holder)
// create a separate "container" view for the MyVolumeView
let volumeViewContainer = UIView()
// we'll make it red for now so we can see it
volumeViewContainer.backgroundColor = .red
volumeViewContainer.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(volumeViewContainer)
// respect safe-area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// let's make the holder 20-points inset on leading/trailing
holder.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
holder.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// holder height (for this example) is 240.0
holder.heightAnchor.constraint(equalToConstant: 240.0),
// let's put its bottom 60-points from the bottom (of the safe area)
holder.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -160.0),
// volume view container leading/trailing equal to holder
volumeViewContainer.leadingAnchor.constraint(equalTo: holder.leadingAnchor, constant: 0.0),
volumeViewContainer.trailingAnchor.constraint(equalTo: holder.trailingAnchor, constant: 0.0),
// volume view container bottom equal to holder bottom
volumeViewContainer.bottomAnchor.constraint(equalTo: holder.bottomAnchor, constant: 0.0),
// volume view container height equal to 30-points
volumeViewContainer.heightAnchor.constraint(equalToConstant: 30.0),
])
// now we'll add a MPVolumeView to the container
let v = MyVolumeView()
volumeViewContainer.addSubview(v)
// we'll use a property observer to update the MyVolumeView frame
// whenever the container bounds changes
volumeViewContainer.addObserver(self, forKeyPath: "bounds", context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "bounds" {
// make sure we're getting notified of the MyVolumeView container view
if let cv = object as? UIView,
let mpv = cv.subviews.first as? MyVolumeView {
// set MyVolumeView frame to container view's bounds
// and offset its y-position by 4-points (because of its buggy layout)
mpv.frame = cv.bounds.offsetBy(dx: 0.0, dy: 4.0)
}
}
}
}
It looks like this when running:
and we can drag the thumb all the way to the left:
and to the right (without overlapping the route button):