如何在iOS上更改设备音量 - 不是音乐音量

16

我希望在iOS(iPhone)上更改设备音量。

我知道可以使用以下代码更改音乐库的音量:

//implement at first MediaPlayer framework
MPMusicPlayerController *musicPlayer = [MPMusicPlayerController iPodMusicPlayer];
musicPlayer.volume = 1;

但这不是我的目的。
我想改变设备音量,或者说更改铃声的音量。

我该怎么做?只是更改设备音量吗?

9个回答

19

回答 brush51 的问题:

我该怎么做?只改变设备音量吗?

正如 0x7fffffff 建议的:

你无法通过编程方式更改设备音量,但是有 MPVolumeView(音量滑块)可用于更改设备音量,但仅限用户交互。

因此,苹果建议使用 MPVolumeView,因此我得出以下内容:

添加 volumeSlider 属性:

@property (nonatomic, strong) UISlider *volumeSlider;

初始化 MPVolumeView 并将其添加到您的视图中(可以隐藏,无框架或空白,因为 showsRouteButton = NOshowsVolumeSlider = NO):

MPVolumeView *volumeView = [MPVolumeView new];
volumeView.showsRouteButton = NO;
volumeView.showsVolumeSlider = NO;
[self.view addSubview:volumeView];

查找并保存对 UISlider 的引用:

__weak __typeof(self)weakSelf = self;
[[volumeView subviews] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if ([obj isKindOfClass:[UISlider class]]) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        strongSelf.volumeSlider = obj;
        *stop = YES;
    }
}];

UIControlEventValueChanged添加目标操作:

[self.volumeSlider addTarget:self action:@selector(handleVolumeChanged:) forControlEvents:UIControlEventValueChanged];

然后检测音量的变化(例如通过硬件音量控制):

- (void)handleVolumeChanged:(id)sender
{
    NSLog(@"%s - %f", __PRETTY_FUNCTION__, self.volumeSlider.value);
}

并且反过来,您也可以通过以下方式设置音量:

self.volumeSlider.value = < some value between 0.0f and 1.0f >;

希望这可以帮到你(也希望苹果不会从MPVolumeView中移除MPVolumeSlider)。


1
你有测试过吗?你因为程序化更改音量而被拒绝了吗? - iCaramba
1
在我的iOS 7.1和8上无法工作。滑块的值会改变,但系统声音大小不会改变。 - Dvole
@msrdjan:根据我在iOS 8.3中的检查,如果volumeView.showsRouteButton = NO; volumeView.showsVolumeSlider = NO;,则音量值不会被设置。应该保持默认为YES。 - nahung89
1
在我的测试中,只有在同时连续播放声音并首先将音量设置为大于0,然后再降低到0时才能正常工作 - 这仅供遇到相同问题的人参考。 - Christian

8

谢谢你,这节省了我几个小时或更多的时间,试图找出如何干净地访问那个滑块。我必须说,这在苹果方面是如此愚蠢 - 我的应用程序全部都是手势,而滑块没有任何用处。希望他们不会因为这个原因拒绝我的应用程序。我会在代码中留下一个好评。 - solenoid
3
几点说明:在应用程序之外更改音量(例如控制中心)会导致此功能无法正常工作。可能是因为我将其放在全局变量 appDelegate 中使用,所以它似乎起作用了。在许多情况下,我也遇到了起始值为 0 的问题。在获取/设置值之前,在某些时候运行 volumeView.setNeedsLayout() 可以解决这个问题。 - solenoid

8

以下是我所做的内容:

func setSystemVolume(volume: Float) {
    let volumeView = MPVolumeView()

    for view in volumeView.subviews {
        if (NSStringFromClass(view.classForCoder) == "MPVolumeSlider") {
            let slider = view as! UISlider
            slider.setValue(volume, animated: false)
        }
    }
}

自iOS 7起,MPMusicPlayerController中的volume属性已被弃用。以下是您唯一可做的方法。


1
我很好奇,虽然这个可以运行,但是已经通过App Store审核了吗? - Brian H

6
您需要使用“applicationMusicPlayer”而不是“iPodMusicPlayer”来设置系统音量:
#import <MediaPlayer/MediaPlayer.h>
musicPlayer = [MPMusicPlayerController applicationMusicPlayer];
musicPlayer.volume = 1; // max volume
musicPlayer.volume = 0; // min volume (mute)
musicPlayer.volume = 0.0625; // 1 bar on the overlay volume display

9
注意:这在iOS 7中已经过时。 - Diego Freniche

3
这是一个小封装类,可以设置系统音量并在任何更改时通知您(通过setTarget:action:传递自己的更改响应者)。
编辑:如果隐藏MPVolumeView控件或不将其添加为子视图,则每当您以编程方式更改音量时,系统都会在屏幕中央显示默认音量方块。如果您不喜欢系统的操作,则解决方案是设置视图坐标超出屏幕范围。我稍微修改了下面的代码以反映这一点。
@import MediaPlayer;


@interface SysVolumeControl : MPVolumeView
@property (nonatomic) float value; // from 0 to 1.0
- (void)setTarget:(id)target action:(SEL)action;
@end


@implementation SysVolumeControl
{
    UISlider* _slider;
}

- (id)init
{
    if (self = [super initWithFrame:CGRectMake(-300, 0, 200, 50)])
    {
        for (UIView* view in self.subviews)
        {
            if ([view isKindOfClass:UISlider.class])
            {
                _slider = (UISlider*)view;
                break;
            }
        }
    }
    return self;
}

- (float)value
    { return _slider.value; }

- (void)setValue:(float)value
    { _slider.value = value; }

- (void)setTarget:(id)target action:(SEL)action
    { [_slider addTarget:target action:action forControlEvents:UIControlEventValueChanged]; }

@end

然后创建一个实例并使用它:
SysVolumeControl* sysVolumeControl = [SysVolumeControl new];
[myView addSubview:sysVolumeControl];
sysVolumeControl.value = 1.0;
[sysVolumeControl setTarget:self action:@selector(mySysVolumeResponder:)];

1

在iOS 11.4中,动态创建MPVolumeView来获取滑块并设置音量不再有效。我通过将新的MPVolumeView添加到我的UIViewController视图中来解决这个问题,否则它无法设置音量。由于我将其添加到控制器中,因此还需要将音量视图位置设置为屏幕外。

代码使用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 setMaxVolume() {
    let lst = volumeControl.subviews.filter{NSStringFromClass($0.classForCoder) == "MPVolumeSlider"}
    let slider = lst.first as? UISlider

    slider?.setValue(1, animated: false)
}

这种方法在iOS 11.4中已经不再适用,请参考https://dev59.com/clwX5IYBdhLWcg3w-DfJ#50740234。 - Andrea Gorrieri

1
应用程序不应更改设备全局设置,你也不应该这样做。用户可以使用标准的iOS功能来完成此操作。相反,如果你正在播放音频/视频,则应使用播放器的音量属性:
var player: AVPlayer!
...
player.volume = 0.5 // 50% level

0
在你的viewDidLoad中调用listenVolumeButton,并将//行替换为你想要执行的任何操作。
private func listenVolumeButton() {
    let audioSession = AVAudioSession.sharedInstance()
    try? audioSession.setActive(true)
    audioSession.addObserver(self,
                             forKeyPath: "outputVolume",
                             options: NSKeyValueObservingOptions.new,
                             context: nil)

    hideVolumeView()
}

private func hideVolumeView() {
    let volumeView = MPVolumeView(frame: CGRect.zero)
    for subview in volumeView.subviews {
        if let button = subview as? UIButton {
            button.setImage(nil, for: .normal)
            button.isEnabled = false
            button.sizeToFit()
        }
    }
    UIApplication.shared.windows.first?.addSubview(volumeView)
    UIApplication.shared.windows.first?.sendSubviewToBack(volumeView)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
    switch keyPath {
    case "outputVolume":
        guard let systemSlider = MPVolumeView().subviews.first(where: { NSStringFromClass($0.classForCoder) == "MPVolumeSlider" }) as? UISlider else {
            return
        }

        let avoidLimitsValue = max(min(AVAudioSession.sharedInstance().outputVolume, Float(0.9)), Float(0.1))

        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
            systemSlider.setValue(avoidLimitsValue, animated: false)
        }

        // DO YOUR STUFF HERE
    default:
        break
    }
}

0

Swift 3 - 我使用这个:

func setVolumeTo(volume: Float) {
  (MPVolumeView().subviews.filter{NSStringFromClass($0.classForCoder) == "MPVolumeSlider"}.first as? UISlider)?.setValue(volume, animated: false)
}

1
这种方法在iOS 11.4中已经不再适用,请参考https://dev59.com/clwX5IYBdhLWcg3w-DfJ#50740234。 - Andrea Gorrieri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接