swiftuichartsshapesdraw

Cross-hatched (Striped) BarMark in SwiftUI


I would like to have BarMark with cross-hatched pattern by using Charts in a SwiftUI project. My data type is (Date, Double).

I created a chart like this:

struct ContentView: View {
    struct  ChartData: Hashable {
        let date: Date
        let value: Double
    }
    
    @State private var model: [ChartData] = [
        ChartData(date: Calendar.current.date(byAdding: .month, value: -1, to: Date())!, value: 70),
        ChartData(date: Date(), value: 170),
        ChartData(date: Calendar.current.date(byAdding: .month, value: 1, to: Date())!, value: 45)
    ]
    
    private let barWidth: CGFloat = 40.0
    
    var body: some View {
            Chart {
                ForEach(model, id: \.self) { data in
                    BarMark(
                        x: .value("Date", data.date),
                        yStart: .value("Start", 0),
                        yEnd: .value("End", data.value),
                        width: .fixed(barWidth)
                    )
                    .foregroundStyle(.yellow)
                }
            }
            .chartXAxis(.hidden)
            .chartYAxis(.hidden)
            .padding()
    }
}

This is the desired BarMark:

Cross-hatched bar mark

Thanks in advance


Solution

  • You can use an ImagePaint as the foregroundStyle of the bar marks.

    You just need to have a pattern image like this:

    enter image description here

    You can create such an image by drawing like this:

    let pattern = Image(size: .init(width: 40, height: 30)) { gc in
        gc.fill(Path(CGRect(x: 0, y: 0, width: 40, height: 30)), with: .color(.orange.opacity(0.3)))
        
        func stroke(from: CGPoint, to: CGPoint) {
            gc.stroke(Path { p in
                p.move(to: from)
                p.addLine(to: to)
            }, with: .color(.orange), style: .init(lineWidth: 10, lineCap: .square))
        }
        
        stroke(from: .init(x: 40, y: 0), to: .init(x: 0, y: 30))
        stroke(from: .init(x: 40, y: -30), to: .zero)
        stroke(from: .init(x: 40, y: 30), to: .init(x: 0, y: 60))
    }
    

    or create one in Photoshop or whatever software you have.

    Then you can just do

    .foregroundStyle(.image(pattern))