SwiftUI自定义Shape绘制坐标网格时文本无法显示的问题
问题原因与解决方案
问题根源
你在Shape结构体的path(in:)方法里调用NSAttributedString.draw(at:)是无效的。因为Shape的核心作用是生成路径(Path),SwiftUI会单独渲染这个路径,而draw(at:)是直接操作Core Graphics上下文的方法,这个上下文并不属于Shape的渲染流程,所以文本不会被绘制出来。
正确实现方式
把网格绘制和文本标签分开处理:用Shape专门负责网格线条,再用SwiftUI的View组合网格和文本标签,通过ZStack叠加显示。
1. 单独定义网格Shape
struct CoordinatesGrid: Shape { func path(in rect: CGRect) -> Path { var path = Path() let dx = rect.width / 10.0 let dy = rect.height / 10.0 for i in 0 ... 10 { // 绘制竖线 path.move(to: CGPoint(x: CGFloat(i) * dx, y: 0)) path.addLine(to: CGPoint(x: CGFloat(i) * dx, y: rect.maxY)) // 绘制横线 path.move(to: CGPoint(x: 0, y: CGFloat(i) * dy)) path.addLine(to: CGPoint(x: rect.maxX, y: CGFloat(i) * dy)) } return path } }
2. 创建带标签的组合View
struct GridWithLabels: View { var body: some View { GeometryReader { proxy in ZStack { // 渲染网格 CoordinatesGrid() .stroke(Color.black, lineWidth: 1) // 添加X轴标签 ForEach(0...10, id: \.self) { i in let xPos = CGFloat(i) * proxy.size.width / 10.0 Text("\(String(format: "%.1f", xPos))") .font(.system(size: 10, design: .monospaced)) // 对应原代码的Courier等宽字体 .position(x: xPos, y: 15) // 调整Y坐标避免文本贴边 } // 添加Y轴标签 ForEach(0...10, id: \.self) { i in let yPos = CGFloat(i) * proxy.size.height / 10.0 Text("\(String(format: "%.1f", yPos))") .font(.system(size: 10, design: .monospaced)) .position(x: 25, y: yPos) // 调整X坐标避免文本贴边 } } } } }
替代方案(Core Graphics直接绘制)
如果想通过Core Graphics同时绘制网格和文本,可以用UIViewRepresentable封装一个UIView:
struct GridView: UIViewRepresentable { func makeUIView(context: Context) -> UIView { GridDrawingView() } func updateUIView(_ uiView: UIView, context: Context) {} } class GridDrawingView: UIView { override func draw(_ rect: CGRect) { guard let context = UIGraphicsGetCurrentContext() else { return } // 绘制网格 context.setStrokeColor(UIColor.black.cgColor) context.setLineWidth(1) let dx = rect.width / 10.0 let dy = rect.height / 10.0 for i in 0 ... 10 { // 竖线 context.move(to: CGPoint(x: CGFloat(i)*dx, y: 0)) context.addLine(to: CGPoint(x: CGFloat(i)*dx, y: rect.maxY)) // 横线 context.move(to: CGPoint(x: 0, y: CGFloat(i)*dy)) context.addLine(to: CGPoint(x: rect.maxX, y: CGFloat(i)*dy)) } context.strokePath() // 绘制文本 let font = UIFont(name: "Courier", size: 10) let attributes: [NSAttributedString.Key: Any] = [.font: font ?? UIFont.systemFont(ofSize: 10)] for i in 0 ... 10 { let xPos = CGFloat(i)*dx let xText = NSAttributedString(string: "\(String(format: "%.1f", xPos))", attributes: attributes) xText.draw(at: CGPoint(x: xPos, y: 0)) let yPos = CGFloat(i)*dy let yText = NSAttributedString(string: "\(String(format: "%.1f", yPos))", attributes: attributes) yText.draw(at: CGPoint(x: 0, y: yPos)) } } }
内容的提问来源于stack exchange,提问作者Maqrol
相关产品推荐
相关产品推荐

