12

Before, I was setting sound volume programmatically using this approach:

MPVolumeView *volumeView = [[MPVolumeView alloc] init];
UISlider *volumeViewSlider = nil;

for (UIView *view in [volumeView subviews])
{
    if ([view.class.description isEqualToString:@"MPVolumeSlider"])
    {
        volumeViewSlider = (UISlider *)view;
        break;
    }
}

[volumeViewSlider setValue:0.5 animated:YES];
[volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];

Till iOS 11.4 it was working well (even on iOS 11.3), but on iOS 11.4 it doesn't. Volume value remains unchanged. Can someone help with this issue? Thanks.

trungduc
  • 11,926
  • 4
  • 31
  • 55
iOS Dev
  • 4,143
  • 5
  • 30
  • 58
  • Why do you want to reduce the volume programmatically? Is your app generating any sound itself? You will not want to turn up the volume to max without the user's consent, and equally reducing the volume of other apps without the user controlling this is also not desirable. If your app itself makes sound that needs to be amplified or attenuated, add an amplification node to your audio chain. – Toine Heuvelmans Jun 07 '18 at 11:29

4 Answers4

15

Changing volumeViewSlider.value after a small delay resolves problem.

- (IBAction)increase:(id)sender {
  MPVolumeView *volumeView = [[MPVolumeView alloc] init];
  UISlider *volumeViewSlider = nil;

  for (UIView *view in volumeView.subviews) {
    if ([view isKindOfClass:[UISlider class]]) {
      volumeViewSlider = (UISlider *)view;
      break;
    }
  }

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    volumeViewSlider.value = 0.5f;
  });
}

Swift version

trungduc
  • 11,926
  • 4
  • 31
  • 55
5

I solved it by adding new MPVolumeView to my UIViewController view, otherwise it didn't set the volume anymore. As I added it to the controller I also need to set the volume view position to be outside of the screen to hide it from the user.

I prefer not to use delayed volume setting as it make things more complicated especially if you need to play sound immediately after setting the volume.

The code is in Swift 4:

let volumeControl = MPVolumeView(frame: CGRect(x: 0, y: 0, width: 120, height: 120))

override func viewDidLoad() {
   self.view.addSubview(volumeControl);
}

override func viewDidLayoutSubviews() {
   volumeControl.frame = CGRect(x: -120, y: -120, width: 100, height: 100);
}

func setVolume(_ volume: Float) {
    let lst = volumeControl.subviews.filter{NSStringFromClass($0.classForCoder) == "MPVolumeSlider"}
    let slider = lst.first as? UISlider

    slider?.setValue(volume, animated: false)
}
Northern Captain
  • 1,147
  • 3
  • 25
  • 32
1

I had to have a MPVolumeView as subview to a view in the hierarchy for the hud not to show up on iOS 12. It needs to be slightly visible:

let volume = MPVolumeView(frame: .zero)
volume.setVolumeThumbImage(UIImage(), for: UIControl.State())
volume.isUserInteractionEnabled = false
volumelume.alpha = 0.0001
volume.showsRouteButton = false
view.addSubview(volume)

When setting the volume I get the slider from MPVolumeView as with previous posters and set the value:

func setVolumeLevel(_ volumeLevel: Float) {
    guard let slider = volume.subviews.compactMap({ $0 as? UISlider }).first else {
        return
    }

    slider.value = volumeLevel
}
jalmaas
  • 908
  • 8
  • 17
0

I just added the MPVolumeView as a subview to another view (that was never drawn on screen).

This had to be done prior to any attempt to set or get the volume.

private let containerView = UIView()
private let volumeView = MPVolumeView()

func prepareWorkaround() {
    self.containerView.addSubview(self.volumeView)
}
Ric Santos
  • 15,419
  • 6
  • 50
  • 75