Skip to content

Instantly share code, notes, and snippets.

@k-angama
Created May 25, 2025 17:04
Show Gist options
  • Save k-angama/1c10f914d2308c73677f253249a3b95d to your computer and use it in GitHub Desktop.
Save k-angama/1c10f914d2308c73677f253249a3b95d to your computer and use it in GitHub Desktop.
BadgePosition.swift
import UIKit
enum BadgePosition {
case topRight
case topLeft
case bottomRight
case bottomLeft
case center
}
class BadgeLabel: UILabel {
private let padding: CGFloat = 6.0
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
backgroundColor = .red
textColor = .white
font = UIFont.systemFont(ofSize: 12, weight: .bold)
textAlignment = .center
clipsToBounds = true
isHidden = true
translatesAutoresizingMaskIntoConstraints = false
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.height / 2
}
override var intrinsicContentSize: CGSize {
let labelSize = super.intrinsicContentSize
let dimension = max(labelSize.width, labelSize.height) + padding
return CGSize(width: dimension, height: dimension)
}
func updateBadge(number: Int) {
if number <= 0 {
isHidden = true
} else {
isHidden = false
text = "\(number)"
invalidateIntrinsicContentSize()
}
}
func position(_ position: BadgePosition, _ padding: CGFloat) {
guard let view = superview else { return }
NSLayoutConstraint.deactivate(view.constraints.filter {
$0.firstItem === self || $0.secondItem === self
})
switch position {
case .topRight:
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: view.topAnchor, constant: padding),
trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding)
])
case .topLeft:
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: view.topAnchor, constant: padding),
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding)
])
case .bottomRight:
NSLayoutConstraint.activate([
bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -padding),
trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding)
])
case .bottomLeft:
NSLayoutConstraint.activate([
bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -padding),
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding)
])
case .center:
NSLayoutConstraint.activate([
centerXAnchor.constraint(equalTo: view.centerXAnchor),
centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
}
private var badgeKey: UInt8 = 0
extension UIView {
private var badge: BadgeLabel? {
get {
return objc_getAssociatedObject(self, &badgeKey) as? BadgeLabel
}
set {
objc_setAssociatedObject(self, &badgeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
func badge(number: Int, position: BadgePosition, padding: CGFloat = 0) {
if badge == nil {
let badgeLabel = BadgeLabel()
addSubview(badgeLabel)
badge = badgeLabel
}
badge?.updateBadge(number: number)
badge?.position(position, padding)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment