如何从SwiftUI的Image中获取Path对象?
如何将SF Symbols系统图标转为Shape可用的Path?
你的代码思路方向是对的,但SwiftUI的Image并没有path(in:)方法,没法直接从Image对象获取路径。要实现把SF Symbols系统图标转为Shape可用的Path,需要借助UIKit的UIImage来提取图标的矢量轮廓,具体实现如下:
完整实现代码
import SwiftUI // 保留你自定义的DrawInfo结构体,可根据需求调整属性 struct DrawInfo { var iconSize: CGFloat } struct SystemIconShape: Shape { var drawInfo: DrawInfo var iconName: String init(_ drawInfo: DrawInfo, _ iconName: String) { self.drawInfo = drawInfo self.iconName = iconName } func path(in rect: CGRect) -> Path { // 加载SF Symbols图标,配置尺寸适配当前矩形 let config = UIImage.SymbolConfiguration(pointSize: min(rect.width, rect.height)) guard let uiImage = UIImage(systemName: iconName, withConfiguration: config) else { return Path() // 图标加载失败时返回空路径 } // 将UIImage转为CGPath guard let cgPath = uiImage.cgPath(in: rect) else { return Path() } return Path(cgPath) } } // 给UIImage添加扩展,提取SF Symbols的矢量路径 extension UIImage { func cgPath(in rect: CGRect) -> CGPath? { // 创建矢量渲染上下文 let renderer = UIGraphicsImageRenderer(size: rect.size) let image = renderer.image { context in self.draw(in: rect) } guard let cgImage = image.cgImage else { return nil } let ciImage = CIImage(cgImage: cgImage) // 使用CoreImage滤镜提取图标轮廓 let edgeFilter = CIFilter(name: "CIColorEdges")! edgeFilter.setValue(ciImage, forKey: kCIInputImageKey) edgeFilter.setValue(1.0, forKey: kCIInputIntensityKey) guard let edgeImage = edgeFilter.outputImage else { return nil } let ciContext = CIContext() guard let edgeCGImage = ciContext.createCGImage(edgeImage, from: edgeImage.extent) else { return nil } // 通过位图分析生成路径 let width = edgeCGImage.width let height = edgeCGImage.height let bytesPerRow = width * 4 guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return nil } context.draw(edgeCGImage, in: CGRect(x: 0, y: 0, width: width, height: height)) guard let data = context.data else { return nil } let pixels = data.bindMemory(to: UInt32.self, capacity: width * height) var path = Path() var isTrackingPath = false // 扫描像素点生成轮廓路径 for y in 0..<height { for x in 0..<width { let pixel = pixels[y * width + x] let alpha = CGFloat((pixel >> 24) & 0xFF) / 255.0 if alpha > 0.5 { let pointX = CGFloat(x) / CGFloat(width) * rect.width + rect.minX let pointY = CGFloat(height - y) / CGFloat(height) * rect.height + rect.minY let point = CGPoint(x: pointX, y: pointY) if !isTrackingPath { path.move(to: point) isTrackingPath = true } else { path.addLine(to: point) } } else { isTrackingPath = false } } isTrackingPath = false } return path.cgPath } } // 使用示例 struct IconShapeDemoView: View { var body: some View { SystemIconShape(DrawInfo(iconSize: 24), "heart.fill") .fill(Color.red) .frame(width: 100, height: 100) } }
关键说明
- 图标加载:通过
UIImage(systemName:withConfiguration:)加载SF Symbols图标,配置尺寸适配目标矩形,保证图标在Shape内显示正常。 - 路径提取:先用CoreImage边缘检测滤镜提取图标轮廓,再通过像素扫描生成
CGPath,最后转为SwiftUI的Path。 - 错误处理:添加了图标加载失败、路径生成失败时的空路径返回逻辑,避免运行时崩溃。
内容的提问来源于stack exchange,提问作者Keith
相关产品推荐
相关产品推荐

