自定义Airplay按钮的外观

18

我在我的应用程序中使用标准的渐变叠加(在Photoshop中完成)来使按钮看起来更好。我添加了一个Airplay按钮,但美学效果不匹配。

输入图像描述

我真的想在它上面放一个渐变层,使它与之匹配,但我能找到的所有内容都只展示如何使用png而不是现有的UIView实现这一点。如果没有渐变层,我只需要以任何方式改变苹果Airplay按钮的外观,同时保持其功能不变。

设置代码很简单:

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

如何使这个与我的控件外观匹配?


如果其他人在问题得到回答后阅读此问题:永远不要更改字形/形状和位置!! 谢谢 :) - user142019
换句话说,“我如何使一个活动按钮看起来像是禁用的?”我建议您不要让用户感到困惑,而是将您的图像变为白色。 - Nicholas Riley
整个用户界面都采用了这种低调的风格,并且该应用程序的用户界面受到了高度赞扬。不过还是谢谢你的意见。 - coneybeare
当有多个音频输出路由可用时,默认情况下会显示路由按钮。我只有一个音频输出,所以我甚至无法让按钮显示出来。有没有办法模拟多个音频输出,以便我可以尝试操作此按钮? - Erik B
@coneybeare 如果我能让按钮出现,我会真正努力改变它的外观。 - Erik B
显示剩余2条评论
5个回答

16

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

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

就是这样了。


1
+1 分钟,但我想补充一点,这种操纵苹果组件标准界面和功能的方式是不被鼓励的,也可能是审核过程中被拒绝的原因之一。此外,一旦苹果决定添加其他按钮或更改它们的顺序,这种方法将会失败。很遗憾,没有其他方法可以改变 MPVolumeView 及其子视图/控件的外观。 - Till
2
@Till 确实如此,但苹果很可能不会只添加另一个按钮,因为那会破坏任何用户界面。无论如何,评估风险取决于 OP。我只提供答案。 - Erik B
@bdmontz 我在应用商店上没有任何带有修改的Airplay按钮的应用程序,所以我不能说我知道,但是这个解决方案不使用私有API方法,因此除非苹果有针对修改Airplay按钮的特定政策,否则我认为你会没问题。此外,创建自定义按钮只需要不到5分钟,恢复默认外观甚至更快。阅读指南以弄清楚苹果是否允许肯定需要更多时间,即使如此,您也无法确定。 - Erik B
2
Ambiance http://ambianceapp.com 是一个备受关注的应用程序,已经被广泛使用。它没有任何问题。 - coneybeare
4
如果以后添加了AirPlay按钮(包括自动动画),你将如何收到通知? - steipete
显示剩余6条评论

16

接受了@Erik B的答案并向他颁发赏金后,我发现还需要进行更多调整才能使其正常工作。为了方便未来在stackoverflow搜索的用户,我在这里发帖。

我遇到的问题是,按钮的内部机制会根据当前的Airplay状态分配图像。因此,在初始化时进行的任何自定义操作都不会保留,如果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"];
    …
}

根据代码观察,如果苹果更改MPVolumeView的内部视图层次结构以添加/删除/修改视图,可能会导致该按钮失效。这使得它有点脆弱,因此使用时需谨慎,或者在出现问题时考虑备选方案。我已经在生产中使用了一年,并没有遇到任何问题。如果您想看它的实际效果,请查看Ambiance中的主播放器屏幕。


它似乎在iOS 5上仍然可以工作,但是当没有AirPlay设备可用时触发该操作会触发一个空的警告视图,没有任何按钮,基本上会导致你的应用程序崩溃。 - THM
好的,成功了。你需要将虚拟的volumeView插入到视图层级中的某个位置,否则它无法显示警报表。 - THM
1
你如何判断当前可见的是选中(蓝色)还是未选中(白色)的按钮?我知道这是可能的,Spotify就做到了。 :-) - Dermot
2
在我的 iPhone 应用中,我正在添加 MPVolumeView,但为什么我无法看到伴随音量滑块的“AirPlay”按钮? - Satyam
你不需要使用KVO,在iOS7上测试过了。初始设置就足够了。如果你需要改变按钮的两种状态,那么请为UIControlStateNormal和UIControlStateSelected提供图像。祝好运! - iWheelBuy
KVO 是必须的,当 AirPlay 源出现和消失时,因为按钮有时会恢复原始状态。 - coneybeare

7

1
虽然我的答案曾经是正确的,但现在这是最好的方法,因为它是API的官方部分。 - coneybeare
路线按钮图片所需的实际图像尺寸是多少? - NSAnant
1
此方法现已弃用 - iOS 13 - Ryan

3

对我而言上述代码不起作用(因为AirPlay按钮是后添加的)。一个解决方法是在UI中添加一个标准的UIButton, 并通过(hidden的)MPVolumeView按钮触发AirPlay。

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

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

使用它似乎很危险,因为当没有可用的AirPlay设备时,iOS 5会弹出一个空的警告视图而没有任何控件来关闭它。 - THM
抱歉,但我无法重现那个问题。对我来说,在iPhone上它会弹出一个警告视图,其中包含取消选项。(iOS 5.1) - voidStern
2
我认为我懂了:当MPVolumeView不在视图层次结构中时,可能会出现这种情况。我想也许它需要一些父框架才能从中显示操作表。无论如何:通过将MPVolumeView添加到层次结构并隐藏它,您的解决方案仍然有效。 - THM

1
我创建了一个自定义方法,它完美地运行...
-(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];

        }

    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接