web-dev-qa-db-fra.com

Swift ajoute un badge à la barre de navigationButtonItem et UIButton

J'essaie d'afficher un badge sur mon bouton de notification, dans l'application telle qu'elle est affichée sur AppIcon. 

Jusqu'à présent, tout ce que j'ai étudié est lié à Obj. C, mais rien qui ait spécifiquement discuté de la manière de mettre en œuvre cette solution dans Swift, 

Aidez-nous à trouver une solution pour ajouter une classe/un code personnalisé permettant d’obtenir un badge sur UiBarbutton et UiButton. 

Cherché jusqu'ici:

https://github.com/Marxon13/M13BadgeView

avec classe MKBadge etc.

13
coolagrwal

Il existe une solution plus élégante avec une extension pour 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 = kCAAlignmentCenter
        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()
    }
}

Ce code génial a été créé par Stefano Vettor et vous pouvez trouver tous les détails sur: https://Gist.github.com/freedom27/c709923b163e26405f62b799437243f4

12
Julio Bailon

Solution de travail:

Étape 1: Créez d’abord un nouveau fichier Swift qui est une sous-classe de UIButton, comme suit:

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")
    }
}

Étape 2: Créez une fonction dans votre fichier de base que vous pourrez utiliser dans chaque contrôleur de vue:

  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)
    }

Étape 3: Utilisez la fonction ci-dessus à partir de n’importe quel contrôleur de vue de cette manière:

self.addBadge(itemvalue: localStorage.string(forKey: "total_products_in_cart") ?? "0")
4
Shivam Tripathi

en utilisant M13BadgeView .. utilisez ce code

(J'utilise fontawesome.Swift pour les boutons :: https://github.com/thii/FontAwesome.Swift )

    let rightButton = UIButton(frame: CGRect(x:0,y:0,width:30,height:30))
    rightButton.titleLabel?.font = UIFont.fontAwesome(ofSize: 22)
    rightButton.setTitle(String.fontAwesomeIcon(name: .shoppingBasket), for: .normal)

    let rightButtonItem : UIBarButtonItem = UIBarButtonItem(customView: rightButton)

    let badgeView = M13BadgeView()
        badgeView.text = "1"
        badgeView.textColor = UIColor.white
        badgeView.badgeBackgroundColor = UIColor.red
        badgeView.borderWidth = 1.0
        badgeView.borderColor = UIColor.white
        badgeView.horizontalAlignment = M13BadgeViewHorizontalAlignmentLeft
        badgeView.verticalAlignment = M13BadgeViewVerticalAlignmentTop
        badgeView.hidesWhenZero = true

    rightButton.addSubview(badgeView)

    self.navigationItem.rightBarButtonItem = rightButtonItem
2
keithics

J'ai eu la même tâche. Je ne voulais pas utiliser de bibliothèques tierces. Tout d’abord, j’ai essayé la solution de Stefano et c’est génial, mais j’ai décidé de mettre en œuvre ma propre façon de le résoudre.

À mon humble avis, quelques étapes simples sont décrites brièvement ci-dessous:

  1. Créez une instance UIView dans le fichier .xib et placez les éléments nécessaires tels que UILabel ou UIImageView instance en fonction de vos exigences de conception . enter image description here

La dernière action que j'ai faite dans cette étape consiste à placer un bouton invisible dans la hiérarchie du haut de la vue.

enter image description here

  1. Créez YourCustomView.Swift et liez tous les @IBOutlets de xib au fichier en cours dans votre implémentation de classe d'affichage personnalisée.

enter image description here

  1. Ensuite, implémentez la fonction de classe dans la classe YourCustomView qui chargera la vue personnalisée à partir de xib et la retournera en tant qu’instance YourCustomView.

enter image description here

  1. Enfin, ajoutez votre badge personnalisé à votre instance de contrôleur de vue personnalisée!

enter image description here

Mon résultat est ..

enter image description here

P.S. Si vous devez implémenter @IBActions, je vous recommande de lier votre vue personnalisée à votre contrôleur de vue personnalisé via le modèle de délégué.

1
Ivan Shokurov

Bonne réponse @Julio Bailon ( https://stackoverflow.com/a/45948819/1898973 )!

Voici le site de l'auteur avec l'explication complète: http://www.stefanovettor.com/2016/04/30/adding-badge-uibarbuttonitem/

Il semble ne pas fonctionner sur iOS 11, peut-être parce que le script tente d'accéder à la propriété "view" de UIBarButtonItem. Je l'ai fait fonctionner:

  1. En créant une UIButton puis en créant la UIBarButtonItem en utilisant la UIButton en tant que customView: 

    navigationItem.rightBarButtonItem = UIBarButtonItem.init(
            customView: shoppingCartButton)
    
  2. En remplaçant la ligne dans l'extension UIBarButtonItem:

    guard let view = self.value(forKey: "view") as? UIView else { return }
    

    avec ce qui suit:

    guard let view = self.customView else { return }
    

Cela me semble élégant et, mieux encore, cela a fonctionné!

0
Leandro