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.
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 = CATextLayerAlignmentMode.center 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
Working Solution :
Step 1: Firstly create new swift file which is a subclass to UIButton as follows:
import UIKit class BadgeButton: UIButton { var badgeLabel = UILabel() var badge: String? { didSet { addbadgetobutton(badge: badge) } } public var badgeBackgroundColor = UIColor.red { didSet { badgeLabel.backgroundColor = badgeBackgroundColor } } public var badgeTextColor = UIColor.white { didSet { badgeLabel.textColor = badgeTextColor } } public var badgeFont = UIFont.systemFont(ofSize: 12.0) { didSet { badgeLabel.font = badgeFont } } public var badgeEdgeInsets: UIEdgeInsets? { didSet { addbadgetobutton(badge: badge) } } override init(frame: CGRect) { super.init(frame: frame) addbadgetobutton(badge: nil) } func addbadgetobutton(badge: String?) { badgeLabel.text = badge badgeLabel.textColor = badgeTextColor badgeLabel.backgroundColor = badgeBackgroundColor badgeLabel.font = badgeFont badgeLabel.sizeToFit() badgeLabel.textAlignment = .center let badgeSize = badgeLabel.frame.size let height = max(18, Double(badgeSize.height) + 5.0) let width = max(height, Double(badgeSize.width) + 10.0) var vertical: Double?, horizontal: Double? if let badgeInset = self.badgeEdgeInsets { vertical = Double(badgeInset.top) - Double(badgeInset.bottom) horizontal = Double(badgeInset.left) - Double(badgeInset.right) let x = (Double(bounds.size.width) - 10 + horizontal!) let y = -(Double(badgeSize.height) / 2) - 10 + vertical! badgeLabel.frame = CGRect(x: x, y: y, width: width, height: height) } else { let x = self.frame.width - CGFloat((width / 2.0)) let y = CGFloat(-(height / 2.0)) badgeLabel.frame = CGRect(x: x, y: y, width: CGFloat(width), height: CGFloat(height)) } badgeLabel.layer.cornerRadius = badgeLabel.frame.height/2 badgeLabel.layer.masksToBounds = true addSubview(badgeLabel) badgeLabel.isHidden = badge != nil ? false : true } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.addbadgetobutton(badge: nil) fatalError("init(coder:) is not implemented") } }
Step 2: Create a function in your base file which u can use in each View Controller :
func addBadge(itemvalue: String) { let bagButton = BadgeButton() bagButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44) bagButton.tintColor = UIColor.darkGray bagButton.setImage(UIImage(named: "ShoppingBag")?.withRenderingMode(.alwaysTemplate), for: .normal) bagButton.badgeEdgeInsets = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 15) bagButton.badge = itemvalue self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: bagButton) }
Step 3 : Use above function from any View Controller in this way :
self.addBadge(itemvalue: localStorage.string(forKey: "total_products_in_cart") ?? "0")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With