iOS UIView渲染异常:旋转后的自定义遮罩无法正确显示
问题原因
当你给triangleView设置transform旋转变换后,maskLayer的路径是基于视图未旋转的原始bounds创建的,且遮罩层没有同步应用该旋转变换。这导致旋转后的视图内容与遮罩路径错位,出现部分区域未被遮挡的异常。
解决方案
将旋转变换同步应用到maskLayer上,让遮罩路径与旋转后的视图内容匹配,有两种实现方式:
方式一:给遮罩层添加旋转变换
取消对triangleView的transform设置,转而给视图的layer和遮罩层maskLayer分别添加相同的旋转变换:
// 替换原triangleView.transform及maskLayer相关代码 let rotationAngle = CGFloat(0.523) // 给视图layer添加旋转变换 triangleView.layer.transform = CATransform3DMakeRotation(rotationAngle, 0, 0, 1) let maskLayer = CAShapeLayer() maskLayer.path = UIBezierPath(triangleIn: triangleView.bounds).cgPath // 给遮罩层添加相同的旋转变换 maskLayer.transform = CATransform3DMakeRotation(rotationAngle, 0, 0, 1) triangleView.layer.mask = maskLayer
方式二:给路径添加旋转变换
在创建遮罩路径时直接应用旋转变换,保持视图的transform设置不变:
// 替换原maskLayer相关代码 let rotationAngle = CGFloat(0.523) triangleView.transform = triangleView.transform.rotated(by: rotationAngle) let maskLayer = CAShapeLayer() let trianglePath = UIBezierPath(triangleIn: triangleView.bounds) // 给路径添加旋转变换 trianglePath.apply(CGAffineTransform(rotationAngle: rotationAngle)) maskLayer.path = trianglePath.cgPath triangleView.layer.mask = maskLayer
修改后的完整代码(方式一)
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 let rotationAngle = CGFloat(0.523) triangleView.layer.transform = CATransform3DMakeRotation(rotationAngle, 0, 0, 1) let maskLayer = CAShapeLayer() maskLayer.path = UIBezierPath(triangleIn: triangleView.bounds).cgPath maskLayer.transform = CATransform3DMakeRotation(rotationAngle, 0, 0, 1) 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) } }
内容的提问来源于stack exchange,提问作者Fan
相关产品推荐
相关产品推荐

