UIBarButtonItem Badge

I have been doing some research on adding a badge to a UIBarButton and found the following code. I have been unable to make it work so I am looking for help.

//This is the code I am using to call the func
itemButton.addBadge(number: 4)

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()
    
    var badgeWidth = 8
    var numberOffset = 4
    
    if number > 9 {
        badgeWidth = 12
        numberOffset = 6
    }
    
    // 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)"
    print("I AM THE STRING DINGALING-----------------------------------------",label.string!)
    label.alignmentMode = kCAAlignmentCenter
    label.fontSize = 11
    label.frame = CGRect(origin: CGPoint(x: location.x - CGFloat(numberOffset), y: offset.y), size: CGSize(width: badgeWidth, 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()
}

}

Hi @jport1130

I have done like below in one of my app :

First create common method for setting bar button.

func setTwoRightBarButtonWithTarget(_ target: AnyObject,action:Selector, imageName:String,action1:Selector, imageName1:String, BadgeCount : Int) -> [UIBarButtonItem] {

var arrBarItems : [UIBarButtonItem] = [UIBarButtonItem()]
let rightBtn = UIButton(type: UIButtonType.custom)
rightBtn.backgroundColor = UIColor.clear
rightBtn.frame = CGRect(x: 0, y: 0, width: 26, height: 26)
rightBtn.setImage(UIImage(named:imageName), for:UIControlState())
rightBtn.addTarget(target, action: action, for: UIControlEvents.touchUpInside)

let negativeSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
negativeSpace.width = -5

// badge label
if  BadgeCount > 0 {
    let label = UILabel(frame: CGRect(x: 10, y: -7, width: 18, height: 18))
    label.layer.borderColor = UIColor.clear.cgColor
    label.layer.borderWidth = 2
    label.layer.cornerRadius = label.bounds.size.height / 2
    label.textAlignment = .center
    label.layer.masksToBounds = true
    label.font = UIFont.systemFont(ofSize: 10.0)
    label.textColor = .white
    label.backgroundColor = .red
    label.adjustsFontSizeToFitWidth = true
    label.text = "\(BadgeCount)"
    rightBtn.addSubview(label)
}
let barButton = UIBarButtonItem(customView:rightBtn)

let rightBtn1 = UIButton(type: UIButtonType.custom)
rightBtn1.backgroundColor = UIColor.clear
rightBtn1.frame = CGRect(x: UIScreen.main.bounds.width - 30, y: 0, width: 26, height: 26)
rightBtn1.setImage(UIImage(named:imageName1), for:UIControlState())
rightBtn1.addTarget(target, action: action1, for: UIControlEvents.touchUpInside)
let barButton1 = UIBarButtonItem(customView:rightBtn1)

arrBarItems = [barButton, barButton1]
return arrBarItems
}

After that called this method whenever you need :

class HomeViewController: UIViewController {

     override func viewDidLoad() {
         super.viewDidLoad()
         self.navigationItem.leftBarButtonItems = setLeftButtonWithTarget(self, action: #selector(btnBackClicked(_:)), imageName: "back_arrow")
         self.navigationItem.rightBarButtonItems = setTwoRightBarButtonWithTarget(self, action: #selector(btnNotificationClicked(_:)), imageName: "notification", action1: #selector(btnCalendarClicked(_:)), imageName1: "calendar_top_icon", BadgeCount: UIApplication.shared.applicationIconBadgeNumber)

        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

Update badge value whenever you need :

    @objc func methodOfReceivedNotification(notification: NSNotification) {
        //Take Action on Notification
         self.navigationItem.rightBarButtonItems = setTwoRightBarButtonWithTarget(self, action: #selector(btnNotificationClicked(_:)), imageName: "notification", action1: #selector(btnCalendarClicked(_:)), imageName1: "calendar_top_icon", BadgeCount: UIApplication.shared.applicationIconBadgeNumber+1)
    }

Same way when you read all notification, then decrease badge count.

This topic was automatically closed after 166 days. New replies are no longer allowed.