How to create notification badge in tableview

1k views Asked by At

How to make a notification badge on the table view?
If there is new data it will display the badge on the table view, and if there is new data again, the badge will automatically be added.

enter image description here

1

There are 1 answers

0
David Seek On

I can strongly recommend you use the following Framework: swift-badge

use_frameworks!
target 'Your target name'
pod 'BadgeSwift', '~> 4.0'

Easy to use:

let badge = BadgeSwift()
view.addSubview(badge)
// Position the badge ...

Customization:

// Text
badge.text = "2"

// Insets
badge.insets = CGSize(width: 12, height: 12)

// Font
badge.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)

// Text color
badge.textColor = UIColor.yellow

// Badge color
badge.badgeColor = UIColor.black

// Shadow
badge.shadowOpacityBadge = 0.5
badge.shadowOffsetBadge = CGSize(width: 0, height: 0)
badge.shadowRadiusBadge = 1.0
badge.shadowColorBadge = UIColor.black

// No shadow
badge.shadowOpacityBadge = 0

// Border width and color
badge.borderWidth = 5.0
badge.borderColor = UIColor.magenta

// Customize the badge corner radius.
// -1 if unspecified. When unspecified, the corner is fully rounded. Default: -1.
badge.cornerRadius = 10

enter image description here

Also. If you don't want to use pods, here is the full and ready to use class It also includes functions I have created:

import UIKit

/**

 Badge view control for iOS and tvOS.

 Project home: https://github.com/marketplacer/swift-badge

 */
@IBDesignable public class BadgeSwift: UILabel {

    /// Background color of the badge
    @IBInspectable public var badgeColor = Colors.red {
        didSet {
            setNeedsDisplay()
        }
    }

    /// Width of the badge border
    @IBInspectable public var borderWidth: CGFloat = 0 {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    /// Color of the bardge border
    @IBInspectable public var borderColor = Colors.white {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    /// Badge insets that describe the margin between text and the edge of the badge.
    @IBInspectable public var insets: CGSize = CGSize(width: 5, height: 2) {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    // MARK: Badge shadow
    // --------------------------

    /// Opacity of the badge shadow
    @IBInspectable public var shadowOpacityBadge: CGFloat = 0.5 {
        didSet {
            layer.shadowOpacity = Float(shadowOpacityBadge)
            setNeedsDisplay()
        }
    }

    /// Size of the badge shadow
    @IBInspectable public var shadowRadiusBadge: CGFloat = 0.5 {
        didSet {
            layer.shadowRadius = shadowRadiusBadge
            setNeedsDisplay()
        }
    }

    /// Color of the badge shadow
    @IBInspectable public var shadowColorBadge = Colors.black {
        didSet {
            layer.shadowColor = shadowColorBadge.cgColor
            setNeedsDisplay()
        }
    }

    /// Offset of the badge shadow
    @IBInspectable public var shadowOffsetBadge: CGSize = CGSize(width: 0, height: 0) {
        didSet {
            layer.shadowOffset = shadowOffsetBadge
            setNeedsDisplay()
        }
    }

    /// Initialize the badge view
    convenience public init() {
        self.init(frame: CGRect())
    }

    /// Initialize the badge view
    override public init(frame: CGRect) {
        super.init(frame: frame)

        setup()
    }

    /// Initialize the badge view
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        setup()
    }

    /// Add custom insets around the text
    override public func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
        let rect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)

        var insetsWithBorder = actualInsetsWithBorder()
        let rectWithDefaultInsets = rect.insetBy(dx: -insetsWithBorder.width, dy: -insetsWithBorder.height)

        // If width is less than height
        // Adjust the width insets to make it look round
        if rectWithDefaultInsets.width < rectWithDefaultInsets.height {
            insetsWithBorder.width = (rectWithDefaultInsets.height - rect.width) / 2
        }
        let result = rect.insetBy(dx: -insetsWithBorder.width, dy: -insetsWithBorder.height)

        return result
    }

    /// Draws the label with insets
    override public func drawText(in rect: CGRect) {
        layer.cornerRadius = rect.height / 2

        let insetsWithBorder = actualInsetsWithBorder()
        let insets = UIEdgeInsets(
            top: insetsWithBorder.height,
            left: insetsWithBorder.width,
            bottom: insetsWithBorder.height,
            right: insetsWithBorder.width)

        let rectWithoutInsets = UIEdgeInsetsInsetRect(rect, insets)

        super.drawText(in: rectWithoutInsets)
    }

    /// Draw the background of the badge
    override public func draw(_ rect: CGRect) {
        let rectInset = rect.insetBy(dx: borderWidth/2, dy: borderWidth/2)
        let path = UIBezierPath(roundedRect: rectInset, cornerRadius: rect.height/2)

        badgeColor.setFill()
        path.fill()

        if borderWidth > 0 {
            borderColor.setStroke()
            path.lineWidth = borderWidth
            path.stroke()
        }

        super.draw(rect)
    }

    private func setup() {
        textAlignment = NSTextAlignment.center
        clipsToBounds = false // Allows shadow to spread beyond the bounds of the badge
    }

    /// Size of the insets plus the border
    private func actualInsetsWithBorder() -> CGSize {
        return CGSize(
            width: insets.width + borderWidth,
            height: insets.height + borderWidth
        )
    }

    /// Draw the stars in interface builder
    @available(iOS 8.0, *)
    override public func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()

        setup()
        setNeedsDisplay()
    }
}

func createBadge(_ who: BadgeSwift, _ view: UIView, _ value: String) {
    if !view.subviews.contains(who) {
        view.addSubview(who)
    }
    configureBadge(who, view, value)
    positionBadge(who, view)
}

func removeBadge(_ who: BadgeSwift, _ view: UIView) {
    if view.subviews.contains(who) {
       who.removeFromSuperview()
    }
}

func configureBadge(_ badge: BadgeSwift, _ view: UIView, _ value: String) {
    badge.text = value
    badge.insets = CGSize(width: 2, height: 2)
    badge.font = UIFont.boldSystemFont(ofSize: 12)
    badge.textColor = Colors.white
    badge.badgeColor = Colors.badgeRed
}

func positionBadge(_ badge: UIView, _ view: UIView) {
    badge.translatesAutoresizingMaskIntoConstraints = false
    var constraints = [NSLayoutConstraint]()

    constraints.append(NSLayoutConstraint(
        item: badge,
        attribute: NSLayoutAttribute.centerY,
        relatedBy: NSLayoutRelation.equal,
        toItem: view,
        attribute: NSLayoutAttribute.top,
        multiplier: 1, constant: 5)
    )

    constraints.append(NSLayoutConstraint(
        item: badge,
        attribute: NSLayoutAttribute.centerX,
        relatedBy: NSLayoutRelation.equal,
        toItem: view,
        attribute: NSLayoutAttribute.right,
        multiplier: 1, constant: -5)
    )
    view.addConstraints(constraints)
}

func calculateCount(_ items: [UITabBarItem]) -> String {
    var countInt = 0
    for i in items {
        if let countString = i.badgeValue {
            countInt = countInt + Int(countString)!
        }
    }
    return String(countInt)
}

For your purpose to create a Badge inside a UITableViewCell, you could use:

let badge = BadgeSwift()
createBadge(badge, MyCell, "10")

That would give you a Badge of 10.