Home » Swift » Swift add badge to navigation barButtonItem and UIButton

Swift add badge to navigation barButtonItem and UIButton

Posted by: admin January 4, 2018 Leave a comment

Questions:

I am trying to display badge on my notification button, in app as displayed on AppIcon.

So far whatever i have researched is related to Obj. C, but nothing that specifically discussed way to implement that solution into Swift,

Please help to find a solution to add a custom class / code to achieve Badge on UiBarbutton and UiButton.

Researched so far:

https://github.com/Marxon13/M13BadgeView

along with MKBadge class etc.

Answers:

For UIBarButtonItem you can try this project:

ENMBadgeBarButtonItem

Questions:
Answers:

There is a more elegant solution with an extension for UIButtonItem

extension CAShapeLayer {
    func drawCircleAtLocation(location: CGPoint, withRadius radius: CGFloat, andColor color: UIColor, filled: Bool) {
        fillColor = filled ? color.cgColor : UIColor.white.cgColor
        strokeColor = color.cgColor
        let origin = CGPoint(x: location.x - radius, y: location.y - radius)
        path = UIBezierPath(ovalIn: CGRect(origin: origin, size: CGSize(width: radius * 2, height: radius * 2))).cgPath
    }
}

private var handle: UInt8 = 0

extension UIBarButtonItem {
    private var badgeLayer: CAShapeLayer? {
        if let b: AnyObject = objc_getAssociatedObject(self, &handle) as AnyObject? {
            return b as? CAShapeLayer
        } else {
            return nil
        }
    }

    func addBadge(number: Int, withOffset offset: CGPoint = CGPoint.zero, andColor color: UIColor = UIColor.red, andFilled filled: Bool = true) {
        guard let view = self.value(forKey: "view") as? UIView else { return }

        badgeLayer?.removeFromSuperlayer()

        // Initialize Badge
        let badge = CAShapeLayer()
        let radius = CGFloat(7)
        let location = CGPoint(x: view.frame.width - (radius + offset.x), y: (radius + offset.y))
        badge.drawCircleAtLocation(location: location, withRadius: radius, andColor: color, filled: filled)
        view.layer.addSublayer(badge)

        // Initialiaze Badge's label
        let label = CATextLayer()
        label.string = "\(number)"
        label.alignmentMode = kCAAlignmentCenter
        label.fontSize = 11
        label.frame = CGRect(origin: CGPoint(x: location.x - 4, y: offset.y), size: CGSize(width: 8, height: 16))
        label.foregroundColor = filled ? UIColor.white.cgColor : color.cgColor
        label.backgroundColor = UIColor.clear.cgColor
        label.contentsScale = UIScreen.main.scale
        badge.addSublayer(label)

        // Save Badge as UIBarButtonItem property
        objc_setAssociatedObject(self, &handle, badge, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }

    func updateBadge(number: Int) {
        if let text = badgeLayer?.sublayers?.filter({ $0 is CATextLayer }).first as? CATextLayer {
            text.string = "\(number)"
        }
    }

    func removeBadge() {
        badgeLayer?.removeFromSuperlayer()
    }
}

This great code was created by Stefano Vettor and you can find all the details at:
https://gist.github.com/freedom27/c709923b163e26405f62b799437243f4

Questions:
Answers:

using M13BadgeView.. use this code

(im using fontawesome.swift for buttons :: https://github.com/thii/FontAwesome.swift)

    let rightButton = UIButton(frame: CGRect(x:0,y:0,width:30,height:30))
    rightButton.titleLabel?.font = UIFont.fontAwesome(ofSize: 22)
    rightButton.setTitle(String.fontAwesomeIcon(name: .shoppingBasket), for: .normal)

    let rightButtonItem : UIBarButtonItem = UIBarButtonItem(customView: rightButton)

    let badgeView = M13BadgeView()
        badgeView.text = "1"
        badgeView.textColor = UIColor.white
        badgeView.badgeBackgroundColor = UIColor.red
        badgeView.borderWidth = 1.0
        badgeView.borderColor = UIColor.white
        badgeView.horizontalAlignment = M13BadgeViewHorizontalAlignmentLeft
        badgeView.verticalAlignment = M13BadgeViewVerticalAlignmentTop
        badgeView.hidesWhenZero = true

    rightButton.addSubview(badgeView)

    self.navigationItem.rightBarButtonItem = rightButtonItem

Questions:
Answers:

The MIBadgeButton-Swift is working also on UIBarButtonItems.
Here is my code after the navigation bar is created:

let rightBarButtons = self.navigationItem.rightBarButtonItems

let alarmsBarButton = rightBarButtons?.last

let alarmsButton = alarmsBarButton.customView as! MIBadgeButton?

alarmsButton.badgeString = "10"

Questions:
Answers:
  1. self.tabBarItem.badgeColor = .red
  2. storyboardOn storyboard
Questions:
Answers:

Good answer @Julio Bailon (https://stackoverflow.com/a/45948819/1898973)!

Here is the author’s site with full explanation: http://www.stefanovettor.com/2016/04/30/adding-badge-uibarbuttonitem/.

It seems not to be working on iOS 11, maybe because the script try to access the “view” property of the UIBarButtonItem. I made it work:

1) By creating a UIButton and then creating the UIBarButtonItem using the UIButton as a customView:

navigationItem.rightBarButtonItem = UIBarButtonItem.init(customView: shoppingCartButton)

2) By replacing the line in the UIBarButtonItem extension:

guard let view = self.value(forKey: "view") as? UIView else { return }

with the following:

guard let view = self.customView else { return }

Seems elegant to me and, best of all, it worked!