iOS 9:如何在不显示系统条形音箱弹出窗口的情况下以编程方式更改音量?
iOS 9: How to change volume programmatically without showing system sound bar popup?
我必须更改 iPad 的音量并使用此代码:
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0];
但是在 iPad 上会改变音量并显示系统音量条。 如何在不显示音量条的情况下改变声音?
我知道,setVolume:
已被弃用,每个人都说要使用 MPVolumeView
。如果这是解决我的问题的唯一方法,那么如何使用 MPVolumeView
更改音量?我在 MPVolumeView
中没有看到任何改变声音的方法。
我应该使用另一个 class 和 MPVolumeView
吗?
但最好使用MPMusicPlayerController
。
谢谢指教!
我不认为有任何方法可以在不闪烁音量控制的情况下改变音量。你应该像这样使用 MPVolumeView
:
MPVolumeView* volumeView = [[MPVolumeView alloc] init];
// Get the Volume Slider
UISlider* volumeViewSlider = nil;
for (UIView *view in [volumeView subviews]){
if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
volumeViewSlider = (UISlider*)view;
break;
}
}
// Fake the volume setting
[volumeViewSlider setValue:1.0f animated:YES];
[volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];
MPVolumeView
有一个滑块,通过改变滑块的值,可以改变设备的音量。我写了一个 MPVolumeView
扩展来轻松访问滑块:
extension MPVolumeView {
var volumeSlider:UISlider {
self.showsRouteButton = false
self.showsVolumeSlider = false
self.hidden = true
var slider = UISlider()
for subview in self.subviews {
if subview.isKindOfClass(UISlider){
slider = subview as! UISlider
slider.continuous = false
(subview as! UISlider).value = AVAudioSession.sharedInstance().outputVolume
return slider
}
}
return slider
}
}
Swift > 2.2,iOS > 8.0,
我没有找到我正在寻找的任何解决方案,但我最终将此作为解决方案:
let volumeView = MPVolumeView()
override func viewDidLoad() {
...
view.addSubview(volumeView)
volumeView.alpha = 0.00001
}
func changeSpeakerSliderPanelControls(volume: Float) {
for subview in self.volumeView.subviews {
if subview.description.rangeOfString("MPVolumeSlider") != nil {
let slider = subview as! UISlider
slider.value = volume
break
}
}
}
这是 Swift 中的解决方案。这可能是一个阴暗的,所以我会在我发布时告诉你苹果是否批准了这个。同时,这对我来说很好用:
在您的视图控制器中定义一个 MPVolumeView 和一个可选的 UISlider
private let volumeView: MPVolumeView = MPVolumeView()
private var volumeSlider: UISlider?
在情节提要中,定义一个对用户隐藏的视图(高度=0 应该可以解决问题),并为其设置一个出口(我们称之为 hiddenView 这里)。此步骤仅适用于您不想在更改音量时显示音量 HUD(请参阅下面的注释):
@IBOutlet weak var hiddenView: UIView!
在 viewDidLoad() 或某处运行一次的 init-y 中,将实际控制音量的 UISlider 捕获到步骤 (1) 中的可选 UISlider:
override func viewDidLoad() {
super.viewDidLoad()
...
hiddenView.addSubview(volumeView)
for view in volumeView.subviews {
if let vs = view as? UISlider {
volumeSlider = vs
break
}
}
}
当您想在代码中设置音量时,只需将 volumeSlider?.value 设置为 0.0 和 1.0 之间的任何值,例如增加音量:
func someFunc() {
if volumeSlider?.value < 0.99 {
volumeSlider?.value += 0.01
} else {
volumeSlider?.value = 1.0
}
}
重要提示:
此解决方案将阻止 iPhone 的音量 HUD 出现 - 无论是在您更改代码中的音量时,还是在用户单击外部音量按钮时。如果您确实想要显示 HUD,则跳过所有隐藏的视图内容,并且根本不要将 MPVolumeView 添加为子视图。这将导致 iOS 在音量变化时显示 HUD。
版本:Swift3 & Xcode8.1
extension MPVolumeView {
var volumeSlider:UISlider { // hacking for changing volume by programing
var slider = UISlider()
for subview in self.subviews {
if subview is UISlider {
slider = subview as! UISlider
slider.isContinuous = false
(subview as! UISlider).value = AVAudioSession.sharedInstance().outputVolume
return slider
}
}
return slider
}
}
@udjat 在 Swift 3
中的回答
extension MPVolumeView {
var volumeSlider: UISlider? {
showsRouteButton = false
showsVolumeSlider = false
isHidden = true
for subview in subviews where subview is UISlider {
let slider = subview as! UISlider
slider.isContinuous = false
slider.value = AVAudioSession.sharedInstance().outputVolume
return slider
}
return nil
}
}
extension UIViewController {
func setVolumeStealthily(_ volume: Float) {
guard let view = viewIfLoaded else {
assertionFailure("The view must be loaded to set the volume with no UI")
return
}
let volumeView = MPVolumeView(frame: .zero)
guard let slider = volumeView.subviews.first(where: { [=10=] is UISlider }) as? UISlider else {
assertionFailure("Unable to find the slider")
return
}
volumeView.clipsToBounds = true
view.addSubview(volumeView)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { [weak slider, weak volumeView] in
slider?.setValue(volume, animated: false)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { [weak volumeView] in
volumeView?.removeFromSuperview()
}
}
}
}
用法:
// set volume to 50%
viewController.setVolume(0.5)
您可以通过以下代码使用默认的 UISlider:
import MediaPlayer
class CusomViewCOntroller: UIViewController
// could be IBOutlet
var customSlider = UISlider()
// in code
var systemSlider = UISlider()
override func viewDidLoad() {
super.viewDidLoad()
let volumeView = MPVolumeView()
if let view = volumeView.subviews.first as? UISlider{
systemSlider = view
}
}
接下来在代码中写
systemSlider.value = customSlide.value
2018 年,致力于 iOS11.4
稍等片刻后,您需要更改 slider.value
。
extension MPVolumeView {
static func setVolume(_ volume: Float) {
let volumeView = MPVolumeView()
let slider = volumeView.subviews.first(where: { [=10=] is UISlider }) as? UISlider
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
slider?.value = volume
}
}
}
用法:
MPVolumeView.setVolume(0.5)
这是我的音频播放器应用的音量控制:
import UIKit
import MediaPlayer
class UIVolumeSlider: UISlider {
func activate(){
updatePositionForSystemVolume()
guard let view = superview else { return }
let volumeView = MPVolumeView(frame: .zero)
volumeView.alpha = 0.000001
view.addSubview(volumeView)
try? AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
AVAudioSession.sharedInstance().addObserver(self, forKeyPath: "outputVolume", options: .new, context: nil)
addTarget(self, action: #selector(valueChanged), for: .valueChanged)
}
func deactivate(){
AVAudioSession.sharedInstance().removeObserver(self, forKeyPath: "outputVolume")
removeTarget(self, action: nil, for: .valueChanged)
superview?.subviews.first(where: {[=10=] is MPVolumeView})?.removeFromSuperview()
}
func updatePositionForSystemVolume(){
try? AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
value = AVAudioSession.sharedInstance().outputVolume
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "outputVolume", let newVal = change?[.newKey] as? Float {
setValue(newVal, animated: true)
}
}
@objc private func valueChanged(){
guard let superview = superview else {return}
guard let volumeView = superview.subviews.first(where: {[=10=] is MPVolumeView}) as? MPVolumeView else { return }
guard let slider = volumeView.subviews.first(where: { [=10=] is UISlider }) as? UISlider else { return }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
slider.value = self.value
}
}
}
连接方式:
- 在 Storyboard 的 Identity Inspector 中设置 UIVolumeSlider class:
- 将实例连接到 class:
@IBOutlet private var volumeSlider:UIVolumeSlider!
- 在viewDidLoad方法中:
volumeSlider.updatePositionForSystemVolume()
- 在 didAppear 和 willDisappear:
中激活和停用它
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
volumeSlider.activate()
}
override func viewWillDisappear(_ animated: Bool) {
volumeSlider.deactivate()
super.viewWillDisappear(animated)
}
Swift 5 / iOS 13
这是我找到的访问系统音量级别的最可靠方法。这是基于此处的其他答案,但不会浪费能源,也不需要异步等待。
在初始化期间(例如在viewDidLoad
中),创建一个离屏MPVolumeView
:
var hiddenSystemVolumeSlider: UISlider!
override func viewDidLoad() {
let volumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y:0, width:0, height:0))
view.addSubview(volumeView)
hiddenSystemVolumeSlider = volumeView.subviews.first(where: { [=10=] is UISlider }) as? UISlider
}
然后在需要时使用隐藏滑块获取或设置系统音量:
var systemVolume:Float {
get {
return hiddenSystemVolumeSlider.value
}
set {
hiddenSystemVolumeSlider.value = newValue
}
}
我不想在视图控制器中进行调用,所以我构建了一个不同的解决方案。您可以像这样从任何地方调用它:
SystemVolumeController.shared.systemVolume = 0.5
所有 MPVolumeView 解决方案要记住的一件事是,当 MPVolumeView 在屏幕上时,当用户按下 phone 没有出现。出于这个原因,我在这个解决方案中内置了一个计时器,它会在几秒钟后删除视图。
这里的一些解决方案建议您需要在设置音量之前设置延迟。我还没有发现我的情况,但你的里程可能会有所不同。
import MediaPlayer
final class SystemVolumeController {
static let shared: SystemVolumeController = SystemVolumeController()
private weak var volumeView: MPVolumeView?
private var removeTimer: Timer?
private func getVolumeView() -> MPVolumeView? {
if let volumeView = volumeView {
return volumeView
}
else {
removeTimer?.invalidate()
let volumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y:0, width:0, height:0))
guard let window = UIApplication.shared.windows.first else {
assertionFailure("No window to add the volume slider to")
return nil
}
window.addSubview(volumeView)
self.volumeView = volumeView
removeTimer = Timer.scheduledTimer(withTimeInterval: 3,
repeats: false,
block: { _ in
volumeView.removeFromSuperview()
})
return volumeView
}
}
private var volumeSlider: UISlider? {
getVolumeView()?.subviews.first(where: { [=10=] is UISlider }) as? UISlider
}
var systemVolume: Float {
get {
AVAudioSession.sharedInstance().outputVolume
}
set {
volumeSlider?.value = newValue
}
}
private init() {}
}```
我必须更改 iPad 的音量并使用此代码:
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0];
但是在 iPad 上会改变音量并显示系统音量条。 如何在不显示音量条的情况下改变声音?
我知道,setVolume:
已被弃用,每个人都说要使用 MPVolumeView
。如果这是解决我的问题的唯一方法,那么如何使用 MPVolumeView
更改音量?我在 MPVolumeView
中没有看到任何改变声音的方法。
我应该使用另一个 class 和 MPVolumeView
吗?
但最好使用MPMusicPlayerController
。
谢谢指教!
我不认为有任何方法可以在不闪烁音量控制的情况下改变音量。你应该像这样使用 MPVolumeView
:
MPVolumeView* volumeView = [[MPVolumeView alloc] init];
// Get the Volume Slider
UISlider* volumeViewSlider = nil;
for (UIView *view in [volumeView subviews]){
if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
volumeViewSlider = (UISlider*)view;
break;
}
}
// Fake the volume setting
[volumeViewSlider setValue:1.0f animated:YES];
[volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];
MPVolumeView
有一个滑块,通过改变滑块的值,可以改变设备的音量。我写了一个 MPVolumeView
扩展来轻松访问滑块:
extension MPVolumeView {
var volumeSlider:UISlider {
self.showsRouteButton = false
self.showsVolumeSlider = false
self.hidden = true
var slider = UISlider()
for subview in self.subviews {
if subview.isKindOfClass(UISlider){
slider = subview as! UISlider
slider.continuous = false
(subview as! UISlider).value = AVAudioSession.sharedInstance().outputVolume
return slider
}
}
return slider
}
}
Swift > 2.2,iOS > 8.0,
我没有找到我正在寻找的任何解决方案,但我最终将此作为解决方案:
let volumeView = MPVolumeView()
override func viewDidLoad() {
...
view.addSubview(volumeView)
volumeView.alpha = 0.00001
}
func changeSpeakerSliderPanelControls(volume: Float) {
for subview in self.volumeView.subviews {
if subview.description.rangeOfString("MPVolumeSlider") != nil {
let slider = subview as! UISlider
slider.value = volume
break
}
}
}
这是 Swift 中的解决方案。这可能是一个阴暗的,所以我会在我发布时告诉你苹果是否批准了这个。同时,这对我来说很好用:
在您的视图控制器中定义一个 MPVolumeView 和一个可选的 UISlider
private let volumeView: MPVolumeView = MPVolumeView() private var volumeSlider: UISlider?
在情节提要中,定义一个对用户隐藏的视图(高度=0 应该可以解决问题),并为其设置一个出口(我们称之为 hiddenView 这里)。此步骤仅适用于您不想在更改音量时显示音量 HUD(请参阅下面的注释):
@IBOutlet weak var hiddenView: UIView!
在 viewDidLoad() 或某处运行一次的 init-y 中,将实际控制音量的 UISlider 捕获到步骤 (1) 中的可选 UISlider:
override func viewDidLoad() { super.viewDidLoad() ... hiddenView.addSubview(volumeView) for view in volumeView.subviews { if let vs = view as? UISlider { volumeSlider = vs break } } }
当您想在代码中设置音量时,只需将 volumeSlider?.value 设置为 0.0 和 1.0 之间的任何值,例如增加音量:
func someFunc() { if volumeSlider?.value < 0.99 { volumeSlider?.value += 0.01 } else { volumeSlider?.value = 1.0 } }
重要提示: 此解决方案将阻止 iPhone 的音量 HUD 出现 - 无论是在您更改代码中的音量时,还是在用户单击外部音量按钮时。如果您确实想要显示 HUD,则跳过所有隐藏的视图内容,并且根本不要将 MPVolumeView 添加为子视图。这将导致 iOS 在音量变化时显示 HUD。
版本:Swift3 & Xcode8.1
extension MPVolumeView {
var volumeSlider:UISlider { // hacking for changing volume by programing
var slider = UISlider()
for subview in self.subviews {
if subview is UISlider {
slider = subview as! UISlider
slider.isContinuous = false
(subview as! UISlider).value = AVAudioSession.sharedInstance().outputVolume
return slider
}
}
return slider
}
}
@udjat 在 Swift 3
中的回答extension MPVolumeView {
var volumeSlider: UISlider? {
showsRouteButton = false
showsVolumeSlider = false
isHidden = true
for subview in subviews where subview is UISlider {
let slider = subview as! UISlider
slider.isContinuous = false
slider.value = AVAudioSession.sharedInstance().outputVolume
return slider
}
return nil
}
}
extension UIViewController {
func setVolumeStealthily(_ volume: Float) {
guard let view = viewIfLoaded else {
assertionFailure("The view must be loaded to set the volume with no UI")
return
}
let volumeView = MPVolumeView(frame: .zero)
guard let slider = volumeView.subviews.first(where: { [=10=] is UISlider }) as? UISlider else {
assertionFailure("Unable to find the slider")
return
}
volumeView.clipsToBounds = true
view.addSubview(volumeView)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { [weak slider, weak volumeView] in
slider?.setValue(volume, animated: false)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { [weak volumeView] in
volumeView?.removeFromSuperview()
}
}
}
}
用法:
// set volume to 50%
viewController.setVolume(0.5)
您可以通过以下代码使用默认的 UISlider:
import MediaPlayer
class CusomViewCOntroller: UIViewController
// could be IBOutlet
var customSlider = UISlider()
// in code
var systemSlider = UISlider()
override func viewDidLoad() {
super.viewDidLoad()
let volumeView = MPVolumeView()
if let view = volumeView.subviews.first as? UISlider{
systemSlider = view
}
}
接下来在代码中写
systemSlider.value = customSlide.value
2018 年,致力于 iOS11.4
稍等片刻后,您需要更改 slider.value
。
extension MPVolumeView {
static func setVolume(_ volume: Float) {
let volumeView = MPVolumeView()
let slider = volumeView.subviews.first(where: { [=10=] is UISlider }) as? UISlider
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
slider?.value = volume
}
}
}
用法:
MPVolumeView.setVolume(0.5)
这是我的音频播放器应用的音量控制:
import UIKit
import MediaPlayer
class UIVolumeSlider: UISlider {
func activate(){
updatePositionForSystemVolume()
guard let view = superview else { return }
let volumeView = MPVolumeView(frame: .zero)
volumeView.alpha = 0.000001
view.addSubview(volumeView)
try? AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
AVAudioSession.sharedInstance().addObserver(self, forKeyPath: "outputVolume", options: .new, context: nil)
addTarget(self, action: #selector(valueChanged), for: .valueChanged)
}
func deactivate(){
AVAudioSession.sharedInstance().removeObserver(self, forKeyPath: "outputVolume")
removeTarget(self, action: nil, for: .valueChanged)
superview?.subviews.first(where: {[=10=] is MPVolumeView})?.removeFromSuperview()
}
func updatePositionForSystemVolume(){
try? AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
value = AVAudioSession.sharedInstance().outputVolume
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "outputVolume", let newVal = change?[.newKey] as? Float {
setValue(newVal, animated: true)
}
}
@objc private func valueChanged(){
guard let superview = superview else {return}
guard let volumeView = superview.subviews.first(where: {[=10=] is MPVolumeView}) as? MPVolumeView else { return }
guard let slider = volumeView.subviews.first(where: { [=10=] is UISlider }) as? UISlider else { return }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
slider.value = self.value
}
}
}
连接方式:
- 在 Storyboard 的 Identity Inspector 中设置 UIVolumeSlider class:
- 将实例连接到 class:
@IBOutlet private var volumeSlider:UIVolumeSlider!
- 在viewDidLoad方法中:
volumeSlider.updatePositionForSystemVolume()
- 在 didAppear 和 willDisappear: 中激活和停用它
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
volumeSlider.activate()
}
override func viewWillDisappear(_ animated: Bool) {
volumeSlider.deactivate()
super.viewWillDisappear(animated)
}
Swift 5 / iOS 13
这是我找到的访问系统音量级别的最可靠方法。这是基于此处的其他答案,但不会浪费能源,也不需要异步等待。
在初始化期间(例如在viewDidLoad
中),创建一个离屏MPVolumeView
:
var hiddenSystemVolumeSlider: UISlider!
override func viewDidLoad() {
let volumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y:0, width:0, height:0))
view.addSubview(volumeView)
hiddenSystemVolumeSlider = volumeView.subviews.first(where: { [=10=] is UISlider }) as? UISlider
}
然后在需要时使用隐藏滑块获取或设置系统音量:
var systemVolume:Float {
get {
return hiddenSystemVolumeSlider.value
}
set {
hiddenSystemVolumeSlider.value = newValue
}
}
我不想在视图控制器中进行调用,所以我构建了一个不同的解决方案。您可以像这样从任何地方调用它:
SystemVolumeController.shared.systemVolume = 0.5
所有 MPVolumeView 解决方案要记住的一件事是,当 MPVolumeView 在屏幕上时,当用户按下 phone 没有出现。出于这个原因,我在这个解决方案中内置了一个计时器,它会在几秒钟后删除视图。
这里的一些解决方案建议您需要在设置音量之前设置延迟。我还没有发现我的情况,但你的里程可能会有所不同。
import MediaPlayer
final class SystemVolumeController {
static let shared: SystemVolumeController = SystemVolumeController()
private weak var volumeView: MPVolumeView?
private var removeTimer: Timer?
private func getVolumeView() -> MPVolumeView? {
if let volumeView = volumeView {
return volumeView
}
else {
removeTimer?.invalidate()
let volumeView = MPVolumeView(frame: CGRect(x: -CGFloat.greatestFiniteMagnitude, y:0, width:0, height:0))
guard let window = UIApplication.shared.windows.first else {
assertionFailure("No window to add the volume slider to")
return nil
}
window.addSubview(volumeView)
self.volumeView = volumeView
removeTimer = Timer.scheduledTimer(withTimeInterval: 3,
repeats: false,
block: { _ in
volumeView.removeFromSuperview()
})
return volumeView
}
}
private var volumeSlider: UISlider? {
getVolumeView()?.subviews.first(where: { [=10=] is UISlider }) as? UISlider
}
var systemVolume: Float {
get {
AVAudioSession.sharedInstance().outputVolume
}
set {
volumeSlider?.value = newValue
}
}
private init() {}
}```