EXC_BAD_ACCESS drawing graph - ios

I'm drawing graph and for that I use SHLineGraphView now after drawing one graph I need to remove that and dray again. in line
CGPathMoveToPoint(backgroundPath, NULL, _leftMarginToLeave, plot.xPoints[0].y);
I am getting get EXC_BAD_ACCESS Message.
this is my function. I add NSZombieEnabled YES in my scheme
- (void)drawPlot:(SHPlot *)plot {
NSDictionary *theme = plot.plotThemeAttributes;
CAShapeLayer *backgroundLayer = [CAShapeLayer layer];
backgroundLayer.frame = self.bounds;
backgroundLayer.fillColor = ((UIColor *)theme[kPlotFillColorKey]).CGColor;
backgroundLayer.backgroundColor = [UIColor clearColor].CGColor;
[backgroundLayer setStrokeColor:[UIColor clearColor].CGColor];
[backgroundLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
CGMutablePathRef backgroundPath = CGPathCreateMutable();
CAShapeLayer *circleLayer = [CAShapeLayer layer];
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 75, 75)];
circleLayer.path = circle.CGPath;
circleLayer.strokeColor = [UIColor orangeColor].CGColor;
circleLayer.fillColor = [UIColor whiteColor].CGColor;
circleLayer.shadowColor =[UIColor blackColor].CGColor;
circleLayer.lineWidth = 3.0;
[self.layer addSublayer:circleLayer];
CGMutablePathRef circlePath = CGPathCreateMutable();
CAShapeLayer *graphLayer = [CAShapeLayer layer];
graphLayer.frame = self.bounds;
graphLayer.fillColor = [UIColor clearColor].CGColor;
graphLayer.backgroundColor = [UIColor clearColor].CGColor;
[graphLayer setStrokeColor:((UIColor *)theme[kPlotStrokeColorKey]).CGColor];
[graphLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
CGMutablePathRef graphPath = CGPathCreateMutable();
double yRange = [_yAxisRange doubleValue];
double yIntervalValue = yRange / INTERVAL_COUNT;
[plot.plottingValues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *dic = (NSDictionary *)obj;
__block NSNumber *_key = nil;
__block NSNumber *_value = nil;
[dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
_key = (NSNumber *)key;
_value = (NSNumber *)obj;
}];
int xIndex = [self getIndexForValue:_key forPlot:plot];
double height = self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE;
double y = height - ((height / ([_yAxisRange doubleValue] + yIntervalValue)) * [_value doubleValue]);
(plot.xPoints[xIndex]).x = ceil((plot.xPoints[xIndex]).x);
(plot.xPoints[xIndex]).y = ceil(y);
}];
CGPathMoveToPoint(graphPath, NULL, _leftMarginToLeave, plot.xPoints[0].y);
CGPathMoveToPoint(backgroundPath, NULL, _leftMarginToLeave, plot.xPoints[0].y);
int count = _xAxisValues.count;
for(int i=0; i< count; i++){
CGPoint point = plot.xPoints[i];
CGPathAddLineToPoint(graphPath, NULL, point.x, point.y);
CGPathAddLineToPoint(backgroundPath, NULL, point.x, point.y);
CGPathAddEllipseInRect(circlePath, NULL, CGRectMake(point.x - 5, point.y - 5, 10, 10));
}
CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave + PLOT_WIDTH, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
CGPathCloseSubpath(backgroundPath);
backgroundLayer.path = backgroundPath;
graphLayer.path = graphPath;
circleLayer.path = circlePath;
CGPathRelease(backgroundPath);
CGPathRelease(circlePath);
CGPathRelease(graphPath);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration = 1;
animation.fromValue = #(0.0);
animation.toValue = #(1.0);
[graphLayer addAnimation:animation forKey:#"strokeEnd"];
backgroundLayer.zPosition = 0;
graphLayer.zPosition = 1;
circleLayer.zPosition = 2;
[self.layer addSublayer:graphLayer];
[self.layer addSublayer:circleLayer];
[self.layer addSublayer:backgroundLayer];
NSUInteger count2 = _xAxisValues.count;
for(int i=0; i< count2; i++){
CGPoint point = plot.xPoints[i];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.backgroundColor = [UIColor clearColor];
btn.tag = i;
btn.frame = CGRectMake(point.x - 10, point.y - 10, 40, 40);
[btn addTarget:self action:#selector(clicked:) forControlEvents:UIControlEventTouchUpInside];
objc_setAssociatedObject(btn, kAssociatedPlotObject, plot, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self addSubview:btn];
}
}
Thanks

Related

Having position problem in Core Text animation

I'm trying to convert following (link) swift code in objective c.
https://stackoverflow.com/a/49100870/797196
CTFontRef helveticaBold = CTFontCreateWithName( CFSTR("Helvetica-Bold"), 60.0, NULL);
NSString* unicodeString =#"Window Title";
CGColorRef color = [UIColor blueColor].CGColor;
NSNumber* underline = [NSNumber numberWithInt:
kCTUnderlineStyleSingle|kCTUnderlinePatternDot];
NSDictionary* attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)helveticaBold, (NSString*)kCTFontAttributeName,
color, (NSString*)kCTForegroundColorAttributeName,
underline, (NSString*)kCTUnderlineStyleAttributeName, nil];
NSAttributedString* stringToDraw = [[NSAttributedString alloc]
initWithString:unicodeString
attributes:attributesDict];
CGMutablePathRef letters = CGPathCreateMutable();
// CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)#"Avenir",20,nil);
CTLineRef lines = CTLineCreateWithAttributedString((CFAttributedStringRef)stringToDraw);
CFArrayRef runArray = CTLineGetGlyphRuns(lines);
CAShapeLayer *pathLayer = [CAShapeLayer layer];
for (int i = 0; i < CFArrayGetCount(runArray); i++) {
CTRunRef run = CFArrayGetValueAtIndex(runArray, i);
CFDictionaryRef dictRef = CTRunGetAttributes(run);
NSDictionary *dict = (__bridge NSDictionary*)dictRef;
CTFontRef runFont = (__bridge CTFontRef)(dict[(NSString*)kCTFontAttributeName]);
for (int runGlyphIndex=0; runGlyphIndex<CTRunGetGlyphCount(run); runGlyphIndex++) {
CGGlyph glyph;
CGPoint position = CGPointZero;
CTRunGetGlyphs(run, CFRangeMake(runGlyphIndex, 1), &glyph);
CTRunGetPositions(run, CFRangeMake(runGlyphIndex, 1), &position);
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, nil);
CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
CGPathAddPath(letters, &t, letter);
CAShapeLayer *pathL = [CAShapeLayer layer];
// pathLayer.frame = _pathLayerView.bounds;
CGRect rect = CGPathGetBoundingBox(letter);
rect.origin = position;
pathL.frame = rect;
pathL.geometryFlipped = true;
pathL.path = letter;
pathL.strokeColor = UIColor.blueColor.CGColor;
pathL.fillColor = UIColor.redColor.CGColor;
pathL.lineWidth = 4.0;
pathL.lineJoin = kCALineJoinBevel;
CABasicAnimation *stroke = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
stroke.fromValue = #(0.0);
stroke.duration = 3.0f;
stroke.repeatCount = 0;
[pathL addAnimation:stroke forKey:nil];
[pathLayer addSublayer:pathL];
}
}
[_pathLayerView.layer addSublayer:pathLayer];
But the problem is letters alignment is not in required position. "i","n","o" letters y position starts from the top. & its obvious, because I didn't use the position of the letters.
I find out the positions of the letters using CTRunGetPositions, but I dont know how to use it or where to use it.

Draw letter(text) with UIBezierPath single line

I am using this and this for reference. I am new for using UIBezierPath, and i want to draw path on character(letter) code i am using is as follow.
CGMutablePathRef letters = CGPathCreateMutable();
CTFontRef font = CTFontCreateWithName(CFSTR("Courier New"), 200.0f, NULL);
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:CFBridgingRelease(font), kCTFontAttributeName,nil];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:#"B"
attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
CFArrayRef runArray = CTLineGetGlyphRuns(line);
// for each RUN
for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++)
{
// Get FONT for this run
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
// for each GLYPH in run
for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++)
{
// get Glyph & Glyph-data
CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
CGGlyph glyph;
CGPoint position;
CTRunGetGlyphs(run, thisGlyphRange, &glyph);
CTRunGetPositions(run, thisGlyphRange, &position);
// Get PATH of outline
{
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
CGPathAddPath(letters, &t, letter);
CGPathRelease(letter);
}
}
}
CFRelease(line);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointZero];
[path appendPath:[UIBezierPath bezierPathWithCGPath:letters]];
CGPathRelease(letters);
CFRelease(font);
NSLog(#"==> %#",path);
// Creates layer
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.frame = drawingView.bounds;
pathLayer.bounds = CGPathGetBoundingBox(path.CGPath);
//pathLayer.backgroundColor = [[UIColor yellowColor] CGColor];
pathLayer.geometryFlipped = NO;
pathLayer.path = path.CGPath;
pathLayer.strokeColor = [[UIColor blackColor] CGColor];
pathLayer.fillColor = nil;
pathLayer.lineWidth = 3.0f;
pathLayer.lineJoin = kCALineJoinBevel;
[drawingView.layer addSublayer:pathLayer];
self.pathLayer = pathLayer;
// Add pointer
UIImage *penImage = [UIImage imageNamed:#"hand.png"];
CALayer *penLayer = [CALayer layer];
penLayer.contents = (id)penImage.CGImage;
penLayer.anchorPoint = CGPointMake(0.35f, 0.98f);
penLayer.frame = CGRectMake(0.0f, 0.0f, penImage.size.width, penImage.size.height);
[pathLayer addSublayer:penLayer];
self.penLayer = penLayer;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 5.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[self.pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
CAKeyframeAnimation *penAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
penAnimation.duration = 5.0;
penAnimation.path = self.pathLayer.path;
penAnimation.calculationMode = kCAAnimationPaced;
penAnimation.delegate = self;
[self.penLayer addAnimation:penAnimation forKey:#"position"];
This code draws latter latter in double line like the image below
instead i want to draw latter in single line like the image below
and the hand should draw the lines on centre of the font and not the border.

Adding a node inside didMoveToView does not appear, however having same code for multiple elements works as expected

I am confused as to why adding a shape node inside of didMoveToView give me no results, however when I call a function and perform the drawing there it renders fine.
Here is my code
-this example does not work
-(void)didMoveToView:(SKView *)view {
SKShapeNode *shape = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
CGPathMoveToPoint(myPath, NULL, self.sizeOfHorizontalSquares, self.sizeOfVerticalSquares);
CGRect bound = CGRectMake(50, 50, self.sizeOfHorizontalSquares,self.sizeOfVerticalSquares);
CGPathAddRect(myPath, NULL, bound);
shape.path = myPath;
[shape setFillColor:[UIColor colorWithRed:1 green:.6 blue:.3 alpha:1.0]];
[shape setStrokeColor:[UIColor colorWithRed:.1 green:.3 blue:.8 alpha:0.2]];
[shape setGlowWidth:1.0];
[self addChild:shape];
}
-Here is the code that works with an array
-(void)didMoveToView:(UIView *)view
{
self.sizeOfVerticalSquares = 20;
self.sizeOfHorizontalSquares = 20;
[self configureSquire];
[self renderSquiares];
}
-(void)configureSquiare
{
for(int i = 0; i < 6; i++)
{
for(int j = 0; j < 5; j++)
{
SKShapeNode *shape = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
//CGPathMoveToPoint(myPath, NULL, i*self.sizeOfHorizontalSquares, j*self.sizeOfVerticalSquares);
CGRect bound = CGRectMake(self.sizeOfHorizontalSquares, self.sizeOfVerticalSquares, self.sizeOfHorizontalSquares,self.sizeOfVerticalSquares);
CGPathAddRect(myPath, NULL, bound);
shape.path = myPath;
[self.squares addObject:shape];
}
}
}
-(void)renderSquiares
{
for(int i = 0; i < self.squares.count; i++)
{
[[self.squares objectAtIndex:i] setFillColor:(__bridge CGColorRef)[UIColor colorWithRed:1 green:.6 blue:.3 alpha:1.0]];
[[self.squares objectAtIndex:i] setStrokeColor:(__bridge CGColorRef)[UIColor colorWithRed:.1 green:.3 blue:.8 alpha:0.2]];
[[self.squares objectAtIndex:i] setGlowWidth:1];
[self addChild:[self.squares objectAtIndex:i]];
}
}

SHGraphLineView remove a plot

I'm using this class called SHLineGraphView and it's working good, but I have a problem that they didn't implemented a methods for removing a plot. Now I have to do it, but I can't quite figure the right way to do.
This is how they drawing the plot:
- (void)addPlot:(SHPlot *)newPlot;
{
if(nil == newPlot) {
return;
}
if(_plots == nil){
_plots = [NSMutableArray array];
}
[_plots addObject:newPlot];
}
- (void)drawPlot:(SHPlot *)plot {
NSDictionary *theme = plot.plotThemeAttributes;
//
CAShapeLayer *backgroundLayer = [CAShapeLayer layer];
backgroundLayer.frame = self.bounds;
backgroundLayer.fillColor = ((UIColor *)theme[kPlotFillColorKey]).CGColor;
backgroundLayer.backgroundColor = [UIColor clearColor].CGColor;
[backgroundLayer setStrokeColor:[UIColor clearColor].CGColor];
[backgroundLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
CGMutablePathRef backgroundPath = CGPathCreateMutable();
//
CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.frame = self.bounds;
circleLayer.fillColor = ((UIColor *)theme[kPlotPointFillColorKey]).CGColor;
circleLayer.backgroundColor = [UIColor clearColor].CGColor;
[circleLayer setStrokeColor:((UIColor *)theme[kPlotPointFillColorKey]).CGColor];
[circleLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
CGMutablePathRef circlePath = CGPathCreateMutable();
//
CAShapeLayer *graphLayer = [CAShapeLayer layer];
graphLayer.frame = self.bounds;
graphLayer.fillColor = [UIColor clearColor].CGColor;
graphLayer.backgroundColor = [UIColor clearColor].CGColor;
[graphLayer setStrokeColor:((UIColor *)theme[kPlotStrokeColorKey]).CGColor];
[graphLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
CGMutablePathRef graphPath = CGPathCreateMutable();
double yRange = [_yAxisRange doubleValue]; // this value will be in dollars
double yIntervalValue = yRange / INTERVAL_COUNT;
//logic to fill the graph path, ciricle path, background path.
[plot.plottingValues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *dic = (NSDictionary *)obj;
__block NSNumber *_key = nil;
__block NSNumber *_value = nil;
[dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
_key = (NSNumber *)key;
_value = (NSNumber *)obj;
}];
int xIndex = [self getIndexForValue:_key forPlot:plot];
//x value
double height = self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE;
double y = height - ((height / ([_yAxisRange doubleValue] + yIntervalValue)) * [_value doubleValue]);
(plot.xPoints[xIndex]).x = ceil((plot.xPoints[xIndex]).x);
(plot.xPoints[xIndex]).y = ceil(y);
}];
//move to initial point for path and background.
CGPathMoveToPoint(graphPath, NULL, _leftMarginToLeave, plot.xPoints[0].y);
CGPathMoveToPoint(backgroundPath, NULL, _leftMarginToLeave, plot.xPoints[0].y);
int count = _xAxisValues.count;
for(int i=0; i< count; i++){
CGPoint point = plot.xPoints[i];
CGPathAddLineToPoint(graphPath, NULL, point.x, point.y);
CGPathAddLineToPoint(backgroundPath, NULL, point.x, point.y);
CGPathAddEllipseInRect(circlePath, NULL, CGRectMake(point.x - 2.5, point.y - 2.5, 5, 5));
}
//move to initial point for path and background.
CGPathAddLineToPoint(graphPath, NULL, _leftMarginToLeave + PLOT_WIDTH, plot.xPoints[count -1].y);
CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave + PLOT_WIDTH, plot.xPoints[count - 1].y);
//additional points for background.
CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave + PLOT_WIDTH, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
CGPathCloseSubpath(backgroundPath);
backgroundLayer.path = backgroundPath;
graphLayer.path = graphPath;
circleLayer.path = circlePath;
//animation
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration = 1;
animation.fromValue = #(0.0);
animation.toValue = #(1.0);
[graphLayer addAnimation:animation forKey:#"strokeEnd"];
backgroundLayer.zPosition = 0;
graphLayer.zPosition = 1;
circleLayer.zPosition = 2;
[self.layer addSublayer:graphLayer];
[self.layer addSublayer:circleLayer];
[self.layer addSublayer:backgroundLayer];
NSUInteger count2 = _xAxisValues.count;
for(int i=0; i< count2; i++){
CGPoint point = plot.xPoints[i];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.backgroundColor = [UIColor clearColor];
btn.tag = i;
btn.frame = CGRectMake(point.x - 20, point.y - 20, 40, 40);
[btn addTarget:self action:#selector(clicked:) forControlEvents:UIControlEventTouchUpInside];
objc_setAssociatedObject(btn, kAssociatedPlotObject, plot, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self addSubview:btn];
}
}
And this is what I've been added, but just can't figure out how to actually remove the plot:
- (void)removePlot:(SHPlot *)plot
{
if (nil == plot) {
return;
}
if (_plots == nil) {
return;
}
[_plots removeObject:plot];
[self removePlotFromView:plot];
}
This is what you need to add in your SHLineGraphView.m file.
This method will remove all the plots added on your graphview.
- (void)removeAllPlots {
for (UIView *view in self.subviews) {
view.layer.sublayers = nil;
[view removeFromSuperview];
}
self.layer.sublayers = nil;
}
For removing a specific plot, first you need to give unique tag to your graphLayer, circleLayer and backgroundLayer.
You can do that by adding following code in your drawPlot: method.
[graphLayer setValue:[NSNumber numberWithInteger:[_plots indexOfObject:plot]] forKey:#"PLTag"];
[circleLayer.frame setValue:[NSNumber numberWithInteger:[_plots indexOfObject:plot]] forKey:#"PLTag"];
[backgroundLayer setValue:[NSNumber numberWithInteger:[_plots indexOfObject:plot]] forKey:#"PLTag"];
Then, add this method to remove specific plot:
- (void)removePlot:(SHPlot *)plot
{
if (plot == nil)
return;
else if (_plots == nil)
return;
for (CALayer *layer in self.layer.sublayers) {
if ([[layer valueForKey:#"PLTag"] isEqualToNumber:[NSNumber numberWithInteger:[_plots indexOfObject:plot]]]) {
[layer removeFromSuperlayer];
}
}
}
Hope it helps you.
So, by #Tejvansh Singh Chhabra's answer I managed to find a solution:
First of all, I've added these 2 new methods to SHGraphLineView.h:
/**
* this methods will remove a plot from the graph.
*
* #param plot the Plot that you want to remove from the Graph.
*/
- (void)removePlot:(SHPlot *)plot;
/**
* this methods will draw a new plot on the graph.
*
* #param plot the Plot that you want to draw on the Graph.
*/
- (void)drawNewPlot:(SHPlot*)plot;
And implement them on the main file:
- (void)removePlot:(SHPlot *)plot
{
if (plot == nil) {
return;
}
else if (_plots == nil) {
return;
}
for (CALayer *layer in [self.layer.sublayers copy])
{
if ([layer valueForKey:#"PLTag"] != nil)
{
NSString *tag = [layer valueForKey:#"PLTag"];
NSString *plotIndex = [NSString stringWithFormat:#"%ld", (long)plot.plotIndex];
if ([tag isEqualToString:plotIndex])
{
[layer removeFromSuperlayer];
}
}
}
[_plots removeObject:plot];
}
- (void)drawNewPlot:(SHPlot*)plot
{
if (nil == plot) {
return;
}
if (_plots == nil) {
_plots = [NSMutableArray array];
}
[_plots addObject:plot];
[self xPointWithoutDrawingForPlot:plot];
[self drawPlot:plot];
}
Now there is another method that create the x point for the plot, but without the labels and all the other this that are drawing with it when you first build the graph:
- (void)xPointWithoutDrawingForPlot:(SHPlot*)plot
{
int xIntervalCount = _xAxisValues.count;
double xIntervalInPx = PLOT_WIDTH / _xAxisValues.count;
//initialize actual x points values where the circle will be
plot.xPoints = calloc(sizeof(CGPoint), xIntervalCount);
for(int i=0; i < xIntervalCount; i++)
{
CGPoint currentLabelPoint = CGPointMake((xIntervalInPx * i) + _leftMarginToLeave, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
CGRect xLabelFrame = CGRectMake(currentLabelPoint.x , currentLabelPoint.y, xIntervalInPx, BOTTOM_MARGIN_TO_LEAVE);
plot.xPoints[i] = CGPointMake((int) xLabelFrame.origin.x + (xLabelFrame.size.width /2) , (int) 0);
}
}
Another important thing is to add a valueForKey to the CAShapeLayer objects of the plot at first draw, we need to add to this method:
- (void)drawPlot:(SHPlot *)plot;
These 3 lines:
[graphLayer setValue:[NSString stringWithFormat:#"%ld", (long)plot.plotIndex] forKey:#"PLTag"];
[circleLayer setValue:[NSString stringWithFormat:#"%ld", (long)plot.plotIndex] forKey:#"PLTag"];
[backgroundLayer setValue:[NSString stringWithFormat:#"%ld", (long)plot.plotIndex] forKey:#"PLTag"];
One last thing, to SHPlot class we need to add an NSInteger that will indicate the index of this specific plot, and you need to set it when you first build the plot
SHPlot.h
/**
* index for plot for identification
*/
#property (assign, nonatomic) NSInteger plotIndex;
Enjoy! :)

EXC_BAD_ACCESS in my code

Can be bad access from this part of code?
I'm trying to draw graph.And in my function i have BAD_ACCESS.I can't find what is wrong.
- (void)drawPlot:(SHPlot *)plot {
NSDictionary *theme = plot.plotThemeAttributes;
CAShapeLayer *backgroundLayer = [CAShapeLayer layer];
backgroundLayer.frame = self.bounds;
backgroundLayer.fillColor = ((UIColor *)theme[kPlotFillColorKey]).CGColor;
backgroundLayer.backgroundColor = [UIColor clearColor].CGColor;
[backgroundLayer setStrokeColor:[UIColor clearColor].CGColor];
[backgroundLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
CGMutablePathRef backgroundPath = CGPathCreateMutable();
CAShapeLayer *circleLayer = [CAShapeLayer layer];
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 75, 75)];
circleLayer.path = circle.CGPath;
circleLayer.strokeColor = [UIColor orangeColor].CGColor;
circleLayer.fillColor = [UIColor whiteColor].CGColor;
circleLayer.shadowColor =[UIColor blackColor].CGColor;
circleLayer.lineWidth = 3.0;
[self.layer addSublayer:circleLayer];
CGMutablePathRef circlePath = CGPathCreateMutable();
CAShapeLayer *graphLayer = [CAShapeLayer layer];
graphLayer.frame = self.bounds;
graphLayer.fillColor = [UIColor clearColor].CGColor;
graphLayer.backgroundColor = [UIColor clearColor].CGColor;
[graphLayer setStrokeColor:((UIColor *)theme[kPlotStrokeColorKey]).CGColor];
[graphLayer setLineWidth:((NSNumber *)theme[kPlotStrokeWidthKey]).intValue];
CGMutablePathRef graphPath = CGPathCreateMutable();
double yRange = [_yAxisRange doubleValue];
double yIntervalValue = yRange / INTERVAL_COUNT;
[plot.plottingValues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *dic = (NSDictionary *)obj;
[dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
int xIndex = [self getIndexForValue:key forPlot:plot];
double height = self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE;
double y = height - ((height / ([_yAxisRange doubleValue] + yIntervalValue)) * [obj doubleValue]);
(plot.xPoints[xIndex]).x = ceil((plot.xPoints[xIndex]).x);
(plot.xPoints[xIndex]).y = ceil(y);
}];
}];
CGPathMoveToPoint(graphPath, NULL, _leftMarginToLeave, plot.xPoints[0].y);
CGPathMoveToPoint(backgroundPath, NULL, _leftMarginToLeave, plot.xPoints[0].y);
int count = _xAxisValues.count;
for(int i=0; i< count; i++){
CGPoint point = plot.xPoints[i];
CGPathAddLineToPoint(graphPath, NULL, point.x, point.y);
CGPathAddLineToPoint(backgroundPath, NULL, point.x, point.y);
CGPathAddEllipseInRect(circlePath, NULL, CGRectMake(point.x - 5, point.y - 5, 10, 10));
}
CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave + PLOT_WIDTH, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
CGPathAddLineToPoint(backgroundPath, NULL, _leftMarginToLeave, self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE);
CGPathCloseSubpath(backgroundPath);
backgroundLayer.path = backgroundPath;
graphLayer.path = graphPath;
circleLayer.path = circlePath;
CGPathRelease(backgroundPath);
CGPathRelease(circlePath);
CGPathRelease(graphPath);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration = 1;
animation.fromValue = #(0.0);
animation.toValue = #(1.0);
[graphLayer addAnimation:animation forKey:#"strokeEnd"];
backgroundLayer.zPosition = 0;
graphLayer.zPosition = 1;
circleLayer.zPosition = 2;
[self.layer addSublayer:graphLayer];
[self.layer addSublayer:circleLayer];
[self.layer addSublayer:backgroundLayer];
NSUInteger count2 = _xAxisValues.count;
for(int i=0; i< count2; i++){
CGPoint point = plot.xPoints[i];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.backgroundColor = [UIColor clearColor];
btn.tag = i;
btn.frame = CGRectMake(point.x - 10, point.y - 10, 40, 40);
[btn addTarget:self action:#selector(clicked:) forControlEvents:UIControlEventTouchUpInside];
//objc_setAssociatedObject(btn, kAssociatedPlotObject, plot, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self addSubview:btn];
}
circleLayer = nil;
graphLayer = nil;
backgroundLayer = nil;
}
This is my full function I have bad access in line [self addSubview:btn];
Move your code into the enumerate block
[plot.plottingValues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *dic = (NSDictionary *)obj;
[dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
int xIndex = [self getIndexForValue:key forPlot:plot];
double height = self.bounds.size.height - BOTTOM_MARGIN_TO_LEAVE;
double y = height - ((height / ([_yAxisRange doubleValue] + yIntervalValue)) * [obj doubleValue]);
(plot.xPoints[xIndex]).x = ceil((plot.xPoints[xIndex]).x);
(plot.xPoints[xIndex]).y = ceil(y);
}];
}];

Resources