能否更改UITabBarItem徽章的颜色?

33

我想改变UITabBarItem badge的背景颜色,但是找不到任何关于如何实现的资源。

输入图片说明

17个回答

44

当我仍然想支持iOS9时会发生什么?我需要将其包含在respondsToSelector#available调用中吗? - Jens
我刚刚意识到我从未回答@jens的问题。这对于这个问题本身可能不再有用,但对于类似的事情可能有用。但是,如果API尚未支持该功能,则必须在“否则”中使用其他方法。在这种情况下,请使用这些答案中显示的任何其他方法。 - Nuno Gonçalves

17

使用UITabBarItembadgeColor属性可以在iOS 10及更高版本中原生支持更改标记颜色。有关该属性的更多信息,请参见苹果文档

示例:

  • Swift 3:myTab.badgeColor = UIColor.blue
  • Objective-C:[myTab setBadgeColor:[UIColor blueColor]];

1
如果([myTab respondsToSelector:@selector(setBadgeColor :)]]){          [myTab setBadgeColor:[UIColor blueColor]]; } - rjobidon
你能解释一下这个是如何实现的吗?我有点困惑。 - Austin Berenyi

12

我写了这段代码给我的应用程序,但是我只在iOS 7上测试过它。

for (UIView* tabBarButton in self.tabBar.subviews) {
    for (UIView* badgeView in tabBarButton.subviews) {
        NSString* className = NSStringFromClass([badgeView class]);

        // looking for _UIBadgeView
        if ([className rangeOfString:@"BadgeView"].location != NSNotFound) {
            for (UIView* badgeSubview in badgeView.subviews) {
                NSString* className = NSStringFromClass([badgeSubview class]);

                // looking for _UIBadgeBackground
                if ([className rangeOfString:@"BadgeBackground"].location != NSNotFound) {
                    @try {
                        [badgeSubview setValue:[UIImage imageNamed:@"YourCustomImage.png"] forKey:@"image"];
                    }
                    @catch (NSException *exception) {}
                }

                if ([badgeSubview isKindOfClass:[UILabel class]]) {
                    ((UILabel *)badgeSubview).textColor = [UIColor greenColor];
                }
            }
        }
    }
}

您只能使用图像而不是颜色更新徽章的背景。如果您想以某种方式更新徽章标签,我也暴露了徽章标签。

请注意,在设置tabBarItem.badgeValue之后必须调用此代码!

编辑:4/14/14

上述代码在任何地方调用都适用于iOS 7。要在iOS 7.1中使其工作,请在视图控制器的-viewWillLayoutSubviews中调用它。

编辑:12/22/14

这是一个我目前正在使用的更新片段。出于简单起见,我将代码放在了类别扩展中。

- (void)badgeViews:(void (^)(UIView* badgeView, UILabel* badgeLabel, UIView* badgeBackground))block {
    if (block) {
        for (UIView* tabBarButton in self.subviews) {
            for (UIView* badgeView in tabBarButton.subviews) {
                NSString* className = NSStringFromClass([badgeView class]);

                if ([className rangeOfString:@"BadgeView"].location != NSNotFound) {
                    UILabel* badgeLabel;
                    UIView* badgeBackground;

                    for (UIView* badgeSubview in badgeView.subviews) {
                        NSString* className = NSStringFromClass([badgeSubview class]);

                        if ([badgeSubview isKindOfClass:[UILabel class]]) {
                            badgeLabel = (UILabel *)badgeSubview;

                        } else if ([className rangeOfString:@"BadgeBackground"].location != NSNotFound) {
                            badgeBackground = badgeSubview;
                        }
                    }

                    block(badgeView, badgeLabel, badgeBackground);
                }
            }
        }
    }
}

然后当您准备好呼叫它时,它将如下所示。

[self.tabBar badgeViews:^(UIView *badgeView, UILabel *badgeLabel, UIView *badgeBackground) {}];

编辑:11/16/15

我注意到有些人需要更清晰地了解此代码发生的情况。for循环正在搜索一些不公开可访问的视图。通过检查视图类名是否包含预期名称的一部分,它确保到达预期的视图,同时不会引起Apple的任何可能的警报。一旦找到所有内容,将执行一个块,以便轻松访问这些视图。

值得注意的是,此代码在未来的iOS更新中可能会停止工作。例如,这些内部视图有一天可能会获得不同的类名。但是,这种可能性几乎为零,因为即使在内部,Apple 也很少重构到这种程度的类。即使他们这样做,它也将是UITabBarBadgeView之类的标题,仍然可以到达代码中预期的位置。考虑到iOS9已经推出,而且这段代码仍然按预期运行,您可以期待这个问题永远不会出现。


提供的解决方案适用于iOS 7,但不适用于iOS 7.1。是否有办法解决7.1的问题? - CrazyDeveloper
确保在您的视图控制器中调用 -viewWillLayoutSubviews 方法。 - cnotethegr8
7
楼主问:“是否可能”,并没有要求不会出现错误的实现。你给我投反对票的理由不足以支持你的行为。请您友善地撤销这个操作。 - cnotethegr8
1
建议:将 if (block) 条件放在方法的顶部:如果没有 block 参数,该方法无论如何都不会执行任何操作。 - Brock Boland
1
我来发表一下我的不请自来的意见。1)私有视图或视图层次结构并不是私有API。苹果公司从很久以前就一直在做出这种区分。2)苹果公司的指南对于SO答案来说是无关紧要的,除非问题明确需要一个符合苹果公司要求的答案。3)注明答案可能不可接受是个好主意,但这可能不值得一个踩。只需留下评论并继续前进。 - Avi
显示剩余12条评论

10

对于使用Swift的人,我改进了TimWhiting的答案,以便使徽章视图在任何屏幕大小和任何方向下工作。

 extension UITabBarController {

    func setBadges(badgeValues: [Int]) {

        for view in self.tabBar.subviews {
            if view is CustomTabBadge {
                view.removeFromSuperview()
            }
        }

        for index in 0...badgeValues.count-1 {
            if badgeValues[index] != 0 {
                addBadge(index, value: badgeValues[index], color:UIColor(paletteItem: .Accent), font: UIFont(name: Constants.ThemeApp.regularFontName, size: 11)!)
            }
        }
    }

    func addBadge(index: Int, value: Int, color: UIColor, font: UIFont) {
        let badgeView = CustomTabBadge()

        badgeView.clipsToBounds = true
        badgeView.textColor = UIColor.whiteColor()
        badgeView.textAlignment = .Center
        badgeView.font = font
        badgeView.text = String(value)
        badgeView.backgroundColor = color
        badgeView.tag = index
        tabBar.addSubview(badgeView)

        self.positionBadges()
    }

    override public func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        self.positionBadges()
    }

    // Positioning
    func positionBadges() {

        var tabbarButtons = self.tabBar.subviews.filter { (view: UIView) -> Bool in
            return view.userInteractionEnabled // only UITabBarButton are userInteractionEnabled
        }

        tabbarButtons = tabbarButtons.sort({ $0.frame.origin.x < $1.frame.origin.x })

        for view in self.tabBar.subviews {
            if view is CustomTabBadge {
                let badgeView = view as! CustomTabBadge
                self.positionBadge(badgeView, items:tabbarButtons, index: badgeView.tag)
            }
        }
    }

    func positionBadge(badgeView: UIView, items: [UIView], index: Int) {

        let itemView = items[index]
        let center = itemView.center

        let xOffset: CGFloat = 12
        let yOffset: CGFloat = -14
        badgeView.frame.size = CGSizeMake(17, 17)
        badgeView.center = CGPointMake(center.x + xOffset, center.y + yOffset)
        badgeView.layer.cornerRadius = badgeView.bounds.width/2
        tabBar.bringSubviewToFront(badgeView)
    }
}

class CustomTabBadge: UILabel {}

10

1
这会被苹果拒绝吗? - Phillip
1
不,我有一个在线应用程序使用这个库。 - eold
你正在访问私有属性。如果([str isEqualToString:@"_UIBadgeView"],为什么苹果不会拒绝它?权限改变了吗? - Mantas Laurinavičius
我几年前在一个项目中使用过它,而且苹果从未拒绝过它。 - eold

7

Swift 3 这是@Kirualex的答案(他在@TimWhiting的答案基础上进行了改进)的更新版本,适用于Swift 3。

extension UITabBarController {

    func setBadges(badgeValues: [Int]) {

        for view in self.tabBar.subviews {
            if view is CustomTabBadge {
                view.removeFromSuperview()
            }
        }

        for index in 0...badgeValues.count-1 {
            if badgeValues[index] != 0 {
                addBadge(index: index, value: badgeValues[index], color: UIColor.blue, font: UIFont(name: "Helvetica-Light", size: 11)!)
            }
        }
    }

    func addBadge(index: Int, value: Int, color: UIColor, font: UIFont) {
        let badgeView = CustomTabBadge()

        badgeView.clipsToBounds = true
        badgeView.textColor = UIColor.white
        badgeView.textAlignment = .center
        badgeView.font = font
        badgeView.text = String(value)
        badgeView.backgroundColor = color
        badgeView.tag = index
        tabBar.addSubview(badgeView)

        self.positionBadges()
    }

    override open func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        self.positionBadges()
    }

    // Positioning
    func positionBadges() {

        var tabbarButtons = self.tabBar.subviews.filter { (view: UIView) -> Bool in
            return view.isUserInteractionEnabled // only UITabBarButton are userInteractionEnabled
        }

        tabbarButtons = tabbarButtons.sorted(by: { $0.frame.origin.x < $1.frame.origin.x })

        for view in self.tabBar.subviews {
            if view is CustomTabBadge {
                let badgeView = view as! CustomTabBadge
                self.positionBadge(badgeView: badgeView, items:tabbarButtons, index: badgeView.tag)
            }
        }
    }

    func positionBadge(badgeView: UIView, items: [UIView], index: Int) {

        let itemView = items[index]
        let center = itemView.center

        let xOffset: CGFloat = 12
        let yOffset: CGFloat = -14
        badgeView.frame.size = CGSize(width: 17, height: 17)
        badgeView.center = CGPoint(x: center.x + xOffset, y: center.y + yOffset)
        badgeView.layer.cornerRadius = badgeView.bounds.width/2
        tabBar.bringSubview(toFront: badgeView)
    }
}

class CustomTabBadge: UILabel {}

7

不,你不能改变颜色,但是你可以使用自己的徽章。将此扩展添加到文件范围,并且您可以自定义徽章。只需在任何根视图控制器中调用self.tabBarController!.setBadges([1,0,2])

需要明确的是,这适用于具有三个项目的选项卡栏,徽章值从左到右。

extension UITabBarController {
    func setBadges(badgeValues:[Int]){

        var labelExistsForIndex = [Bool]()

        for value in badgeValues {
            labelExistsForIndex.append(false)
        }

        for view in self.tabBar.subviews {
            if view.isKindOfClass(PGTabBadge) {
                let badgeView = view as! PGTabBadge
                let index = badgeView.tag

                if badgeValues[index]==0 {
                    badgeView.removeFromSuperview()
                }

                labelExistsForIndex[index]=true
                badgeView.text = String(badgeValues[index])

            }
        }

        for var i=0;i<labelExistsForIndex.count;i++ {
            if labelExistsForIndex[i] == false {
                if badgeValues[i] > 0 {
                    addBadge(i, value: badgeValues[i], color:UIColor(red: 4/255, green: 110/255, blue: 188/255, alpha: 1), font: UIFont(name: "Helvetica-Light", size: 11)!)
                }
            }
        }


    }

    func addBadge(index:Int,value:Int, color:UIColor, font:UIFont){

        let itemPosition = CGFloat(index+1)
        let itemWidth:CGFloat = tabBar.frame.width / CGFloat(tabBar.items!.count)

        let bgColor = color

        let xOffset:CGFloat = 12
        let yOffset:CGFloat = -9

        var badgeView = PGTabBadge()
        badgeView.frame.size=CGSizeMake(17, 17)
        badgeView.center=CGPointMake((itemWidth * itemPosition)-(itemWidth/2)+xOffset, 20+yOffset)
        badgeView.layer.cornerRadius=badgeView.bounds.width/2
        badgeView.clipsToBounds=true
        badgeView.textColor=UIColor.whiteColor()
        badgeView.textAlignment = .Center
        badgeView.font = font
        badgeView.text = String(value)
        badgeView.backgroundColor = bgColor
        badgeView.tag=index
        tabBar.addSubview(badgeView)

    }
}

class PGTabBadge: UILabel {

}

4

看起来不行。您只能设置值。

根据苹果的文档,徽章是:

显示在物品右上角的红色椭圆形周围的文本。


3

iOS 15采用了不同的方法,以下是在我的情况下有效的方法:

let appearance = UITabBarAppearance()
appearance.configureWithTransparentBackground()
        
let barAppearance = UITabBarItemAppearance()
        
barAppearance.normal.badgeBackgroundColor = .green
barAppearance.normal.badgeTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.red]

appearance.stackedLayoutAppearance = barAppearance
     
tabBar.standardAppearance = appearance

2
请注意,除了 normal 状态之外,在 UITabBarItemAppearance 中还有选中(selected)和聚焦(focused)状态。 - Jason Moore

1
你需要指定要更改徽章颜色的选项卡项目索引,#在iOS 10中可用。
    if #available(iOS 10.0, *) 
    {
        self.kAppTabBarController.tabBar.items![1].badgeColor = YOUR_COLOR
    }

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