首页 > 解决方案 > 使用 draw(_ rect:) 函数时出现奇怪的黑角

问题描述

如果我将我的代码UIBezierPath放在draw(_ rect:)函数内部,我会在视图和尾部周围得到这些奇怪的非常细的黑色角落。当拖动视图时(例如在呈现的视图控制器中),这些细线也开始闪烁。我认为这是一个奇怪的渲染错误。有谁知道是否有办法解决这个问题?

在此处输入图像描述

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

编辑: 原来我不得不addClip()在贝塞尔路径上使用来摆脱这些角落

标签: iosswiftxcodeviewdraw

解决方案


不确定这是否是您的目标,但是当您将shapeBackground()方法移出draw(_ rect:)并进行一些细微修改时,它看起来完美无瑕。

我在里面修改了你的一些绘图例程shapeBackground(),并将函数移入layoutSubviews()以根据约束生成的框架计算尾部的位置。tailWidth我还为, &添加了一些变量tailHeight

像这样:

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

推荐阅读