Home > other >  How to use MPVolume View correctly?
How to use MPVolume View correctly?

Time:10-08

After implementing DonMag's answer, work's great After implementing DonMag's answer, work's great

-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.

When user swipes the button of the slider all the way to minimum the button of the slider doesn't move to the end.

When user swipes all the way to maximum the button of the slider hovers over the connectivity button.

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:

enter image description here

and we can drag the thumb all the way to the left:

enter image description here

and to the right (without overlapping the route button):

enter image description here

  • Related