Captura de tela do meu problema
Estou com um problema em que a borda da visualização principal de uma classe UIview personalizada está sobrepondo a rankbadgeview dentro dessa classe personalizada. Não consigo entender o porquê. Tentei trazer a subvisualização rankbadgeview para a frente, junto com o uilabel dentro dela, mas ainda não funciona. Alguém pode me ajudar a identificar o problema?
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)
}
}
Dos documentos da Apple : "A borda... é composta acima do conteúdo e das subcamadas do receptor..."
Então, se adicionarmos uma subvisualização a uma visualização e definirmos as
layer.border
propriedades da visualização assim:obtemos este resultado:
Em vez disso, conforme
matt
comentado, queremos desenhar (usando umCAShapeLayer
) a forma com borda:e temos isso:
Aqui está sua classe com
CAShapeLayer
modificação - procure por comentários relacionados começando com// DonMag -
:e agora obtemos o resultado desejado, sem a borda sobreposta:
O problema não é a ordenação z-index das visualizações.
rankBadgeView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 12)
Esta linha posiciona-se
rankBadgeView
12 pontos abaixo da parte inferior da vista principal (LeaderboardCircleView
). Como o emblema está tecnicamente fora dos limites , a borda é desenhada sobre ele , independentemente da posição z oubringSubviewToFront()
.Mesmo que você defina:
A renderização das bordas da camada não respeita o zPosition da subvisualização fora dos limites da maneira esperada. As bordas são desenhadas sobre tudo no Core Animation, a menos que você as mova para as subvisualizações ou as mascare.
Você pode mover o
rankBadgeView
interior dos limitesEm vez de colocar o emblema 12 pontos abaixo da vista principal:
Isso mantém o emblema dentro dos limites. Combinado com o posicionamento z correto, o emblema será exibido acima da borda. Me avise se funcionar.