iOS:如何使用Core Graphics实现可切片图像混合
用Core Graphics混合可切片框架图与背景图的实现方案
嘿,这个需求我之前做UI自定义的时候刚好碰到过,用Core Graphics完全能搞定,核心就是不能直接把两张图整张混合——那样会破坏可切片图像的9宫格结构,得按切片区域拆分处理,再重新拼接。下面给你详细说下实现思路和代码:
核心思路
可切片图像(比如常见的气泡、按钮背景)本质是遵循9宫格规则:4个角保持固定大小不拉伸,4条边只沿单一方向拉伸,中间区域可以自由拉伸。要保持混合后的图像仍可切片,就必须对两张图的对应9宫格区域分别混合,再把这些混合后的区域拼接成最终图像。
具体实现步骤
- 第一步:确定切片参数
先拿到框架图的UIEdgeInsets(也就是上、下、左、右的固定边距,比如框架图的左上角10pt区域是固定角,那edgeInsets就是UIEdgeInsets(top:10, left:10, bottom:10, right:10))。背景图如果也是可切片的,建议用相同的insets;如果不是,就按框架图的insets来划分区域。 - 第二步:创建绘制上下文
用UIGraphicsBeginImageContextWithOptions创建和最终图像尺寸一致的上下文,记得设置scale为屏幕scale,避免生成模糊的图像。 - 第三步:按9宫格区域逐一混合绘制
对每个区域,先绘制背景图的对应部分,再通过Core Graphics的混合模式绘制框架图的对应部分。混合模式可以根据需求选择(比如正常叠加、颜色相乘、提亮等)。 - 第四步:提取并清理
从上下文里取出最终图像,然后关闭上下文,避免内存泄漏。
Swift示例代码
func blendSlicableFrame(with backgroundImage: UIImage, frameImage: UIImage, edgeInsets: UIEdgeInsets) -> UIImage? { // 确定最终图像尺寸,这里假设背景图和框架图尺寸一致,可按需调整 let finalSize = backgroundImage.size // 创建上下文,false表示透明背景,scale用屏幕scale保证清晰度 UIGraphicsBeginImageContextWithOptions(finalSize, false, UIScreen.main.scale) // 确保上下文一定会被清理 defer { UIGraphicsEndImageContext() } guard let context = UIGraphicsGetCurrentContext() else { return nil } // 定义9宫格的所有区域 let topLeft = CGRect(x: 0, y: 0, width: edgeInsets.left, height: edgeInsets.top) let topMiddle = CGRect(x: edgeInsets.left, y: 0, width: finalSize.width - edgeInsets.left - edgeInsets.right, height: edgeInsets.top) let topRight = CGRect(x: finalSize.width - edgeInsets.right, y: 0, width: edgeInsets.right, height: edgeInsets.top) let leftMiddle = CGRect(x: 0, y: edgeInsets.top, width: edgeInsets.left, height: finalSize.height - edgeInsets.top - edgeInsets.bottom) let center = CGRect(x: edgeInsets.left, y: edgeInsets.top, width: finalSize.width - edgeInsets.left - edgeInsets.right, height: finalSize.height - edgeInsets.top - edgeInsets.bottom) let rightMiddle = CGRect(x: finalSize.width - edgeInsets.right, y: edgeInsets.top, width: edgeInsets.right, height: finalSize.height - edgeInsets.top - edgeInsets.bottom) let bottomLeft = CGRect(x: 0, y: finalSize.height - edgeInsets.bottom, width: edgeInsets.left, height: edgeInsets.bottom) let bottomMiddle = CGRect(x: edgeInsets.left, y: finalSize.height - edgeInsets.bottom, width: finalSize.width - edgeInsets.left - edgeInsets.right, height: edgeInsets.bottom) let bottomRight = CGRect(x: finalSize.width - edgeInsets.right, y: finalSize.height - edgeInsets.bottom, width: edgeInsets.right, height: edgeInsets.bottom) let allRegions = [topLeft, topMiddle, topRight, leftMiddle, center, rightMiddle, bottomLeft, bottomMiddle, bottomRight] for region in allRegions { // 先绘制背景图的当前区域 backgroundImage.draw(in: region) // 设置混合模式,这里用正常叠加,可替换为.multiply/.overlay等 context.setBlendMode(.normal) // 绘制框架图的当前区域 frameImage.draw(in: region) } // 从上下文提取最终图像 return UIGraphicsGetImageFromCurrentImageContext() }
关键注意事项
- 尺寸适配:如果背景图和框架图尺寸不同,先把背景图缩放到框架图的尺寸,或者按比例调整区域大小,确保每个9宫格区域对应准确。
- 混合模式选择:Core Graphics提供了十几种混合模式,比如
kCGBlendModeMultiply会让颜色叠加变暗,kCGBlendModeScreen会提亮颜色,你可以根据视觉需求测试调整。 - 性能优化:如果这个混合操作会频繁调用(比如列表里的每个cell都要生成),建议缓存混合后的图像,避免重复绘制消耗性能。
- 切片规则一致性:混合后的图像仍可按原框架图的
edgeInsets进行切片,只要你保持了每个区域的对应关系。
内容的提问来源于stack exchange,提问作者Daumantas Versockas
相关产品推荐
相关产品推荐

