i want to show a grapph/bar chart in iphone how do i do this without custom API;s
Write your own. It's not easy, I'm in the process of doing the same thing right now. Here's how I'm doing it:
First, ignore any desire you may have to try using a UIScrollView if you want to allow zooming. It's totally not worth it.
Second, create something like a GraphElement
protocol. I have a hierarchy that looks something like this:
- GraphElement
- GraphPathElement
- GraphDataElement
- GraphDataSupplierElement
GraphElement contains the basic necessary methods for a graph element, including how to draw, a maximum width (for zooming in), whether a point is within that element (for touches) and the standard touchBegan
, touchMoved
, and touchEnded
functions.
GraphPathElement
contains a CGPath
, a line color and width, a fill color and a drawing mode. Whenever it's prompted to draw, it simply adds the path to the context, sets the colors and line width, and draws the path with the given drawing mode.
GraphDataElement
, as a subclass of GraphPathElement
, takes in a set of data in x-y coordinates, a graph type (bar or line), a frame, and a bounds. The frame is the actual size of the created output CGPath. The bounds is the size of the data in input coordinates. Essentially, it lets you scale the data to the screen size.
It creates a graph by first calculating an affine transform to transform the bounds to the frame, then it loops through each point and adds it as data to a path, applying that transform to the point before adding it. How it adds data depends on the type.
If it's a bar graph, it creates a rectangle of width 0, origin at (x,frame.size.height-y), and height=y. Then it "insets" the graph by -3 pixels horizontally, and adds that to the path.
If it's a line graph, it's much simpler. It just moves to the first point, then for each other point, it adds a line to that point, adds a circle in a rect around that point, then moves back to that point to go on to the next point.
GraphDataSupplierElement
is the interface to my database that actually contains all the data. It determines what kind of graph it should be, formats the data into the required type for GraphDataElement
, and passes it on, with the color to use for that particular graph.
For me, the x-axis is time, and is represented as NSTimeInterval
s. The GraphDataSupplierElement
contains a minDate
and maxDate
so that a GraphDateElement
can draw the x-axis labels as required.
Once all this is done, you need to create the actual graph. You can go about it several ways. One option is to keep all the elements in an NSArray
and whenever drawRect:
is called, loop through each element and draw it. Another option is to create a CALayer
for each element, and use the GraphPathElement
as the CALayer
's delegate. Or you could make GraphPathElement
extend from CALayer
directly. It's up to you on this one. I haven't gotten as far as trying CALayer
s yet, I'm still stuck in the simple NSArray
stage. I may move to CALayer
s at some point, once I'm satisfied with how everything looks.
So, all in all, the idea is that you create the graph as one or many CGPath
s beforehand, and just draw that when you need to draw the graph, rather than trying to actually parse data whenever you get a drawRect:
call.
Scaling can be done by keeping the source data in your GraphDataElement
, and just change the frame so that the scaling of the bounds to the frame creates a CGPath
wider than the screen, or whatever your needs are. I basically re-implemented my own pinch-zoom for my Graph UIView
subclass that only scales horizontally, by changing its transform, then on completion, get the current frame, reset the transform to identity, set the frame to the saved value, and set the frame of all of the GraphElement
s to the new frame as well, to make them scale. Then just call [self setNeedsDisplay]
to draw.
Anyway, that's a bit ramble-ish, but it's an outline of how I made it happen. If you have more specific questions, feel free to comment.