Customize the Airplay button's appearance

后端 未结 5 782
小鲜肉
小鲜肉 2020-12-23 00:22

I use a standard gradient overlay (done in photoshop) to make buttons look nicer in my app. I added an Airplay button, but the aesthetics are not matching.

相关标签:
5条回答
  • I finally found a bluetooth headset so that I could test the button. Changing its appearance was very simple. Here's the code:

    for (UIButton *button in volumeView.subviews) {
        if ([button isKindOfClass:[UIButton class]]) {
            [button setImage:[UIImage imageNamed:@"custom-route-button.png"] forState:UIControlStateNormal];
            [button sizeToFit];
        }
    }
    

    That's all there's to it.

    0 讨论(0)
  • 2020-12-23 00:39

    The above code didn't work for me (as the AirPlay button is added later). As a workaround you can a standard UIButton in your UI and trigger an AirPlay in a (hidden) MPVolumeView button with

    for (UIButton *button in volumeView.subviews)
    {
        if ([button isKindOfClass:[UIButton class]]) 
        {
            [button sendActionsForControlEvents:UIControlEventTouchUpInside];
        }
    }
    

    A side effect is that the button doesn't auto hide when only one route is available, which may or may not be desired behavior.

    0 讨论(0)
  • 2020-12-23 00:48

    After accepting @Erik B's answer and awarding the bounty to him, I found that there was more tweaking necessary to get it to work. I am posting here for the benefit of future SO searchers.

    The problem I was seeing was that the internal mechanisms of the buttons would assign the image based on the current airplay state. Thus any customizations I made during init would not stick if the Airplay receiver went away, or the state was changed somehow. To solve this, I setup a KVO observation on the button's alpha key. I noticed that the button is always faded in/out which is an animation on alpha.

    MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:CGRectZero];
    [volumeView setShowsVolumeSlider:NO];
    for (UIButton *button in volumeView.subviews) {
        if ([button isKindOfClass:[UIButton class]]) {
            self.airplayButton = button; // @property retain
            [self.airplayButton setImage:[UIImage imageNamed:@"airplay.png"] forState:UIControlStateNormal];
            [self.airplayButton setBounds:CGRectMake(0, 0, kDefaultIconSize, kDefaultIconSize)];
            [self.airplayButton addObserver:self forKeyPath:@"alpha" options:NSKeyValueObservingOptionNew context:nil];
        }
    }
    [volumeView sizeToFit];
    

    Then I observe the changed value of the buttons alpha.

    - (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.png"] forState:UIControlStateNormal];
            [(UIButton *)object setBounds:CGRectMake(0, 0, kDefaultIconSize, kDefaultIconSize)];
        }
    }
    

    Don't forget to remove the observer if you destroy the button

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

    Based on code observation, the button will break if Apple changes the internal view hierarchy of the MPVolumeView to add/remove/alter the views such that a different button comes up. This makes it kind of fragile, so use at your own risk, or come up with a plan b in case this happens. I have been using it for over a year in production with no issues. If you want to see it in action, check out the main player screen in Ambiance

    0 讨论(0)
  • 2020-12-23 00:49

    Update: As of iOS 13, this method is deprecated!

    I think there's a better solution to this since iOS 6: Use

    - (void)setRouteButtonImage:(UIImage *)image forState:(UIControlState)state;

    on MPVolumeView.

    See https://developer.apple.com/Library/ios/documentation/MediaPlayer/Reference/MPVolumeView_Class/index.html#//apple_ref/occ/instm/MPVolumeView/setRouteButtonImage:forState:

    0 讨论(0)
  • 2020-12-23 00:50

    I created a custom method which works perfectly...

    -(void)airplayIcon:(CGRect)rect {
        UIView *aiplayView = [[UIView alloc] initWithFrame:self.bounds];
        aiplayView.clipsToBounds = true;
        aiplayView.backgroundColor = [UIColor clearColor];
        [self addSubview:aiplayView];
    
        MPVolumeView *airplayVolume = [[MPVolumeView alloc] initWithFrame:aiplayView.bounds];
        airplayVolume.showsVolumeSlider = false;
        [aiplayView addSubview:airplayVolume];
    
        for (UIButton *button in airplayVolume.subviews) {
            [button setFrame:self.bounds];
            if ([button isKindOfClass:[UIButton class]]) {
                [button setImage:[UIImage imageNamed:@"normal.png"] forState:UIControlStateNormal];
                [button setImage:[UIImage imageNamed:@"selected.png"] forState:UIControlStateSelected];
                [button sizeToFit];
    
            }
    
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题