-
-
Save freedom27/c709923b163e26405f62b799437243f4 to your computer and use it in GitHub Desktop.
I needed same thing for a regular UIButton,
extension UIButton
{
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 , addedView:UIView?) {
guard let view = addedView 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)"
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 UIButtonItem 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()
}
}
then wherever
override func viewDidLoad() {
super.viewDidLoad()
button.tag = tagNumber
}
then
if let foundView = self.button.superView.viewWithTag(tagNumber) {
self.button.addBadge(number: 2, addedView: foundView)
}
Hi all,
after doing this in viewDidLoad (and adding the extension code), i can see the bar button item in the navigation bar but not the badge :(
let inboxButton:UIBarButtonItem = UIBarButtonItem(image: UIImage(named: "inbox"), style: .plain, target: self, action: #selector(presentInbox))
let point = CGPoint(x: 0, y: 0)
inboxButton.addBadge(number: 2, withOffset: point, andColor: UIColor.black, andFilled: false)
self.navigationItem.leftBarButtonItem = inboxButton
Has someone a simple working example? Thank you very much Guys :)
https://gist.github.com/mohn93/bd950eeed82749b2ecad40ba3d4d0f1d
This fork has more features, hope it helps
Swift 4 version of the same
import UIKit
extension CAShapeLayer {
func drawRoundedRect(rect: CGRect, andColor color: UIColor, filled: Bool) {
fillColor = filled ? color.cgColor : UIColor.white.cgColor
strokeColor = color.cgColor
path = UIBezierPath(roundedRect: rect, cornerRadius: 7).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 setBadge(text: String?, withOffsetFromTopRight offset: CGPoint = CGPoint.zero, andColor color:UIColor = UIColor.red, andFilled filled: Bool = true, andFontSize fontSize: CGFloat = 11)
{
badgeLayer?.removeFromSuperlayer()
if (text == nil || text == "") {
return
}
addBadge(text: text!, withOffset: offset, andColor: color, andFilled: filled)
}
private func addBadge(text: String, withOffset offset: CGPoint = CGPoint.zero, andColor color: UIColor = UIColor.red, andFilled filled: Bool = true, andFontSize fontSize: CGFloat = 11)
{
guard let view = self.value(forKey: "view") as? UIView else { return }
var font = UIFont.systemFont(ofSize: fontSize)
if #available(iOS 9.0, *) {
font = UIFont.monospacedDigitSystemFont(ofSize: fontSize, weight: UIFont.Weight.regular)
}
let badgeSize = text.size(withAttributes: [NSAttributedStringKey.font: font])
// Initialize Badge
let badge = CAShapeLayer()
let height = badgeSize.height;
var width = badgeSize.width + 2 /* padding */
//make sure we have at least a circle
if (width < height) {
width = height
}
//x position is offset from right-hand side
let x = view.frame.width - width + offset.x
let badgeFrame = CGRect(origin: CGPoint(x: x, y: offset.y), size: CGSize(width: width, height: height))
badge.drawRoundedRect(rect: badgeFrame, andColor: color, filled: filled)
view.layer.addSublayer(badge)
// Initialiaze Badge's label
let label = CATextLayer()
label.string = text
label.alignmentMode = kCAAlignmentCenter
label.font = font
label.fontSize = font.pointSize
label.frame = badgeFrame
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)
}
private func removeBadge() {
badgeLayer?.removeFromSuperlayer()
}
}
Has anyone been able to use this with xcode 9?
This code does not work anymore:
guard let view = self.value(forKey: "view") as? UIView else { return }
works for me 9.0.1
** #guard let view = self.value(forKey: "view") as? UIView else { return }** here view is getting value nil because of which it does not show badge how do i fix this?
@zeeip6, I faced the same issue. It guess the name of the key for view might be changed to something else and because of this its returning nil. The workaround I did for that was to use customView as below
guard let view = self.customView else { return }
and initialize a BarButtonItem by giving an UIButton as its custom view.
let bellButton = UIButton(type: .custom)
bellButton.setBackgroundImage(UIImage(named:"notification"), for: .normal)
bellButton.addTarget(self, action: #selector(didClickNotificationButton(_:)), for: .touchUpInside)
let notificationBarButton = UIBarButtonItem(customView: bellButton)
Note: Offset needs to be adjusted as required when adding a badge.
@febinfathah code is working, but in my case badge was a truncated a bit from the top and I had to add an offset, not a big deal. But then I realized that badge is drawing behind icon, which is unacceptable. I fixed it by adding the following line on the bottom off "addBadge":
badge.zPosition = 1000
So, the new code is:
import UIKit
extension CAShapeLayer {
func drawRoundedRect(rect: CGRect, andColor color: UIColor, filled: Bool) {
fillColor = filled ? color.cgColor : UIColor.white.cgColor
strokeColor = color.cgColor
path = UIBezierPath(roundedRect: rect, cornerRadius: 7).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 setBadge(text: String?, withOffsetFromTopRight offset: CGPoint = CGPoint.zero, andColor color:UIColor = UIColor.red, andFilled filled: Bool = true, andFontSize fontSize: CGFloat = 11) {
badgeLayer?.removeFromSuperlayer()
if (text == nil || text == "") {
return
}
addBadge(text: text!, withOffset: offset, andColor: color, andFilled: filled)
}
private func addBadge(text: String, withOffset offset: CGPoint = CGPoint.zero, andColor color: UIColor = UIColor.red, andFilled filled: Bool = true, andFontSize fontSize: CGFloat = 11) {
guard let view = self.value(forKey: "view") as? UIView else { return }
var font = UIFont.systemFont(ofSize: fontSize)
if #available(iOS 9.0, *) {
font = UIFont.monospacedDigitSystemFont(ofSize: fontSize, weight: UIFont.Weight.regular)
}
let badgeSize = text.size(withAttributes: [NSAttributedStringKey.font: font])
//initialize Badge
let badge = CAShapeLayer()
let height = badgeSize.height;
var width = badgeSize.width + 2 /* padding */
//make sure we have at least a circle
if (width < height) {
width = height
}
//x position is offset from right-hand side
let x = view.frame.width - width + offset.x
let badgeFrame = CGRect(origin: CGPoint(x: x, y: offset.y), size: CGSize(width: width, height: height))
badge.drawRoundedRect(rect: badgeFrame, andColor: color, filled: filled)
view.layer.addSublayer(badge)
//initialiaze Badge's label
let label = CATextLayer()
label.string = text
label.alignmentMode = kCAAlignmentCenter
label.font = font
label.fontSize = font.pointSize
label.frame = badgeFrame
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)
//bring layer to front
badge.zPosition = 1000
}
private func removeBadge() {
badgeLayer?.removeFromSuperlayer()
}
}
Tested on both iPhone and iPad with iOS 11.1. Maintains its appearance after changing orientation as well.
Hello guys, I was trying to implement this using Xamarin but I always stop here because the view returned is always null. Am I missing out something or do I not understand how swift works? Here's where the error is and my c# implementation of that same code.
Swift
guard let view = self.value(forKey: "view") as? UIView else { return }
C#
var view = this.ValueForKey((Foundation.NSString)"view") as UIView;
if (view==null) return;
Hi,
Any of you have solved this.
guard let view = self.value(forKey: "view") as? UIView else { return }
This always returns a null value (Swift 3).
thank you.
guard let view = self.value(forKey: "view") as? UIView else { return }
This code will always return nil, if you set the badge in the wrong spot.
So if you want to display the badge, you would have to set the badge outside of viewDidLoad and you should see the badge. It seems setting the badge in viewDidAppear works for me there.
If you have been setting the badge in viewDidLoad, in iOS11 it handles it differently. Any related UI work will break or wont work. Try setting the badge outside of viewDidLoad and see if that works.
I hope this helps
As others have mentioned the view will be nil if you set the badge on viewDidLoad on iOS 11 and Xcode 9. To solve this just set the badge to the bar button Item in the " viewDidAppear" function.
Thank you all for the solution, neat and concise.
Instead of global handle var, it is better to use static struct, IMHO
private struct AssociatedKey {
static var badgeLayer = "badgeLayer"
}
fileprivate var badgeLayer: CAShapeLayer? {
get {
return objc_getAssociatedObject(self, &AssociatedKey.badgeLayer) as? CAShapeLayer
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKey.badgeLayer,
newValue as CAShapeLayer?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
Does anyone know why this doesn't work with back button?
Its not working in Swift4.
Different approach and works with Swift 4 and various view controller states: https://gist.github.com/yonat/75a0f432d791165b1fd6
I have used this for tabItem but I want to show the layer on top of tabImage but layer is added behind it. Any way through which it can be attained?
This doesn't work unless it is a custom uibarbutton unforunately. :(
I have used this for tabItem but I want to show the layer on top of tabImage but layer is added behind it. Any way through which it can be attained?
u can get it by the property "badgeLayer"
This doesn't work unless it is a custom uibarbutton unforunately. :(
u can extension a button to do it , c the #ugenlik comment
swift 4.2
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)"
label.alignmentMode = CATextLayerAlignmentMode.center
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()
}
}
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)" label.alignmentMode = CATextLayerAlignmentMode.center 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() }
}
This is not working.
ok this code is not work for me:
guard let view = self.valueForKey("view") as? UIView else { return }
It always returns nil.
This is my code (Xcode 10, swift 5):
`private lazy var btnNoti: UIBarButtonItem = {
return EBarButtonItem.notification.create(target: self, action: #selector(btnBell_Touched))
}()
self.navigationItem.setLeftBarButton(btnNoti, animated: true)
btnNoti.addBadge(number:10)
`
Update to Swift 5 + fix SwiftLint warnings:
import UIKit
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 setBadge(text: String?, offset: CGPoint = .zero, color: UIColor = .red, filled: Bool = true, fontSize: CGFloat = 11.0) {
badgeLayer?.removeFromSuperlayer()
guard let text = text, !text.isEmpty else {
return
}
addBadge(text: text, offset: offset, color: color, filled: filled)
}
private func addBadge(text: String, offset: CGPoint = .zero, color: UIColor = .red, filled: Bool = true, fontSize: CGFloat = 11) {
guard let view = self.value(forKey: "view") as? UIView else {
return
}
var font = UIFont.systemFont(ofSize: fontSize)
if #available(iOS 9.0, *) {
font = UIFont.monospacedDigitSystemFont(ofSize: fontSize, weight: .regular)
}
let badgeSize = text.size(withAttributes: [.font: font])
// initialize Badge
let badge = CAShapeLayer()
let height = badgeSize.height
var width = badgeSize.width + 2 // padding
// make sure we have at least a circle
if width < height {
width = height
}
// x position is offset from right-hand side
let x = view.frame.width - width + offset.x
let badgeFrame = CGRect(origin: CGPoint(x: x, y: offset.y), size: CGSize(width: width, height: height))
badge.drawRoundedRect(rect: badgeFrame, andColor: color, filled: filled)
view.layer.addSublayer(badge)
// initialiaze Badge's label
let label = CATextLayer()
label.string = text
label.alignmentMode = .center
label.font = font
label.fontSize = font.pointSize
label.frame = badgeFrame
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)
// bring layer to front
badge.zPosition = 1_000
}
private func removeBadge() {
badgeLayer?.removeFromSuperlayer()
}
}
// MARK: - Utilities
extension CAShapeLayer {
func drawRoundedRect(rect: CGRect, andColor color: UIColor, filled: Bool) {
fillColor = filled ? color.cgColor : UIColor.white.cgColor
strokeColor = color.cgColor
path = UIBezierPath(roundedRect: rect, cornerRadius: 7).cgPath
}
}
Working in swift 5, same code of @adam-leitgeb , but i only wanted the red dot, without the number, so if anyone is looking for the same, i leave the code here 👍 .
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 setBadge(offset: CGPoint = .zero, color: UIColor = .red, filled: Bool = true, fontSize: CGFloat = 11) {
badgeLayer?.removeFromSuperlayer()
guard let view = self.value(forKey: "view") as? UIView else {
return
}
var font = UIFont.systemFont(ofSize: fontSize)
if #available(iOS 9.0, *) {
font = UIFont.monospacedDigitSystemFont(ofSize: fontSize, weight: .regular)
}
//Size of the dot
let badgeSize = UILabel(frame: CGRect(x: 22, y: -05, width: 10, height: 10))
// initialize Badge
let badge = CAShapeLayer()
let height = badgeSize.height
let width = badgeSize.width
// x position is offset from right-hand side
let x = view.frame.width - width + offset.x
// I suggest you try the x and y sets, for my case, i will use this coordinates for better result,
// but depends on the syze of your image
// let x = view.frame.width + offset.x - 17
// let y = view.frame.height + offset.y - 34
let badgeFrame = CGRect(origin: CGPoint(x: x, y: offset.y), size: CGSize(width: width, height: height))
badge.drawRoundedRect(rect: badgeFrame, andColor: color, filled: filled)
view.layer.addSublayer(badge)
// initialiaze Badge's label
let label = CATextLayer()
label.alignmentMode = .center
label.font = font
label.fontSize = font.pointSize
label.frame = badgeFrame
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)
// bring layer to front
badge.zPosition = 1_000
}
private func removeBadge() {
badgeLayer?.removeFromSuperlayer()
}
}
// MARK: - Utilities
extension CAShapeLayer {
func drawRoundedRect(rect: CGRect, andColor color: UIColor, filled: Bool) {
fillColor = filled ? color.cgColor : UIColor.white.cgColor
strokeColor = color.cgColor
path = UIBezierPath(roundedRect: rect, cornerRadius: 7).cgPath
}
}
I made the extension more generic so to work on any old NSObject aswel as UIViews.
A new class can confirm by implementing the var badgeParent variable.
Also added some standardisation to the API;
NOTE: remove default parameters from initial functions!
Be wary of autolayout update cycles with the view since it uses relative positioning with points
usage:
let badgeView = UIView(frame: .zero).addBrandedBadge(text: "1", size: .small, color: .regular)
private var handle: UInt8 = 0
public protocol Badge {
var badgeParentView : UIView? { get }
}
extension UIBarButtonItem : Badge {
public var badgeParentView: UIView? {
self.value(forKey: "view") as? UIView
}
}
extension UIView : Badge {
public var badgeParentView: UIView? { self }
}
public extension Badge {
private var badgeLayer: CAShapeLayer? {
if let b: AnyObject = objc_getAssociatedObject(self, &handle) as AnyObject? {
return b as? CAShapeLayer
} else {
return nil
}
}
public func setBadge(text: String?, offset: CGPoint , color: UIColor, borderColor: UIColor, borderWidth: CGFloat, textColor: UIColor, filled: Bool = true, fontSize: CGFloat) {
badgeLayer?.removeFromSuperlayer()
guard let text = text, !text.isEmpty else {
return
}
addBadge(text: text, offset: offset, color: color,borderColor: borderColor, borderWidth: borderWidth, textColor: textColor, filled: filled, fontSize: fontSize)
}
private func addBadge(text: String, offset: CGPoint , color: UIColor, borderColor: UIColor, borderWidth: CGFloat, textColor: UIColor, filled: Bool = true, fontSize: CGFloat) {
guard let view = badgeParentView else { return }
var font = UIFont.systemFont(ofSize: fontSize)
if #available(iOS 9.0, *) {
font = UIFont.monospacedDigitSystemFont(ofSize: fontSize, weight: .regular)
}
let badgeSize = text.size(withAttributes: [.font: font])
// initialize Badge
let badge = CAShapeLayer()
let height = badgeSize.height
var width = badgeSize.width + 2 // padding
// make sure we have at least a circle
if width < height {
width = height
}
// x position is offset from right-hand side
let x = view.frame.width - width + offset.x
let badgeFrame = CGRect(origin: CGPoint(x: x, y: offset.y), size: CGSize(width: width, height: height))
badge.drawRoundedRect(rect: badgeFrame, andColor: color, filled: filled)
view.layer.addSublayer(badge)
// initialiaze Badge's label
let label = CATextLayer()
label.string = text
label.alignmentMode = .center
label.font = font
label.fontSize = font.pointSize
label.foregroundColor = textColor.cgColor
label.borderWidth = borderWidth
label.borderColor = borderColor.cgColor
label.frame = badgeFrame
label.cornerRadius = label.frame.height / 2
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)
// bring layer to front
badge.zPosition = 1_000
}
public func removeBadge() {
badgeLayer?.removeFromSuperlayer()
}
}
public extension Badge {
@discardableResult
func addBrandedBadge(text: String, size: BadgeSizeVariant, color: BadgeColorVariant) -> Self {
self.setBadge(text: text, offset: size.offSet, color: color.backroundColor, borderColor: color.borderColor, borderWidth: size.borderWidth, textColor: color.textColor, filled: true, fontSize: size.fontSize)
return self
}
}
public struct BadgeSizeVariant {
public let borderWidth: CGFloat
public let fontSize : CGFloat
public let offSet : CGPoint
public init(borderWidth: CGFloat, fontSize: CGFloat, offSet: CGPoint) {
self.borderWidth = borderWidth
self.fontSize = fontSize
self.offSet = offSet
}
}
public extension BadgeSizeVariant {
static var small = Self(borderWidth: 1, fontSize: 12, offSet: CGPoint(x: -8, y: 4))
static var medium = Self(borderWidth: 1.5, fontSize: 16, offSet: CGPoint(x: -4, y: 2))
static var large = Self(borderWidth: 3, fontSize: 24, offSet: CGPoint(x: 8, y: -8))
}
public struct BadgeColorVariant {
public let borderColor : UIColor
public let backroundColor : UIColor
public let textColor : UIColor
public init(border: UIColor, background: UIColor, text: UIColor) {
self.borderColor = border
self.backroundColor = background
self.textColor = text
}
}
public extension BadgeColorVariant {
static var regular = Self(border: .white, background: .red, text: .white)
static var success = Self(border: .white, background: .green, text: .white)
}
// MARK: - Utilities
public extension CAShapeLayer {
public func drawRoundedRect(rect: CGRect, andColor color: UIColor, filled: Bool) {
fillColor = filled ? color.cgColor : UIColor.white.cgColor
strokeColor = color.cgColor
path = UIBezierPath(roundedRect: rect, cornerRadius: 7).cgPath
}
}
@AP-94 love u dude, but im not gay 😎
thanks its work fine.