2012-09-07 12 views
7

Hace 2 días que intento resolver este problema y me rindo. Estoy tratando de implementar un botón Airplay personalizado (tengo que ser porque el fondo es blanco y el botón debe ser negro). He añadido una vista en Interfacebuilder y elegí mpVolumeView para ella. Luego hice las conexiones y escribí el siguiente código;Cómo personalizar el botón Airplay cuando Airplay está activo

viewDidLoad.. { 
    ..... 

    [_volumeView setShowsVolumeSlider:NO]; 
    for (UIButton *button in _volumeView.subviews) { 
     if ([button isKindOfClass:[UIButton class]]) { 
      [button setImage:[UIImage imageNamed:@"airplay_icon.png"] forState:UIControlStateNormal]; 
      [button addObserver:self forKeyPath:@"alpha" options:NSKeyValueObservingOptionNew context:nil]; 
      [button addTarget:self action:@selector(switchAirplayButton) forControlEvents:UIControlEventTouchUpInside]; 
      [button sizeToFit]; 
     } 
    } 
    [_volumeView sizeToFit]; 

} 

-(void)switchAirplayButton { 

    for (UIButton *button in _volumeView.subviews) { 
     if ([button isKindOfClass:[UIButton class]]) { 

      NSLog(@"%d", _controlBar.player.airPlayVideoActive); 

      if(_controlBar.player.airPlayVideoActive) { 
       [button setImage:[UIImage imageNamed:@"airplay_icon_pressed.png"] forState:UIControlStateNormal]; 
      } else [button setImage:[UIImage imageNamed:@"airplay_icon.png"] forState:UIControlStateNormal]; 

      [button sizeToFit]; 
     } 
    } 


} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
    if ([object isKindOfClass:[UIButton class]] && [[change valueForKey:NSKeyValueChangeNewKey] intValue] == 1) { 
     [(UIButton *)object setImage:[UIImage imageNamed:@"airplay_icon.png"] forState:UIControlStateNormal]; 
     [(UIButton *)object sizeToFit]; 
    } 
} 

El "jugador" es un singelton basado en AVPLayer. Sin embargo, siempre devuelve falso al verificar si airPlay está activo. Quizás es solo porque estoy usando sonido, no video.

Así que mi pregunta es, ¿cómo podría cambiar el botón para ... digamos uno naranja (solo para que coincida con el resto de la interfaz) cuando se transmite airplay (al igual que la manzana lo está haciendo azul). He intentado todo y simplemente no funciona en absoluto. Por favor, ayúdame.

+0

chikuba, cómo manejas este problema, estoy enfrentando el mismo problema en mi aplicación de VoIP para iOS, ¿podrías darme alguna sugerencia? ¡Gracias! –

Respuesta

18

Editar:

Aunque el código de abajo funciona tanto en iOS 5 y 6, comenzando con iOS 6.0 hay una forma oficial de hacer esto, que es mucho más fácil. Basta con mirar la documentación de MPVolumeView, específicamente – setRouteButtonImage:forState:.

==== vieja respuesta: ====

Esto es bastante difícil de lograr, pero he encontrado una manera para iOS 5.0 o superior. En primer lugar, añada la siguiente línea a su ViewController:

#import <AudioToolbox/AudioToolbox.h> 

En su viewDidLoad, que ya está haciendo la mayor parte de las cosas bien, esta es mi código:

for (id current in self.volumeView.subviews){ 
    if([current isKindOfClass:[UIButton class]]) { 
     UIButton *airPlayButton = (UIButton*)current; 
     self.airPlayButton = airPlayButton; 
     [self setAirPlayButtonSelected:[self isAirPlayActive]]; 
     [airPlayButton addObserver:self forKeyPath:@"alpha" options:NSKeyValueObservingOptionNew context:nil]; 
    } 
} 

Aquí está el ayudante setAirPlayButtonSelected método, sólo se establece la imagen:

- (void)setAirPlayButtonSelected:(BOOL)selected { 
    UIImage* image; 
    if (selected) { 
     image = [UIImage imageNamed:@"button-airplay-selected"]; 
    }else { 
     image = [UIImage imageNamed:@"button-airplay"]; 
    } 
    [self.airPlayButton setImage:image forState:UIControlStateNormal]; 
    [self.airPlayButton setImage:image forState:UIControlStateHighlighted]; 
    [self.airPlayButton setImage:image forState:UIControlStateSelected]; 
} 

Por el bien de la finalización, la observeValueForKeyPath:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 

    if (object == self.airPlayButton && [[change valueForKey:NSKeyValueChangeNewKey] intValue] == 1) { 
     [self setAirPlayButtonSelected:[self isAirPlayActive]]; 
    } 
} 

Y ahora viene la parte interesante. Aquí está el método de ayuda isAirPlayActive. Utiliza el marco AudioSession para determinar el audioSource que se está reproduciendo actualmente.

- (BOOL)isAirPlayActive{ 
    CFDictionaryRef currentRouteDescriptionDictionary = nil; 
    UInt32 dataSize = sizeof(currentRouteDescriptionDictionary); 
    AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &dataSize, &currentRouteDescriptionDictionary); 
    if (currentRouteDescriptionDictionary) { 
     CFArrayRef outputs = CFDictionaryGetValue(currentRouteDescriptionDictionary, kAudioSession_AudioRouteKey_Outputs); 
     if(CFArrayGetCount(outputs) > 0) { 
      CFDictionaryRef currentOutput = CFArrayGetValueAtIndex(outputs, 0); 
      CFStringRef outputType = CFDictionaryGetValue(currentOutput, kAudioSession_AudioRouteKey_Type); 
      return (CFStringCompare(outputType, kAudioSessionOutputRoute_AirPlay, 0) == kCFCompareEqualTo); 
     } 
    } 

    return NO; 
} 

Así que todo este código cambia el botón de AirPlay correctamente al iniciar la aplicación. ¿Qué hay de las actualizaciones? Necesitamos escuchar los cambios de AudioSource. Agregue la siguiente línea a su viewDidLoad:

AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, audioRouteChangeCallback, (__bridge void*)self); 

No se olvide de anular el registro en dealloc:

- (void)dealloc { 
    [self.airPlayButton removeObserver:self forKeyPath:@"alpha"]; 

    AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange, audioRouteChangeCallback, (__bridge void*)self); 

} 

y añadir a la función C por encima de su ViewController de @implementation:

void audioRouteChangeCallback (void     *inUserData, 
             AudioSessionPropertyID inPropertyID, 
             UInt32     inPropertyValueSize, 
             const void    *inPropertyValue) { 

    if (inPropertyID != kAudioSessionProperty_AudioRouteChange) { 
     return; 
    } 

    CFDictionaryRef routeChangeDictionary = inPropertyValue; 

    CFDictionaryRef currentRouteDescriptionDictionary = CFDictionaryGetValue(routeChangeDictionary, kAudioSession_AudioRouteChangeKey_CurrentRouteDescription); 
    CFArrayRef outputs = CFDictionaryGetValue(currentRouteDescriptionDictionary, kAudioSession_AudioRouteKey_Outputs); 
    if(CFArrayGetCount(outputs) > 0) { 
     CFDictionaryRef currentOutput = CFArrayGetValueAtIndex(outputs, 0); 
     CFStringRef outputType = CFDictionaryGetValue(currentOutput, kAudioSession_AudioRouteKey_Type); 

     [(__bridge SettingsViewController*)inUserData setAirPlayButtonSelected:CFStringCompare(outputType, kAudioSessionOutputRoute_AirPlay, 0) == kCFCompareEqualTo]; 
    } 

} 

Como puede ver, todo lo que hace es determinar si una fuente de salida de AirPlay está activa o no y llama al método del método setAirPlayButtonSelected ingly.

Consulte el Audio Session Programming Guide de Apple, específicamente this section para obtener información detallada sobre cómo funcionan exactamente las devoluciones de llamada, etc.

+0

acaba de ganarse un rep-boost real :) llegué a casi la misma solución ayer, pero usted señaló algunas cosas realmente buenas que tengo que tener en mente (eliminar el oyente, por ejemplo). ¡muchas gracias! – chikuba

+0

El observador alfa es lo que necesitaba para mi solución. ¡Buena cosa! : D –

+0

en iOS 8 necesita cambiar: if (CFArrayGetCount (outputs)> 0) to: if (outputs && CFArrayGetCount (outputs)> 0) –

0

Salida esto similar link here.

No estoy seguro de si esto iba a funcionar, pero trate de hacer un botón negro con el símbolo radiofónica, luego ponerlo en la parte superior del botón de AirPlay que es difícil de ver. Debe configurar la interacción del usuario como deshabilitada. Pruébalo, podría ser una solución más fácil.

* no olvides desactivar la interacción del usuario en el inspector de atributos y el inspector de identidad.

Cuestiones relacionadas