常见的几种会引发离屏渲染的设置
1、使用了mask的layer(layer.mask)
2、需要进行裁剪的layer(layer.masksToBounds / view.clipsToBounds)(模拟器会离屏渲染,真机测试暂无离屏渲染的情况)
3、设置了组透明度true,并且透明度不为1的layer (layer.allowsGroupOpacity/layer.opacity)
4、添加了投影的layer(layer.shadow)
5、采用了光栅化的layer(layer.shouldRasterize)
6、绘制了文字的layer (UILabel,CATextLayer,CoreText等)
已知的解决方法:
Shadow 可以通过指定路径来取消离屏渲染。 Mask 无法取消离屏渲染,可以利用混合layer图层或者另添加新的图片图层来进行优化。
示例代码
/** 切角必须明确View的大小 可重写View的layoutSubviews方法使用,剪切超出部分View本身自行控制*/
override func layoutSubviews() {
super.layoutSubviews()
// 有圆角或边框的都统一创建,不需要考虑渐变,阴影
if cornerRadius > 0 || borderWidth > 0 {
let originalImg = createOriginalImage()
createCornerImage(originalImg)
}// 渐变无切角设置原图片
else if isGradient {
let originalImg = createOriginalImage()
addSubviewImage(originalImg)
}// 阴影有填充背景色
else if isShadow {
layer.backgroundColor = fillColor?.cgColor
// 阴影设置消除离屏渲染
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
}
}
/// fillColor色或者渐变色生成图片
private func createOriginalImage() -> UIImage? {
if isGradient, let gradient = gradientView {
gradient.frame = bounds
return gradient.asImage()
}
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor((fillColor ?? UIColor.white).cgColor)
context?.fill(bounds)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
/// 对图片进行切角+边框
private func createCornerImage(_ image: UIImage?) {
let borderW = borderWidth; let borderC = borderColor ?? .clear
let cornerRadii = CGSize(width: cornerRadius, height: cornerRadius)
let rect = CGRect(origin: .zero, size: bounds.size)
let bezierPath = UIBezierPath(roundedRect: rect, byRoundingCorners: corners ?? .allCorners, cornerRadii: cornerRadii)
UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context?.addPath(bezierPath.cgPath)
context?.clip()
image?.draw(in: rect)
borderC.setStroke()
bezierPath.lineWidth = borderW
bezierPath.stroke()
let cornerImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
addSubviewImage(cornerImage)
}
/// 给当前LCornerView视图对象添加显示图片
private func addSubviewImage(_ img: UIImage?) {
subviews.forEach { subView in subView.removeFromSuperview() }
let imageView = UIImageView(image: img)
imageView.frame = bounds
addSubview(imageView)
}
使用方法:
/// 圆角视图的固定tag值
fileprivate let cornerTag: Int = 99999999
/// 解决UIView离屏渲染的切圆角问题。 纯切角,默认颜色为白色, 默认切角为all。 全切角以及对UIImageView切角,不需要添加图片设置圆角推荐使用系统方法layer.cornerRadius
/// - Parameters:
/// - value: 圆角半径值
/// - corners: 指定切角类型
/// - fillColor: 圆角视图背景填充色 默认白色
func cornerRadius(value: CGFloat = 0, corners: UIRectCorner? = nil, fillColor: UIColor? = nil) {
let imageView = viewWithTag(cornerTag) as? LCornerView ?? LCornerView()
imageView.setupCorner(radius: value, corners, fillColor)
remakeConstraints(view: imageView)
}
/// 设置边框。配合func cornerRadius(_ radius: CGFloat, corners: UIRectCorner? = nil, color: UIColor? = nil)方法使用
/// - Parameters:
/// - width: 边框宽度
/// - borderColor: 边框颜色
func borderWidth(_ value: CGFloat, borderColor: UIColor? = nil) {
let imageView = viewWithTag(cornerTag) as? LCornerView ?? LCornerView()
imageView.setupBorder(width: value, borderColor: borderColor)
remakeConstraints(view: imageView)
}
/// 渐变+纯切角。默认颜色为白色, 默认切角为all。可同时使用func borderWidth(_ width: CGFloat, borderColor: UIColor? = nil)
/// - Parameters:
/// - colors: 渐变色数组
/// - start: 开始点位置
/// - end: 结束点位置
/// - locations: 渐变色分布位置
func gradientColors(_ colors: [UIColor], _ start: CGPoint? = nil, _ end: CGPoint? = nil, locations: [NSNumber]? = nil) {
let imageView = viewWithTag(cornerTag) as? LCornerView ?? LCornerView()
let gradientView = imageView.gradientView ?? LGradientView()
gradientView.setGradientColors(colors, start, end, locations)
imageView.setupGradient(gradientView: gradientView)
remakeConstraints(view: imageView)
}
/// 阴影 当前以及其frame相等的父视图不能做切除操作,否则会把阴影切掉。可同时使用func borderWidth(_ width: CGFloat, borderColor: UIColor? = nil)
/// - Parameters:
/// - color: 阴影颜色
/// - opacity: 阴影透明度 0-1 默认1不透明
/// - shadowRadius: 阴影的圆角半径
/// - offset: 阴影偏移量
/// - fillColor: 阴影视图背景填充色 默认白色
func shadowColor(_ color: UIColor, opacity: CGFloat = 1, shadowRadius: CGFloat, offset: CGSize? = nil, fillColor: UIColor? = nil) {
let shadowView = viewWithTag(cornerTag) as? LCornerView ?? LCornerView()
shadowView.setupShadow(shadowColor: color, opacity, shadowRadius, offset, fillColor: fillColor)
remakeConstraints(view: shadowView)
}
private func remakeConstraints(view: UIView) {
view.tag = cornerTag
if !subviews.contains(view) { insertSubview(view, at: 0) }
view.snp.remakeConstraints { make in
make.edges.equalToSuperview()
}
}