Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hide grey volume overlay when using MPVolumeView?

I've put a MPVolumeView over a movie I'm playing. The trouble is, whenever I adjust the vol using the MPVolumeView, then the standard grey volume overlay appears (the one that appears when you use the rocker switch on the iPhone). Is there a way to disable the standard grey overlay?

like image 397
cannyboy Avatar asked Sep 15 '11 12:09

cannyboy


2 Answers

I might be a bit late, but I think I have something useful to add. If you're just looking for an answer, skip to How did I fix it?

What is MPVolumeView?
The MPVolumeView class is used to put a slider onscreen that controls the system volume. It's supposed to prevent the system volume overlay from appearing while it is onscreen, but sometimes does not. I ran into this issue when I added my own MPVolumeView to the view hierarchy, but still saw the overlay appear when I dragged the slider.

How does MPVolumeView tell when it is visible?
This is the question I asked myself. Somehow, the class detects whether or not it is visible, and tells the system to hide or display the system volume overlay accordingly. This is because Apple does not really want you using the class just to prevent the overlay from appearing, without actually displaying the slider UI to the user (as in myell0w's answer).

Here's how I believe MPVolumeView tries to check if it is really visible:

  1. When the view is added to a superview, as detected by viewWillMoveToSuperview: and viewDidMoveToSuperview, it starts a short timer.
  2. When the timer fires, the view traverses its view ancestor tree by recursively examining the superview property.
  3. MPVolumewView checks that each of its ancestors has hidden = NO and alpha greater than some small value (likely 0.05).

There could be other checks that I'm not aware of, since this code is of course closed-source. The timer in step 1 is there so that code like the following will not "fool" the view:

MPVolumeView *volView = [[MPVolumeView alloc] init];
[self.view addSubview:volView];
volView.hidden = YES;

because it will check the hidden property not immediately, but in a bit, after it is already set to YES.

How did I figure all of this out? A key find was that the instance of MPVolumeView was calling isHidden on its superview, as shown in the following stack trace:

MPVolumeView stack trace

What was my problem?
In short, I did something like this:

- (void)viewDidLoad {
    // Add an MPVolumeView to my view
    self.volView = [[MPVolumeView alloc] init];
    [self.view addSubview:self.volView];
    self.volView.hidden = YES;
}

- (void)someButtonTouched {
    // Show the MPVolumeView (and hopefully hide the system overlay)
    self.volView.hidden = NO;
}

But when I dragged the slider of the newly revealed MPVolumeView, the overlay still appeared. I realized that this was because during the MPVolumeView's instantiation, its superview was hidden.

How did I fix it?
Once I had realized how the MPVolumeView class judged whether it was visible, I had an easy way to "fool" it. I added the following method to the class responsible for the MPVolumeView:

- (void)refreshVolumeView {
    [self.volView willMoveToSuperview:self];
    [self.volView didMoveToSuperview];
}

and called it each time I need to force the view to reevaluate whether it was visible. This method simply pretends to re-add the view to the hierarchy. Once I've satisfied the conditions that the view evaluates (each ancestor is not hidden or of very low alpha), I call it, and the overlay stops showing up. In my example code above, I would add one line:

- (void)viewDidLoad {
    // Add an MPVolumeView to my view
    self.volView = [[MPVolumeView alloc] init];
    [self.view addSubview:self.volView];
    self.volView.hidden = YES;
}

- (void)someButtonTouched {
    // Show the MPVolumeView (and hopefully hide the system overlay)
    self.volView.hidden = NO;
    [self refreshVolumeView];  // <<< This line was added
}
like image 124
ravron Avatar answered Nov 19 '22 10:11

ravron


Normally when a MPVolumeView is visible the HUD doesn't appear. I'm using the following method in my App and it is working fine:

+ (void)preventSystemVolumePopup {
    // Prevent Audio-Change Popus
    MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(-2000., -2000., 0.f, 0.f)];
    NSArray *windows = [UIApplication sharedApplication].windows;

    volumeView.alpha = 0.1f;
    volumeView.userInteractionEnabled = NO;

    if (windows.count > 0) {
       [[windows objectAtIndex:0] addSubview:volumeView];
    }
}

It basically just adds a volume view to the first window at a position where nobody can see it and thus prevents the system volume HUD from showing. I wonder why you see this HUD, even though you put a volume view above your movie.

like image 7
myell0w Avatar answered Nov 19 '22 09:11

myell0w