0

I want to place an MPVolumeView in my SwiftUI view but it doesn't behave like a normal SwiftUI view. I want the volume slider to be centered vertically between the two dividers. If you replaced VolumeSlider with Text it would be centered. How can I make the VolumeSlider behave in the same way?

// Must be run on real device, not simulator

import SwiftUI
import MediaPlayer

struct ContentView: View {
    var body: some View {
        VStack {
            Divider()
            VolumeSlider()
                .frame(height: 128)
            Divider()
        }
        
    }
}

struct VolumeSlider: UIViewRepresentable {
    
    func makeUIView(context: Context) -> MPVolumeView {
        MPVolumeView(frame: .zero)
    }
    
    func updateUIView(_ view: MPVolumeView, context: Context) {}
}

Not centered vertically

Steve M
  • 9,296
  • 11
  • 49
  • 98

2 Answers2

0

I came across the same issue today and built a solution based off the selected answer from this post: Matching MPVolumeView and UISlider vertical positions in a UIToolBar

The issued is resolved by overriding volumeSliderRect and routeButtonRect.

import SwiftUI
import MediaPlayer
import UIKit

struct VolumeSlider: UIViewRepresentable {
    
    class SystemVolumeView: MPVolumeView {
        
        override func volumeSliderRect(forBounds bounds: CGRect) -> CGRect {
            var newBounds = super.volumeSliderRect(forBounds: bounds)
            newBounds.origin.y = bounds.origin.y
            newBounds.size.height = bounds.size.height
            return newBounds
        }
        
        override func routeButtonRect(forBounds bounds: CGRect) -> CGRect {
            var newBounds = super.routeButtonRect(forBounds: bounds)
            newBounds.origin.y = bounds.origin.y
            newBounds.size.height = bounds.size.height
            return newBounds
        }
    }
    
    func makeUIView(context: Context) -> SystemVolumeView {
        SystemVolumeView(frame: .zero)
    }
    
    func updateUIView(_ view: SystemVolumeView, context: Context) {}
}

Then, you can use the new UIViewRepresentable (in this example VolumeSlider()) like so:

ZStack {
    Rectangle()
        .fill(.black.opacity(0.50))
    VolumeSlider()
        .padding(.horizontal)
        .tint(.white)
}
.frame(height: 150)

You'll see the VolumeSider() is now centered within the ZStack as you'd expect. I've hardcoded the height to 150 just for an example.

enter image description here

eric
  • 45
  • 7
-1

Firstly this behavior is in fact normal for a SwiftUI view. You are specifying a point height for the VolumeSlider. If you decrease the 128 it will not have the space between slider and bottom divider.

struct ContentView: View {
    var body: some View {
        VStack {
            Divider()
            VolumeSlider()
                .frame(height: 50)
            Divider()
        }
    }
}

I'll update this answer to try and make it not take up the whole space when .frame is removed.

atultw
  • 921
  • 7
  • 15