我正在写一个非常简单的应用程序,按下按钮时会发出声音。由于当设备设置为静音时该按钮没有多大意义,我想在设备的音量为零时禁用它。 (然后当音量再次升高时重新启用它。)
我正在寻找一种工作(和AppStore安全)方式来检测当前的音量设置,并在音量级别改变时获得通知/回调。我不想改变音量设置。
所有这些都在我的ViewController
中实现,其中使用了所述按钮。我用运行iOS 4.0.1和4.0.2的iPhone 4以及运行4.0.1的iPhone 3G测试了这个。使用iOS SDK 4.0.2和llvm 1.5构建。 (使用gcc或llvm-gcc不能改进任何东西。)在构建实现任何一种方式时都没有问题,既不是错误也不是警告。静态分析仪也很开心。
这是我到目前为止所尝试的,都没有任何成功。
根据Apple的音频服务文档,我应该为AudioSessionAddPropertyListener
注册一个kAudioSessionProperty_CurrentHardwareOutputVolume
,它应该像这样工作:
// Registering for Volume Change notifications
AudioSessionInitialize(NULL, NULL, NULL, NULL);
returnvalue = AudioSessionAddPropertyListener (
kAudioSessionProperty_CurrentHardwareOutputVolume ,
audioVolumeChangeListenerCallback,
self
);
returnvalue
是0
,这意味着注册回调有效。
可悲的是,当我按下设备上的音量按钮,耳机响铃或翻转振铃静音开关时,我从未收到回调功能audioVolumeChangeListenerCallback
。
当使用完全相同的代码注册kAudioSessionProperty_AudioRouteChange
(用作WWDC视频中的类似示例项目,开发人员文档和互联网上的众多站点)时,我实际上在更改音频路径时(通过插入/拔出)获得回调耳机或对接设备)。
一个名为Doug的用户打开了一个名为iPhone volume changed event for volume already max的帖子,他声称他成功地使用了这种方式(除非音量实际上没有变化,因为它已经设置为最大值)。不过,它对我不起作用。
我尝试过的另一种方法是像这样在NSNotificationCenter
注册。
// sharedAVSystemController
AudioSessionInitialize(NULL, NULL, NULL, NULL);
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(volumeChanged:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
这应该通知我的方法volumeChanged
任何SystemVolume
变化,但它实际上没有这样做。
由于共同的信念告诉我,如果一个人努力工作以实现与Cocoa的某些事情,那么我就会做一些根本错误的事情,我期待在这里错过一些东西。很难相信没有简单的方法可以获得当前的音量水平,但我还是找不到使用Apple的文档,示例代码,Google,Apple Developer论坛或观看WWDC 2010视频。
你有没有机会为volumeChanged:方法签名错误?这对我有用,倾倒在我的appdelegate中:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(volumeChanged:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
}
- (void)volumeChanged:(NSNotification *)notification
{
float volume =
[[[notification userInfo]
objectForKey:@"AVSystemController_AudioVolumeNotificationParameter"]
floatValue];
// Do stuff with volume
}
我的volumeChanged:方法在每次按下按钮时都会被击中,即使音量没有因此而改变(因为它已经是最大/分钟)。
此处某些答案使用的AudioSession
API自iOS 7起已被弃用。它被AVAudioSession
取代,后者为系统范围的输出量公开了outputVolume
属性。如文档中所指出的,使用KVO可以观察到当卷发生变化时接收通知:
值范围为0.0到1.0,其中0.0表示最小体积,1.0表示最大体积。
系统范围的输出量只能由用户直接设置;要在您的应用程序中提供音量控制,请使用MPVolumeView类。
您可以使用键值观察来观察对此属性值的更改。
您需要确保应用程序的音频会话处于活动状态才能使其正常工作:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(true)
startObservingVolumeChanges()
} catch {
print(“Failed to activate audio session")
}
因此,如果您只需要查询当前系统卷:
let volume = audioSession.outputVolume
或者我们可以收到类似变化的通知:
private struct Observation {
static let VolumeKey = "outputVolume"
static var Context = 0
}
func startObservingVolumeChanges() {
audioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.Initial, .New], context: &Observation.Context)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &Observation.Context {
if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeNewKey] as? NSNumber)?.floatValue {
// `volume` contains the new system output volume...
print("Volume: \(volume)")
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
在被解除分配之前不要忘记停止观察:
func stopObservingVolumeChanges() {
audioSession.removeObserver(self, forKeyPath: Observation.VolumeKey, context: &Observation.Context)
}
-(float) getVolumeLevel
{
MPVolumeView *slide = [MPVolumeView new];
UISlider *volumeViewSlider;
for (UIView *view in [slide subviews]){
if ([[[view class] description] isEqualToString:@"MPVolumeSlider"]) {
volumeViewSlider = (UISlider *) view;
}
}
float val = [volumeViewSlider value];
[slide release];
return val;
}
这应该可以获得当前的音量水平。 1是最大音量,0是无音量。注意:不需要显示UI元素才能使其生效。另请注意,当前音量级别与耳机或扬声器相关(意味着两个音量级别不同,这可以使您获得当前正在使用的设备。这不会回答有关接收音量变化时的通知的问题。
你是否用AudioSessionSetActive开始音频会话?
Swift 3版Stuart的优秀答案:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(true)
startObservingVolumeChanges()
}
catch {
print("Failed to activate audio session")
}
let volume = audioSession.outputVolume
private struct Observation {
static let VolumeKey = "outputVolume"
static var Context = 0
}
func startObservingVolumeChanges() {
audioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.Initial, .New], context: &Observation.Context)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &Observation.Context {
if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeNewKey] as? NSNumber)?.floatValue {
// `volume` contains the new system output volume...
print("Volume: \(volume)")
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
我认为这取决于其他实施。例如,如果您使用滑块来控制声音音量,您可以通过UIControlEventValueChanged
进行检查操作,如果您获得0值,则可以将按钮设置为隐藏或禁用。
就像是:
[MusicsliderCtl addTarget:self action:@selector(checkZeroVolume:)forControlEvents:UIControlEventValueChanged];
void checkZeroVolume
可以比较实际音量,因为它在任何音量变化后被触发。
使用AVAudioSession添加Stuart的答案来解释Swift 3中的一些变化。我希望代码能够清楚地说明每个组件的位置。
override func viewWillAppear(_ animated: Bool) {
listenVolumeButton()
}
func listenVolumeButton(){
let audioSession = AVAudioSession.sharedInstance()
do{
try audioSession.setActive(true)
let vol = audioSession.outputVolume
print(vol.description) //gets initial volume
}
catch{
print("Error info: \(error)")
}
audioSession.addObserver(self, forKeyPath: "outputVolume", options:
NSKeyValueObservingOptions.new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "outputVolume"{
let volume = (change?[NSKeyValueChangeKey.newKey] as
NSNumber)?.floatValue
print("volume " + volume!.description)
}
}
override func viewWillDisappear(_ animated: Bool) {
audioSession.removeObserver(self, forKeyPath: "outputVolume")
}
斯威夫特4
func startObservingVolumeChanges() {
avAudioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.initial, .new], context: &Observation.Context)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &Observation.Context {
if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeKey.newKey] as? NSNumber)?.floatValue {
print("\(logClassName): Volume: \(volume)")
}
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
func stopObservingVolumeChanges() {
avAudioSession.removeObserver(self, forKeyPath: Observation.VolumeKey, context: &Observation.Context)
}
然后你打电话
var avAudioSession = AVAudioSession.sharedInstance()
try? avAudioSession.setActive(true)
startObservingVolumeChanges()
进入设置 - >声音并选中“使用按钮更改”。如果关闭,按音量按钮时系统音量不会改变。也许这就是你没有得到通知的原因。