Etranges coins noirs lors de l'utilisation de la fonction draw (_ rect :)

Dec 07 2020

Si je mets le code pour ma fonction UIBezierPathà l'intérieur, draw(_ rect:)j'obtiens ces étranges coins noirs très fins autour de la vue et de la queue. Lorsque vous faites glisser la vue (par exemple dans un contrôleur de vue présenté), ces lignes fines commencent également à clignoter. Je suppose que c'est un bug de rendu étrange. Quelqu'un sait-il s'il existe un moyen de résoudre ce problème?

class RenderingView: UIView {
    lazy var backgroundView: UIView = {
        let view = UIView()
        view.layer.cornerRadius = 8
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    private lazy var shadowView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    private lazy var textLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = "Rendering Bug"
        return label
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setup() {
        backgroundColor = .clear
        backgroundView.backgroundColor = .yellow
        layer.borderWidth = 0
        setupLayout()
    }

    private func setupLayout() {
        [shadowView, backgroundView, textLabel].forEach(addSubview)

        backgroundView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        backgroundView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        backgroundView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
        backgroundView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true

        shadowView.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor).isActive = true
        shadowView.topAnchor.constraint(equalTo: backgroundView.topAnchor).isActive = true
        shadowView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor).isActive = true
        shadowView.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor).isActive = true

        textLabel.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor, constant: 10).isActive = true
        textLabel.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor, constant: -10).isActive = true
        textLabel.topAnchor.constraint(equalTo: backgroundView.topAnchor, constant: 10).isActive = true
        textLabel.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor, constant: -10).isActive = true
    }

    override func draw(_ rect: CGRect) {
        shapeBackground()
    }

    private func shapeBackground() {
        let tailLayer = CAShapeLayer()

        let bezierPath = UIBezierPath(roundedRect: CGRect(x: backgroundView.bounds.minX,
                                                          y: backgroundView.bounds.minY,
                                                          width: backgroundView.bounds.width,
                                                          height: backgroundView.bounds.height - 12),
                                      cornerRadius: 8)

        let shadowBezierPath = UIBezierPath(roundedRect: CGRect(x: backgroundView.bounds.minX + 5,
                                                                y: backgroundView.bounds.minY + 10,
                                                                width: backgroundView.bounds.width - 10,
                                                                height: backgroundView.bounds.height - 12 - 10),
                                            cornerRadius: 8)

        [bezierPath, shadowBezierPath].forEach {
            $0.move(to: CGPoint(x: backgroundView.bounds.midX - 12, y: backgroundView.bounds.maxY - 12)) $0.addLine(to: CGPoint(x: backgroundView.bounds.midX, y: backgroundView.bounds.maxY))
            $0.addLine(to: CGPoint(x: backgroundView.bounds.midX + 12, y: backgroundView.bounds.maxY - 12)) $0.fill()
            $0.close()
        }

        tailLayer.path = bezierPath.cgPath
        tailLayer.fillColor = UIColor.white.cgColor

        shadowView.layer.shadowPath = shadowBezierPath.cgPath
        shadowView.layer.cornerRadius = 8

        backgroundView.layer.masksToBounds = true
        backgroundView.layer.mask = tailLayer
    }
}

EDIT: Il s'est avéré que j'ai dû utiliser addClip()les chemins de Bézier pour me débarrasser de ces coins

Réponses

2 Eskils Dec 07 2020 at 23:44

Je ne sais pas si c'est ce que vous visez, mais cela semble parfait lorsque vous déplacez la shapeBackground()méthode draw(_ rect:)et effectuez de légères modifications.

J'ai modifié certaines de vos routines de dessin à l'intérieur shapeBackground()et déplacé la fonction layoutSubviews()pour calculer la position de la queue à partir du cadre créé par des contraintes. J'ai également ajouté quelques variables pour tailWidth, & tailHeight.

Ainsi:

class RenderingView: UIView {
    lazy var backgroundView: UIView = {
        let view = UIView()
        view.layer.cornerRadius = 8
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var shadowView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var textLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = "No Rendering Bug"
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setup() {
        backgroundColor = .clear
        backgroundView.backgroundColor = .systemTeal
        //backgroundView.frame.size = CGSize(width: 100, height: 100)
        layer.borderWidth = 0
        setupLayout()
    }
    
    private func setupLayout() {
        [shadowView, backgroundView, textLabel].forEach(addSubview)
        
        backgroundView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        backgroundView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        backgroundView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
        backgroundView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        
        shadowView.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor).isActive = true
        shadowView.topAnchor.constraint(equalTo: backgroundView.topAnchor).isActive = true
        shadowView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor).isActive = true
        shadowView.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor).isActive = true
        
        textLabel.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor, constant: 10).isActive = true
        textLabel.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor, constant: -10).isActive = true
        textLabel.topAnchor.constraint(equalTo: backgroundView.topAnchor, constant: 10).isActive = true
        textLabel.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor, constant: -10).isActive = true
        
        
    }
    
    override func didMoveToWindow() {
        super.didMoveToWindow()
        
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        shapeBackground(color: UIColor.systemTeal)
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
    }
    
    private func shapeBackground(color: UIColor) {
        let tailLayer = CAShapeLayer()
        tailLayer.name = "tailLayer"
        
        let tailWidth: CGFloat = 16
        let tailHeight: CGFloat = 10
        
        let bezierPath = UIBezierPath()
        let shadowBezierPath = UIBezierPath()
        
        [bezierPath, shadowBezierPath].forEach {
            $0.move(to: CGPoint(x: 0, y: 0)) $0.addLine(to: CGPoint(x: tailWidth / 2, y: tailHeight))
            $0.addLine(to: CGPoint(x: tailWidth, y: 0)) $0.fill()
            $0.close()
        }
        
        tailLayer.path = bezierPath.cgPath
        tailLayer.fillColor = color.cgColor
        
        shadowView.layer.shadowPath = shadowBezierPath.cgPath
        shadowView.layer.cornerRadius = 8
        
        print(backgroundView.bounds.width)
        
        tailLayer.frame = CGRect(x: (backgroundView.bounds.width - tailWidth) / 2,
                                 y: backgroundView.bounds.maxY,
                                 width: tailWidth,
                                 height: tailHeight)
        
        backgroundView.layer.masksToBounds = false
        backgroundView.layer.addSublayer(tailLayer)
    }
}