我遇到了一个问题:我自定义的 UIview 类的主视图边框覆盖了该类中的 rankbadgeview。我怎么也想不出原因。我试过把 rankbadgeview 子视图连同其中的 uilabel 一起放到最上面,但仍然不行。谁能帮我看看这个问题?
class LeaderboardCircleView: UIView {
private let mainLabel = UILabel()
let rankBadgeView = UIView()
private let rankLabel = UILabel()
private let scoreLabel = UILabel()
init(mainText: String, rankText: String, backgroundColor: UIColor, score: Int) {
super.init(frame: .zero)
setupViews()
configure(mainText: mainText, rankText: rankText, backgroundColor: backgroundColor, score: score)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func updateScoreLabelWith(score: Int) {
let trophyAttachment = NSTextAttachment()
trophyAttachment.image = UIImage(systemName: "trophy.fill")?.withTintColor(.systemYellow, renderingMode: .alwaysOriginal)
trophyAttachment.bounds = CGRect(x: 0, y: -2, width: 16, height: 16)
let trophyString = NSAttributedString(attachment: trophyAttachment)
let scoreString = NSAttributedString(string: " \(score) pts", attributes: [
.font: AppStyle.shared.headerFont(size: 12),
.foregroundColor: UIColor.darkGray
])
let fullScoreText = NSMutableAttributedString()
fullScoreText.append(trophyString)
fullScoreText.append(scoreString)
scoreLabel.attributedText = fullScoreText
}
private func setupViews() {
layer.borderWidth = 2
layer.borderColor = AppStyle.shared.primaryColor?.cgColor
translatesAutoresizingMaskIntoConstraints = false
clipsToBounds = false
mainLabel.translatesAutoresizingMaskIntoConstraints = false
mainLabel.textAlignment = .center
mainLabel.adjustsFontSizeToFitWidth = true
mainLabel.minimumScaleFactor = 0.5
mainLabel.numberOfLines = 2
mainLabel.font = AppStyle.shared.headerFont(size: 18)
mainLabel.textColor = .white
addSubview(mainLabel)
rankBadgeView.translatesAutoresizingMaskIntoConstraints = false
rankBadgeView.clipsToBounds = true
rankBadgeView.backgroundColor = AppStyle.shared.primaryColor
rankBadgeView.layer.zPosition = 1 // Sends it behind the border
addSubview(rankBadgeView)
rankLabel.translatesAutoresizingMaskIntoConstraints = false
rankLabel.textAlignment = .center
rankLabel.font = AppStyle.shared.headerFont(size: 12)
rankLabel.textColor = .white
rankBadgeView.addSubview(rankLabel)
scoreLabel.translatesAutoresizingMaskIntoConstraints = false
scoreLabel.textAlignment = .center
scoreLabel.adjustsFontSizeToFitWidth = true
scoreLabel.minimumScaleFactor = 0.5
scoreLabel.numberOfLines = 1
addSubview(scoreLabel)
NSLayoutConstraint.activate([
mainLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
mainLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
mainLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
mainLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
rankBadgeView.centerXAnchor.constraint(equalTo: centerXAnchor),
rankBadgeView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 12),
rankBadgeView.widthAnchor.constraint(equalToConstant: 30),
rankBadgeView.heightAnchor.constraint(equalToConstant: 30),
rankLabel.centerXAnchor.constraint(equalTo: rankBadgeView.centerXAnchor),
rankLabel.centerYAnchor.constraint(equalTo: rankBadgeView.centerYAnchor),
scoreLabel.topAnchor.constraint(equalTo: rankBadgeView.bottomAnchor, constant: 4),
scoreLabel.centerXAnchor.constraint(equalTo: centerXAnchor)
])
bringSubviewToFront(rankBadgeView)
rankBadgeView.bringSubviewToFront(scoreLabel)
}
private func configure(mainText: String, rankText: String, backgroundColor: UIColor, score: Int) {
mainLabel.text = mainText
self.backgroundColor = backgroundColor
rankLabel.text = rankText
let trophyAttachment = NSTextAttachment()
trophyAttachment.image = UIImage(systemName: "trophy.fill")?.withTintColor(.systemYellow, renderingMode: .alwaysOriginal)
trophyAttachment.bounds = CGRect(x: 0, y: -2, width: 16, height: 16)
let trophyString = NSAttributedString(attachment: trophyAttachment)
let scoreString = NSAttributedString(string: " \(score) pts", attributes: [
.font: AppStyle.shared.headerFont(size: 12),
.foregroundColor: UIColor.darkGray
])
let fullScoreText = NSMutableAttributedString()
fullScoreText.append(trophyString)
fullScoreText.append(scoreString)
scoreLabel.attributedText = fullScoreText
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.width / 2
rankBadgeView.layer.cornerRadius = rankBadgeView.bounds.width / 2
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.2
layer.shadowRadius = 6.0
layer.shadowOffset = CGSize(width: 0, height: 4)
layer.masksToBounds = false
rankBadgeView.layer.shadowColor = UIColor.black.cgColor
rankBadgeView.layer.shadowOpacity = 0.2
rankBadgeView.layer.shadowRadius = 4.0
rankBadgeView.layer.shadowOffset = CGSize(width: 0, height: 2)
rankBadgeView.layer.masksToBounds = false
}
func update(mainText: String, rankText: String, backgroundColor: UIColor, score: Int) {
configure(mainText: mainText, rankText: rankText, backgroundColor: backgroundColor, score: score)
}
}
来自 Apple 的文档:“边框......合成在接收器的内容和子层之上......”
layer.border
因此,如果我们向视图添加子视图,并像这样设置视图的属性:我们得到这个结果:
相反,正如
matt
评论所说,我们想要绘制(使用CAShapeLayer
)带边框的形状:我们得到了这个:
这是经过修改的课程
CAShapeLayer
- 查找以以下内容开头的相关评论// DonMag -
:现在我们得到了想要的结果,没有重叠的边框:
问题不在于视图的 z-index 排序。
rankBadgeView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 12)
这条线位于主视图底部下方
rankBadgeView
12 个点的LeaderboardCircleView
位置 ( )。由于徽章严格来说位于边界之外,因此无论 z 位置或 如何,边框都会绘制在其上方bringSubviewToFront()
。即使您设置:
图层的边框渲染不符合您预期,无法尊重子视图 zPosition 的边界外属性。在 Core Animation 中,除非您将边框移入子视图或将其遮罩,否则边框会绘制在所有内容之上。
您可以在边界
rankBadgeView
内移动不要将徽章放在主视图下方12 点的位置:
这样可以让徽章保持在边界内。结合适当的 z 位置,徽章将显示在边界上方。如果有效,请告诉我。