Search code examples
pdfios-charts

Is it possible to use iOS Charts (Daniel Gindi) to generate graphs in pdf document on iOS


I am using iOS Charts (Daniel Gindi) to generate graphs in an iOS app and I want to be able to generate a PDF report with those graphs included in the body of the report. Can anyone explain how to go about doing this. Ideally I don't want to generate an image from the UIView that shows in the app because the size/resolution would not be suitable for the PDF document.

As I understand it there are a few options:

  1. use the graphics context for the pdf document to draw the graph on - it's not clear whether this would be possible when using the Charts library
    1. use a UIView somehow to generate the graph and generate a PDF image data from that, embed this image into the pdf report

It seems like option 1 is probably the preferred way to get best resolution/control - somewhat speculative - doing it this way means you should be able to specify the exact position and size and get the correct font sizes, line thicknesses, etc..

Using option 2 means you have to figure out the scaling between a UIView and the PDF page view and I am not sure how these would map to each other.

Can anyone provide any suggestions on the following:

  1. Is it possible to use Charts to generate graphs in a PDF document, and if so how?
  2. If not what other options are there, short of writing custom drawing code.

Solution

  • OK so here is what I have done

    Option 1: Using a UIView.layer to render on the PDF CGContect

    func drawLineGraph(x: CGFloat, y: CGFloat)->CGRect{
    
        let width = (pageSize.width - 2*kBorderInset - 2*kMarginInset)/2.0 - 50.0
    
        let renderingRect = CGRect(x: x, y: y + 50.0, width: width, height: 150.0)
    
        // Create a view for the Graph
        let graphController = LineChartController(rect: renderingRect, building: self.building)
    
        if let currentContext = UIGraphicsGetCurrentContext() {
    
            let frame = graphController.chartView.frame
            currentContext.saveGState()
            currentContext.translateBy(x:frame.origin.x, y:frame.origin.y);
            graphController.chartView.layer.render(in: currentContext)
            currentContext.restoreGState()
        }
        return renderingRect
    }
    

    The graphController is just an object that has essentially the same function as the usual parent ViewController that would contain the graph. Sets the graph parameters and data.

    Once that has been done the function below is called to render on the PDF page context.

    A bit of translation required to put the graphs in the correct position.

    Option 2: Drawing on the PDF Page CGContect

    And the solution is...ta da...

    func drawBarGraph(x: CGFloat, y: CGFloat)->CGRect{
    
            let width = (pageSize.width - 2*kBorderInset - 2*kMarginInset)/2.0 - 50.0
    
            let renderingRect = CGRect(x: x + width + 50, y: y + 50.0, width: width, height: 150.0)
    
            // Create a view for the Graph
            let graphController = BarChartController(rect: renderingRect, building: self.building)
    
            if let currentContext = UIGraphicsGetCurrentContext() {
    
                let frame = graphController.chartView.frame
                currentContext.saveGState()
                currentContext.translateBy(x:frame.origin.x, y:frame.origin.y)
    
                //graphController.chartView.layer.render(in: currentContext)
                graphController.chartView.draw(frame)
    
                currentContext.restoreGState()   
    
            }
            return renderingRect
        }
    

    Since the current context is set to the PDF page's context just call the charts draw() function directly passing the frame rectangle.

    What have I missed here, can it be this easy ?

    You can find a copy of the generated PDF here as well as sample code.