Comment obtenir le niveau de volume audio, et le volume modifié notifications sur iOS?
j'écris une application très simple qui joue un son en appuyant sur un bouton. Puisque ce bouton n'a pas beaucoup de sens quand l'appareil est réglé au silence, je veux le désactiver quand le volume audio de l'appareil est zéro. (Et ensuite le réenrêter lorsque le volume est augmenté de nouveau.)
je cherche un moyen efficace (et sûr AppStore) de détecter le réglage de volume actuel et obtenir un avis/rappel lorsque le volume les changements de niveau. I ne veulent pas modifier le réglage du volume .
tout ceci est implémenté dans mon ViewController
où ce bouton est utilisé. J'ai testé cela avec un iPhone 4 tournant sous iOS 4.0.1 et 4.0.2 ainsi qu'un iPhone 3G tournant sous 4.0.1. Construit avec IOS SDK 4.0.2 avec llvm 1.5. (Utiliser gcc ou llvm-gcc n'améliore rien.) Il n'y a pas de problèmes lors de la mise en œuvre de la construction dans un sens ou dans l'autre, ni d'erreurs ni d'Avertissements. Analyseur statique est heureux ainsi.
Voici ce que j'ai essayé jusqu'à présent, toutes sans succès.
suite à la documentation des services audio D'Apple je devrais enregistrer un AudioSessionAddPropertyListener
pour kAudioSessionProperty_CurrentHardwareOutputVolume
qui devrait fonctionner comme ceci:
// Registering for Volume Change notifications
AudioSessionInitialize(NULL, NULL, NULL, NULL);
returnvalue = AudioSessionAddPropertyListener (
kAudioSessionProperty_CurrentHardwareOutputVolume ,
audioVolumeChangeListenerCallback,
self
);
returnvalue
est 0
, ce qui signifie que l'enregistrement du rappel a fonctionné.
malheureusement, je n'obtiens jamais un rappel à ma fonction audioVolumeChangeListenerCallback
quand j'appuie sur les boutons de volume sur mon dispositif, le casque cliquent ou appuyer sur le commutateur ringer-silent.
lors de l'utilisation du même code pour s'enregistrer pour kAudioSessionProperty_AudioRouteChange
(qui est utilisé comme un projet d'échantillon analogue dans les vidéos WWDC, la documentation du développeur et sur de nombreux sites sur les interwebs) Je effectivement do obtenir un rappel lors du changement de la route audio (en branchant/Hors un casque ou en amarrant l'appareil).
un utilisateur nommé Doug ouvert un fil intitulé iPhone volume changé événement pour le volume déjà max où il a déclaré qu'il utilise avec succès cette façon (à moins que le volume ne serait pas réellement changer parce qu'il est déjà fixé au maximum). Pourtant, il ne fonctionne pas pour moi.
une autre façon que j'ai essayé est de m'inscrire à NSNotificationCenter
comme ceci.
// sharedAVSystemController
AudioSessionInitialize(NULL, NULL, NULL, NULL);
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(volumeChanged:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
cela devrait notifier ma méthode volumeChanged
de toute modification SystemVolume
mais il n'est pas réellement le faire.
depuis la croyance commune me dit que si on travaille trop dur pour accomplir quelque chose avec le cacao on fait quelque chose fondamentalement mal je m'attends à manquer quelque chose ici. Il est difficile de croire qu'Il ya no simple way à obtenir le niveau de volume actuel, mais je n'ai pas été en mesure d'en trouver un en utilisant la documentation D'Apple, code échantillon, Google, Apple Developer Forums ou en regardant WWDC 2010 vidéo.
9 réponses
y a-t-il une chance que vous ayez mal signé pour la méthode volumeChanged:? Cela a fonctionné pour moi, jeté dans mon 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
}
Mon volumeChanged: méthode reçoit le coup à chaque fois que le bouton est pressé, même si le volume ne change pas (car il est déjà au max/min).
L'API AudioSession
utilisée par certaines réponses a été dépréciée depuis iOS 7. Il a été remplacé par AVAudioSession
, qui expose une propriété outputVolume
pour le volume de production à l'échelle du système. Cela peut être observé en utilisant KVO pour recevoir des notifications lorsque le volume change, comme indiqué dans la documentation:
valeur comprise entre 0,0 et 1,0, 0,0 représentant le minimum volume et 1.0 représentant le volume maximum.
le volume de sortie à l'échelle du système ne peut être réglé directement que par l'utilisateur; à fournir le contrôle de volume dans votre application, Utilisez la classe MPVolumeView.
vous pouvez observer les changements de la valeur de cette propriété en utilisant observation de la valeur clé.
vous devez vous assurer que la session audio de votre application est active pour que cela fonctionne:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(true)
startObservingVolumeChanges()
} catch {
print(“Failed to activate audio session")
}
donc si tout ce que vous avez besoin est d'interroger le volume du système actuel:
let volume = audioSession.outputVolume
Ou nous pouvons être informé des changements de la sorte:
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)
}
}
N'oubliez pas arrêter d'observer avant d'être désalloué:
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;
}
qui devrait vous donner le niveau de volume actuel. 1 est le volume maximum, 0 n'est pas le volume. Remarque: Il n'est pas nécessaire d'Afficher des éléments D'assurance-chômage pour que cela fonctionne. Notez également que le niveau de volume actuel est relatif aux écouteurs ou haut-parleurs (c'est-à-dire que les deux niveaux de volume sont différents, ce qui vous permet de choisir celui que l'appareil utilise actuellement. Cela ne répond pas à votre question concernant la réception des notifications de changement de volume.
avez-vous commencé la session audio avec AudioSessionSetActive
je pense que cela dépend d'autres implémentations. Si vous utilisez par exemple le curseur pour contrôler le volume du son, vous pouvez faire une action de vérification par UIControlEventValueChanged
et si vous obtenez une valeur 0, Vous pouvez régler le bouton caché ou désactivé.
quelque chose comme:
[MusicsliderCtl addTarget:self action:@selector(checkZeroVolume:)forControlEvents:UIControlEventValueChanged];
vide checkZeroVolume
pourrait faire la comparaison entre le volume réel puisqu'il est déclenché après tout changement de volume.
ajout à la réponse de Stuart en utilisant AVAudioSession pour rendre compte de certains changements dans Swift 3. J'espère que le code indiquera clairement où va chaque composante.
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")
}
version Swift 3 de L'excellente réponse de 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)
}
}
Swift 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)
}
et puis vous appelez
var avAudioSession = AVAudioSession.sharedInstance()
try? avAudioSession.setActive(true)
startObservingVolumeChanges()
Allez dans paramètres->sons et cocher "Modifier avec les Boutons'. S'il est éteint le volume du système ne changera pas en appuyant sur les boutons de volume. C'est peut-être la raison pour laquelle vous n'avez pas averti.