我可能有点晚了,但我想我有一些有用的东西要补充。如果您只是在寻找答案,请跳至我是如何解决的?
什么是MPVolumeView?
MPVolumeView 类用于在屏幕上放置一个控制系统音量的滑块。它应该防止系统音量覆盖在屏幕上出现,但有时不会。当我将自己的 MPVolumeView 添加到视图层次结构时遇到了这个问题,但是当我拖动滑块时仍然看到覆盖出现。
MPVolumeView 如何判断它何时可见?
这是我问自己的问题。不知何故,该类检测它是否可见,并告诉系统相应地隐藏或显示系统卷覆盖。这是因为 Apple 并不真正希望您使用该类只是为了防止覆盖出现,而不是实际向用户显示滑块 UI(如 myell0w 的回答)。
这就是我认为MPVolumeView 尝试检查它是否真的可见的方式:
- 当视图被添加到父视图时,
viewWillMoveToSuperview: 和 viewDidMoveToSuperview 检测到,它会启动一个短计时器。
- 当计时器触发时,视图通过递归检查
superview 属性来遍历其视图祖先树。
-
MPVolumewView 检查其每个祖先的 hidden = NO 和 alpha 是否大于某个小值(可能为 0.05)。
可能还有其他我不知道的检查,因为此代码当然是封闭源代码。第 1 步中的计时器在那里,这样下面的代码就不会“欺骗”视图:
MPVolumeView *volView = [[MPVolumeView alloc] init];
[self.view addSubview:volView];
volView.hidden = YES;
因为它不会立即检查hidden 属性,而是在它已经设置为YES 之后稍等片刻。
我是如何弄清楚这一切的?一个关键发现是MPVolumeView 的实例在其父视图上调用isHidden,如以下堆栈跟踪所示:
我的问题是什么?
简而言之,我做了这样的事情:
- (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;
}
但是当我拖动新显示的MPVolumeView的滑块时,覆盖仍然出现。我意识到这是因为在MPVolumeView 的实例化过程中,它的超级视图被隐藏了。
我是如何解决的?
一旦我意识到MPVolumeView 类如何判断它是否可见,我就有一个简单的方法来“愚弄”它。我在负责MPVolumeView的类中添加了以下方法:
- (void)refreshVolumeView {
[self.volView willMoveToSuperview:self];
[self.volView didMoveToSuperview];
}
并在每次我需要强制视图重新评估它是否可见时调用它。此方法只是假装将视图重新添加到层次结构中。一旦我满足了视图评估的条件(每个祖先都不是隐藏的或非常低的 alpha),我就调用它,并且覆盖层停止显示。在上面的示例代码中,我将添加一行:
- (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
}