当包含在 UIBarButtonItem 中时增加 UIButton 的固有大小

Increasing UIButton's intrinsic size when contained in UIBarButtonItem

我创建了一个 UIButton 子类,其视图位于按钮边界之外。

如截图所示:

如您所见,徽章位于按钮之外。它的中心固定在按钮的右上角。

问题是当按钮放在 UIBarButtonItem 内时,徽章被剪掉了。

视图层次检查器显示原因:

如何告知 UIBarButtonItem 按钮的尺寸比按钮本身大?我必须覆盖 UIButton 的哪些属性?

我用于按钮的代码:


import UIKit
import BadgeSwift

@objc public class BadgeButton: UIButton {
    private lazy var badge: BadgeSwift = {
        let b = BadgeSwift()
        b.textColor = .white
        b.insets = CGSize(width: 0, height: 0)
        b.font = UIFont.preferredFont(forTextStyle: .caption1)
        return b
    }()

    public override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required public init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    private func commonInit() {
        addSubview(badge)
        badge.text = "1"
    }

    public override func layoutSubviews() {
        super.layoutSubviews()
        clipsToBounds = false
        badge.sizeToFit()
        badge.center = center
        badge.center = CGPoint(x: bounds.origin.x + bounds.size.width, y: bounds.origin.y)
    }
}

对于徽章视图,我使用以下库:BadgeSwift Library

您可以为您的 UIBarButtonItem 创建图层并塑造它。

创建 .swift 文件并粘贴此代码

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

    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: [NSAttributedString.Key.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 = CATextLayerAlignmentMode.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)
    }

    func removeBadge() {
        badgeLayer?.removeFromSuperlayer()
    }
}

用法来自您的ViewController

//ADD BADGE
self.yourBarBtn.addBadge(text: String(7))

//REMOVE BADGE
self.pendingBarBtn.removeBadge()

已通过在徽章周围添加清晰的边框来解决问题:


import UIKit
import BadgeSwift

@objc public class BadgeButton: UIButton {
    private lazy var badge: BadgeSwift = {
        let b = BadgeSwift()
        b.textColor = .white
        b.borderWidth = 1
        b.borderColor = .clear
        b.font = UIFont.preferredFont(forTextStyle: .footnote)
        return b
    }()

    public var badgeText: String? {
        get {
            return badge.text
        }
        set {
            badge.text = newValue
        }
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required public init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    private func commonInit() {
        addSubview(badge)
        badge.text = "1"
    }

    public override func layoutSubviews() {
        super.layoutSubviews()
        clipsToBounds = false
        badge.sizeToFit()
        badge.center = CGPoint(x: bounds.origin.x + bounds.size.width, y: bounds.origin.y)
    }
}