按CGPath遮罩UIImage后保存至相册失效问题求助
解决UIImage遮罩保存无效的问题
我来帮你排查下问题所在,你的预览代码能正常工作,但保存原图尺寸遮罩图片时失效,主要是这几个关键点没匹配上:
1. 裁剪规则不匹配
你在预览时给maskLayer设置了fillRule = .evenOdd,但Core Graphics默认的裁剪规则是非零缠绕规则(nonZero),这会导致裁剪效果和预览完全不一致,甚至看起来遮罩没生效。
2. 坐标系方向差异
UIKit的坐标系原点在左上角,但Core Graphics的原点在左下角。如果不做翻转处理,你的遮罩路径会上下颠倒,根本没覆盖在图片的正确位置上。
3. 图像上下文的分辨率问题
你用的UIGraphicsBeginImageContext不支持Retina屏幕的scale,不仅会导致保存的图片模糊,还可能因为scale不匹配让路径缩放计算出错。
修改后的完整代码
首先修正CGPath的缩放扩展(更准确的居中计算)
extension CGPath { func resized(to rect: CGRect) -> CGPath { let boundingBox = self.boundingBox let boxWidth = boundingBox.width let boxHeight = boundingBox.height guard boxWidth > 0, boxHeight > 0 else { return self } let boxAspect = boxWidth / boxHeight let rectAspect = rect.width / rect.height // 计算适配目标rect的缩放比例 let scaleFactor: CGFloat = boxAspect > rectAspect ? rect.width / boxWidth : rect.height / boxHeight // 计算平移量,保证缩放后的路径在rect中居中 let targetCenterX = rect.midX let targetCenterY = rect.midY let scaledBoxCenterX = boundingBox.midX * scaleFactor let scaledBoxCenterY = boundingBox.midY * scaleFactor // 因为先缩放再平移,所以平移量要除以缩放比例 let tx = (targetCenterX - scaledBoxCenterX) / scaleFactor let ty = (targetCenterY - scaledBoxCenterY) / scaleFactor var transform = CGAffineTransform.identity .scaledBy(x: scaleFactor, y: scaleFactor) .translatedBy(x: tx, y: ty) return copy(using: &transform)! } }
然后修改遮罩图片生成与保存函数
func getMaskedImage(path: CGPath) { guard let originalImage = UIImage(named: "1") else { print("无法加载原图") return } // 创建支持Retina的图像上下文,匹配原图的scale UIGraphicsBeginImageContextWithOptions(originalImage.size, false, originalImage.scale) defer { UIGraphicsEndImageContext() } // 确保上下文被正确释放 guard let context = UIGraphicsGetCurrentContext() else { return } // 翻转坐标系,对齐UIKit的Y轴方向 context.scaleBy(x: 1, y: -1) context.translateBy(x: 0, y: -originalImage.size.height) // 适配路径到原图尺寸 let targetRect = CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.height) let maskedPath = path.resized(to: targetRect) // 应用evenOdd裁剪规则,和预览保持一致 context.saveGState() context.addPath(maskedPath) context.setFillRule(.evenOdd) context.clip(using: .evenOdd) // 绘制原图到裁剪区域内 originalImage.draw(at: .zero) context.restoreGState() // 获取处理后的图片并保存到相册 if let maskedImage = UIGraphicsGetImageFromCurrentImageContext() { UIImageWriteToSavedPhotosAlbum(maskedImage, nil, nil, nil) } }
额外注意事项
别忘了在Info.plist中添加相册写入权限描述,否则保存时会崩溃:
<key>NSPhotoLibraryAddUsageDescription</key> <string>需要保存处理后的图片到你的相册</string>
内容的提问来源于stack exchange,提问作者SwiftArtery
相关产品推荐
相关产品推荐

