自定义 Airplay 按钮的外观

Posted

技术标签:

【中文标题】自定义 Airplay 按钮的外观【英文标题】:Customize the Airplay button's appearance 【发布时间】:2011-03-02 22:14:07 【问题描述】:

我使用标准渐变叠加(在 photoshop 中完成)使按钮在我的应用中看起来更漂亮。我添加了一个 Airplay 按钮,但美观不匹配。

我真的很想在它上面放一个渐变层以便匹配,但我能找到的任何东西都只显示了如何使用 png 来做到这一点,而不是现有的 UIView。如果不是渐变层,我只需要以某种方式,any 来改变 Apple 播放按钮的外观,同时保持其功能不变。

设置代码很简单:

MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:frame];
[volumeView setShowsVolumeSlider:NO];
[bottomPanel addSubview:volumeView];

我怎样才能让它的外观与我的控件相匹配?

【问题讨论】:

如果其他人在回答后阅读了这个问题:永远不要改变字形/形状和位置!!谢谢 :) 换句话说,“如何使活动按钮显示为禁用?”我建议您不要混淆用户,而是让您的图像变白。 整个UI以这种不张扬的风格为主题,应用UI得到了高度评​​价。不过感谢您的意见。 "当有多个音频输出路由可用时,路由按钮默认可见。"我只有一个音频输出,所以我什至无法显示按钮。反正有没有模拟多个音频输出,这样我就可以玩这个按钮? @coneybeare 如果我能让按钮出现,我会努力改变它的外观。 【参考方案1】:

在接受@Erik B 的回答并将赏金授予他之后,我发现需要进行更多调整才能使其正常工作。我在这里发帖是为了未来的 SO 搜索者。

我看到的问题是按钮的内部机制会根据当前的播放状态分配图像。因此,如果 Airplay 接收器消失,或者状态以某种方式改变,我在初始化期间所做的任何自定义都不会生效。为了解决这个问题,我在按钮的alpha 键上设置了一个 KVO 观察。我注意到按钮总是淡入/淡出,这是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];

然后我观察按钮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)];
    

如果你销毁按钮,别忘了移除观察者

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

根据代码观察,如果 Apple 更改 MPVolumeView 的内部视图层次结构以添加/删除/更改视图以便出现不同的按钮,则该按钮将中断。这使得它有点脆弱,所以使用风险自负,或者想出一个计划 b,以防发生这种情况。我已经在生产中使用它一年多了,没有任何问题。如果您想查看它的实际效果,请查看 Ambiance 中的主播放器屏幕

【讨论】:

它似乎仍在 ios 5 中工作,但是当没有可用的 AirPlay 设备时触发该操作会触发没有任何按钮的空警报视图,基本上会使您的应用程序崩溃。 好的,让它工作。您需要在视图层次结构中的某处实际插入虚拟volumeView,否则它不会显示警报表。 如何判断当前可见的是选中的(蓝色)还是未选中的(白色)按钮?我知道这是可能的,Spotify 做到了。 :-) 在我的 iPhone 应用程序中,我正在添加 MPVolumeView,但为什么我看不到“airplay”按钮以及音量滑块? 你不需要KVO,在iOS7上测试过。初始设置就足够了。如果您需要更改按钮的两种状态,请为 UIControlStateNormal 和 UIControlStateSelected 提供图像。祝你好运【参考方案2】:

我终于找到了一个蓝牙耳机,这样我就可以测试按钮了。改变它的外观非常简单。代码如下:

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

仅此而已。

【讨论】:

+1 为正确答案 - 不过,我想补充一点,不鼓励这种操纵苹果组件标准界面和功能的方式,这可能是在批准过程中被拒绝的原因。此外,一旦苹果决定添加其他按钮或更改其顺序,这将失败。太糟糕了,没有其他方法可以更改 MPVolumeView 及其子视图/控件的外观。 @Till True,但 Apple 不太可能只添加另一个按钮,因为这会破坏任何 UI。无论如何,由 OP 来评估风险。我只是提供答案。 我希望得到一个不需要这样做的答案,但有时你必须做你必须做的事情。 Ambiance ambianceapp.com 是一个高调的应用程序,它已被使用。完全没有问题 如果稍后添加 AirPlay 按钮(带有自动动画?),您如何收到通知【参考方案3】:

更新:从 iOS 13 开始,此方法已弃用!

我认为自 iOS 6 以来有更好的解决方案: 使用

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

在 MPVolumeView 上。

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

【讨论】:

虽然我的答案在很长一段时间内都是正确的,但现在这是最好的方法,因为它是 API 的官方部分 routeButtonImage 所需的实际图像尺寸是多少? 此方法现已弃用 - iOS 13【参考方案4】:

上面的代码对我不起作用(因为稍后添加了 AirPlay 按钮)。作为一种解决方法,您可以在 UI 中使用标准 UIButton 并在(隐藏的)MPVolumeView 按钮中使用

触发 AirPlay
for (UIButton *button in volumeView.subviews)

    if ([button isKindOfClass:[UIButton class]]) 
    
        [button sendActionsForControlEvents:UIControlEventTouchUpInside];
    

副作用是,当只有一条路线可用时,按钮不会自动隐藏,这可能是也可能不是所需的行为。

【讨论】:

这似乎使用起来很危险,因为当没有可用的 AirPlay 设备时,iOS 5 会弹出一个没有任何控件的空警报视图。 抱歉,我无法重现。对我来说,它带来了一个带有 iPhone 和取消选项的警报视图。 (iOS 5.1) 我想我明白了:当 MPVolumeView 不在视图层次结构中的任何位置时,可能会发生这种情况。我认为它可能需要一些父框架来显示操作表。无论如何:通过将 MPVolumeView 添加到层次结构并将其隐藏,您的解决方案仍然有效。【参考方案5】:

我创建了一个完美运行的自定义方法...

-(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];

        

    


【讨论】:

以上是关于自定义 Airplay 按钮的外观的主要内容,如果未能解决你的问题,请参考以下文章

在我的自定义按钮中打开 Airplay 弹出框

带有自定义 html5 控件的 Airplay

自定义 DataTable 按钮的外观

无法使用 airplay (Apple TV) 将自定义视图推送到外部显示器

我将 UIButton 子类化以创建自己的自定义按钮外观,但它不会重绘所有触摸事件

Expression Blend创建自定义按钮