UIButton 上的子视图始终出现在标签和图像后面

问题描述 投票:0回答:1

我有一个标签,应该是未读通知计数的徽章,但由于重构为使用 UIButton.Configuration,无论如何,标签总是呈现在按钮标签和图像后面。

这是所需的布局:

但由于某种原因我得到了这个

代码如下:

UIButton 的配置

private func getNotificationButtonConfiguration() -> UIButton.Configuration {
        var container = AttributeContainer()
        container.font = .sansSemiBold(size: 14)
        container.foregroundColor = .warmGray
        
        var configuration = UIButton.Configuration.plain()
        configuration.contentInsets = .zero
        configuration.attributedTitle = AttributedString("Notifications".localize, attributes: container)
        configuration.imagePadding = 3
        
        return configuration
    }

然后在成功获取未读通知的数量后,我调用此块来显示徽章

private func showBadge(withCount count: Int) {
        let badge = UILabel.badgeLabel(withCount: count)
        badge.tag = 9830384
        notificationButton.addSubview(badge)
        
        let size: CGSize = (badge.text! as NSString).size(withAttributes: [NSAttributedString.Key.font: UIFont.sansBold(size: 12)])
        
        badge.snp.makeConstraints { make in
            make.trailing.equalTo(notificationButton.snp.leading).offset(25)
            make.top.equalTo(notificationButton.snp.top)
            make.width.equalTo(size.width + 10)
            make.height.equalTo(16)
        }
    }

生成徽章标签的函数是

static func badgeLabel(withCount count: Int) -> UILabel {
        let badgeCount = UILabel(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
        badgeCount.translatesAutoresizingMaskIntoConstraints = false
        badgeCount.layer.cornerRadius = badgeCount.bounds.size.height / 2
        badgeCount.textAlignment = .center
        badgeCount.layer.masksToBounds = true
        badgeCount.textColor = .white
        badgeCount.font = .sansBold(size: 12)
        badgeCount.backgroundColor = .heartRed
        badgeCount.text = String(count)
        return badgeCount
    }

我尝试在

notificationButton.bringSubviewToFront(badge)
函数中调用
showBadge
,但它对布局没有任何影响。

任何想法都将不胜感激!

在 UIButton 上添加子视图会始终将该子视图发送到后面,而不是在按钮标签和图像上渲染。

按钮初始化:

let notificationButton = UIButton(type: .custom)
notificationButton.setImage(UIImage(named: "icon_bell_filled"), for: .normal)
notificationButton.configuration = getNotificationButtonConfiguration()
notificationButton.titleLabel?.tag = 200
notificationButton.addTarget(self, action: #selector(openNotificationsButtonAction), for: .touchUpInside)
ios swift uikit uibutton addsubview
1个回答
0
投票

正如您在评论中提到的,您可以将按钮和徽章包装在

UIView

或者,您可以子类化

UIButton
并添加附加/分离方法。

简单示例:

class BadgedButton: UIButton {
    
    public weak var theBadgeView: UIView!
    
    public func attachBadge(_ v: UIView) -> Bool {
        // we must have a superview!
        guard let sv = self.superview else { return false }
        if let curBadge = theBadgeView {
            curBadge.removeFromSuperview()
        }
        v.translatesAutoresizingMaskIntoConstraints = false
        sv.addSubview(v)
        let sz: CGSize = v.intrinsicContentSize
        NSLayoutConstraint.activate([
            v.trailingAnchor.constraint(equalTo: self.leadingAnchor, constant: 25.0),
            v.topAnchor.constraint(equalTo: self.topAnchor, constant: 0.0),
            v.widthAnchor.constraint(equalToConstant: sz.width + 10.0),
            v.heightAnchor.constraint(equalToConstant: 16.0),
        ])
        self.theBadgeView = v
        return true;
    }
    
    public func detachBadge() {
        if let curBadge = self.theBadgeView {
            curBadge.removeFromSuperview()
        }
    }
    
    override func didMoveToSuperview() {
        // remove the badge view when self is removed from superview
        if nil == self.superview,
           let curBadge = self.theBadgeView
        {
            curBadge.removeFromSuperview()
        }
    }
    
}

class BadgeTestVC: UIViewController {
    
    var btn: BadgedButton!
    
    var badgeCount: Int = 0
    
    let incLabel = UILabel()
    let decLabel = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
        
        var cfg = getNotificationButtonConfiguration()
        btn = BadgedButton(configuration: cfg)
        
        btn.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(btn)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
        ])
        
        [incLabel, decLabel].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(v)
            v.numberOfLines = 0
            v.textAlignment = .center
        }
        incLabel.text = "Tap here to\nIncrement\nBadge Count"
        decLabel.text = "Tap here to\nDecrement\nBadge Count"
        incLabel.backgroundColor = .cyan
        decLabel.backgroundColor = .yellow
        NSLayoutConstraint.activate([
            decLabel.topAnchor.constraint(equalTo: g.centerYAnchor, constant: 0.0),
            decLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            decLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
            
            incLabel.topAnchor.constraint(equalTo: g.centerYAnchor, constant: 0.0),
            incLabel.leadingAnchor.constraint(equalTo: decLabel.trailingAnchor, constant: 20.0),
            incLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
            incLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),

            incLabel.widthAnchor.constraint(equalTo: decLabel.widthAnchor),
        ])
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let t = touches.first else { return }
        let p = t.location(in: self.view)
        if decLabel.frame.contains(p) {
            badgeCount -= 1
        }
        if incLabel.frame.contains(p) {
            badgeCount += 1
        }
        badgeCount = max(badgeCount, 0)
        showBadge(withCount: badgeCount)
    }
    
    private func getNotificationButtonConfiguration() -> UIButton.Configuration {
        var container = AttributeContainer()
        container.font = .systemFont(ofSize: 14.0, weight: .bold) //.sansSemiBold(size: 14)
        container.foregroundColor = .gray // .warmGray
        
        var configuration = UIButton.Configuration.plain()
        configuration.contentInsets = .zero
        configuration.attributedTitle = AttributedString("Notifications", attributes: container)
        configuration.imagePadding = 3
        
        if let img = UIImage(systemName: "bell.fill") {
            configuration.image = img
        }
        
        return configuration
    }
    
    private func showBadge(withCount count: Int) {
        if count == 0 {
            btn.detachBadge()
            return
        }
        let badge = UILabel.badgeLabel(withCount: count)
        badge.tag = 9830384
        if !btn.attachBadge(badge) {
            print("Attach failed!")
        }
    }
    
}

extension UILabel {

    static func badgeLabel(withCount count: Int) -> UILabel {
        let badgeCount = UILabel(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
        badgeCount.translatesAutoresizingMaskIntoConstraints = false
        badgeCount.layer.cornerRadius = badgeCount.bounds.size.height / 2
        badgeCount.textAlignment = .center
        badgeCount.layer.masksToBounds = true
        badgeCount.textColor = .white
        badgeCount.font = .systemFont(ofSize: 12.0, weight: .bold) //.sansBold(size: 12)
        badgeCount.backgroundColor = .red // .heartRed
        badgeCount.text = String(count)
        return badgeCount
    }

}

运行时看起来像这样:

点击增量一次:

并点击 12 次:

attachBadge(...)
方法会在添加新视图(标签)之前删除“已附加”视图(标签)。

如果需要,可以轻松优化以“更新”标签文本。

© www.soinside.com 2019 - 2024. All rights reserved.