You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 11:05:10