iOS UIView 渲染问题:渲染后旋转蒙版显示不正确

iOS UIView Rendering Issue: Incorrect Display of Rotated Mask After Rendering

提问人:Fan 提问时间:8/14/2023 最后编辑:Fan 更新时间:8/16/2023 访问量:53

问:

我尝试在 CustomView 中渲染 imageView,发现 triangleView 的蒙版在旋转后没有正确渲染,并且没有覆盖其中的一部分。

异常如图所示

import UIKit

extension UIBezierPath {
    convenience init(triangleIn rect: CGRect) {
        self.init()
        let startX = rect.midX
        let startY = rect.minY
        let endY = rect.maxY
        self.move(to: CGPoint(x: startX, y: startY))
        self.addLine(to: CGPoint(x: rect.maxX, y: endY))
        self.addLine(to: CGPoint(x: rect.minX, y: endY))
        self.close()
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        let w: CGFloat = view.frame.width

        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: w, height: w))
        imageView.image = UIImage(named: "IMG_2778")
        view.addSubview(imageView)

        let triangleView = UIView(frame: CGRect(x: w/2, y: w/2, width: 100, height: 100))
        triangleView.backgroundColor = .systemBlue
        triangleView.transform = triangleView.transform.rotated(by: 0.523)

        let maskLayer = CAShapeLayer()
        maskLayer.path = UIBezierPath(triangleIn: triangleView.bounds).cgPath
        triangleView.layer.mask = maskLayer

        imageView.addSubview(triangleView)

        let customView = CustomView(frame: CGRect(x: 0, y: w + 10, width: w, height: w))
        customView.renderView = imageView
        view.addSubview(customView)
    }
}

class CustomView: UIView {
    var renderView: UIView!

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

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        guard let renderView = renderView else { return }

        renderView.layer.render(in: context)
    }
}

我希望自定义路径的掩码正常呈现

iOS UIVie查看

评论


答:

0赞 DonMag 8/16/2023 #1

当视图的框架更改大小时,图层和图层蒙版不会自动调整大小。

如果我们在三角形视图的“下方”添加一个轮廓分明的半透明黄色视图,并在旋转三角形视图时更新其框架,我们可以看到(某种程度上)发生了什么......

一、不带旋转变换:

enter image description here

然后,在旋转变换后:

enter image description here

非常古怪的结果,但显然是由于内部工作方式:UIView.layer.render(in: context)

此方法直接从图层树进行渲染,忽略添加到渲染树的任何动画。在图层的坐标空间中渲染。

根据您的最终目标和需求,一种选择是为您的三角形视图使用子类,例如(使用 UIBezierPath 扩展):UIViewtriangleIn rect:

class MyTriangleView: UIView {
    
    override class var layerClass: AnyClass { CAShapeLayer.self }
    var shapeLayer: CAShapeLayer { layer as! CAShapeLayer }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        shapeLayer.path = UIBezierPath(triangleIn: bounds).cgPath
    }
    
}

那么没有对你的改变,只有对你的原始的微小改变:CustomViewViewController

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        let w: CGFloat = view.frame.width
        
        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: w, height: w))
        imageView.image = UIImage(named: "IMG_2778")
        view.addSubview(imageView)
        
        // use custom MyTriangleView
        let triangleView = MyTriangleView(frame: CGRect(x: w/2, y: w/2, width: 100, height: 100))
        triangleView.shapeLayer.fillColor = UIColor.systemBlue.cgColor
        
        triangleView.transform = triangleView.transform.rotated(by: 0.523)
        
        imageView.addSubview(triangleView)
        
        let customView = CustomView(frame: CGRect(x: 0, y: w + 10, width: w, height: w))
        customView.renderView = imageView
        view.addSubview(customView)
    }
}

结果:

enter image description here