Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Edit:</p> <p>Though the code below works on both iOS 5 and 6, starting with iOS 6.0 there is an official way to do this, which is much much easier. Just look at the documentation of <code>MPVolumeView</code>, specifically <code>– setRouteButtonImage:forState:</code>.</p> <p>==== Old answer: ====</p> <p>This is pretty hard to accomplish, but I found a way for iOS 5.0+. First of all, add the following line to your ViewController:</p> <pre><code>#import &lt;AudioToolbox/AudioToolbox.h&gt; </code></pre> <p>In your viewDidLoad, you're already doing most of the things right, this is my code:</p> <pre><code>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]; } } </code></pre> <p>Here's the helper <code>setAirPlayButtonSelected</code> method, it just sets the image:</p> <pre><code>- (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]; } </code></pre> <p>For completion's sake, the <code>observeValueForKeyPath</code>:</p> <pre><code>- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object == self.airPlayButton &amp;&amp; [[change valueForKey:NSKeyValueChangeNewKey] intValue] == 1) { [self setAirPlayButtonSelected:[self isAirPlayActive]]; } } </code></pre> <p>And now comes the interesting part. Here's the <code>isAirPlayActive</code> helper method. It uses the AudioSession framework to determine the currently playing audioSource. </p> <pre><code>- (BOOL)isAirPlayActive{ CFDictionaryRef currentRouteDescriptionDictionary = nil; UInt32 dataSize = sizeof(currentRouteDescriptionDictionary); AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &amp;dataSize, &amp;currentRouteDescriptionDictionary); if (currentRouteDescriptionDictionary) { CFArrayRef outputs = CFDictionaryGetValue(currentRouteDescriptionDictionary, kAudioSession_AudioRouteKey_Outputs); if(CFArrayGetCount(outputs) &gt; 0) { CFDictionaryRef currentOutput = CFArrayGetValueAtIndex(outputs, 0); CFStringRef outputType = CFDictionaryGetValue(currentOutput, kAudioSession_AudioRouteKey_Type); return (CFStringCompare(outputType, kAudioSessionOutputRoute_AirPlay, 0) == kCFCompareEqualTo); } } return NO; } </code></pre> <p>So all this code changes the AirPlay Button correctly on app launch. What about updates? We need to listen for AudioSource changes. Add the following line to your <code>viewDidLoad</code>:</p> <pre><code> AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, audioRouteChangeCallback, (__bridge void*)self); </code></pre> <p>Don't forget to unregister in <code>dealloc</code>:</p> <pre><code>- (void)dealloc { [self.airPlayButton removeObserver:self forKeyPath:@"alpha"]; AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange, audioRouteChangeCallback, (__bridge void*)self); } </code></pre> <p>And add this C function above your ViewController's <code>@implementation</code>:</p> <pre><code>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) &gt; 0) { CFDictionaryRef currentOutput = CFArrayGetValueAtIndex(outputs, 0); CFStringRef outputType = CFDictionaryGetValue(currentOutput, kAudioSession_AudioRouteKey_Type); [(__bridge SettingsViewController*)inUserData setAirPlayButtonSelected:CFStringCompare(outputType, kAudioSessionOutputRoute_AirPlay, 0) == kCFCompareEqualTo]; } } </code></pre> <p>As you can see, all it does is determine whether or not an AirPlay output source is active and calls the <code>setAirPlayButtonSelected</code> method accordingly.</p> <p>See Apple's <a href="http://developer.apple.com/library/ios/#documentation/Audio/Conceptual/AudioSessionProgrammingGuide/Introduction/Introduction.html" rel="noreferrer">Audio Session Programming Guide</a>, specifically <a href="http://developer.apple.com/library/ios/#documentation/Audio/Conceptual/AudioSessionProgrammingGuide/HandlingRouteChanges/HandlingRouteChanges.html#//apple_ref/doc/uid/TP40007875-CH12-SW1" rel="noreferrer">this section</a> for detailed information on how the callbacks exactly work, etc.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload