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);
}];
}];
Related
I'm trying to create an infinitely tracing path that draws and the back of it disappears. However, the code I have makes the path disappear after a single iteration. In fact, the layer disappears before the path's disappear animation completes. It seems like, instead, it disappears when the drawing animation completes.
Furthermore, once it disappears, it doesn't show up again for another few seconds (perhaps however long it would take for the animation to repeat) before starting again.
Two questions:
Why is the layer disappearing before the disappearing animation completes?
How can I make the animation play continuously without disappearing?
Thank you in advance! Here's my code:
CGRect boundingFrame = CGRectMake(CGRectGetMidX(self.bounds) - 48.0, CGRectGetMidY(self.bounds) - 48.0, 96.0, 96.0);
_pathPoints = #[
[NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(boundingFrame), CGRectGetMinY(boundingFrame) + squareSideWidth/2.0)],
[NSValue valueWithCGPoint:CGPointMake(CGRectGetMinX(boundingFrame) + squareSideWidth/2.0, CGRectGetMidY(boundingFrame))],
[NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(boundingFrame), CGRectGetMaxY(boundingFrame) - squareSideWidth/2.0)],
[NSValue valueWithCGPoint:CGPointMake(CGRectGetMaxX(boundingFrame) - squareSideWidth/2.0, CGRectGetMidY(boundingFrame))]
];
CAShapeLayer *line = [CAShapeLayer layer];
line.path = pathForPoints(_pathPoints).CGPath;
line.lineCap = kCALineCapRound;
line.strokeColor = color.CGColor;
line.fillColor = [UIColor clearColor].CGColor;
line.strokeEnd = 0.0;
[self.layer addSublayer:line];
NSMutableArray<CABasicAnimation *> *animations = [NSMutableArray new];
for (int i = 0; i < [_pathPoints count]; i++) {
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.beginTime = index;
drawAnimation.duration = 1.0;
drawAnimation.fromValue = #(strokeEndForIndex(points, index - 1));
drawAnimation.toValue = #(strokeEndForIndex(points, index));
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
drawAnimation.additive = YES;
drawAnimation.removedOnCompletion = NO;
CABasicAnimation *eraseAnimation = [CABasicAnimation animationWithKeyPath:#"strokeStart"];
eraseAnimation.beginTime = index + 0.75;
eraseAnimation.duration = 1.0;
eraseAnimation.fromValue = #(strokeEndForIndex(points, index - 1));
eraseAnimation.toValue = #(strokeEndForIndex(points, index));
eraseAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
eraseAnimation.additive = YES;
eraseAnimation.removedOnCompletion = NO;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = #[drawAnimation, eraseAnimation];
group.duration = 2.0 * [animations count];
group.repeatCount = INFINITY;
group.removedOnCompletion = NO;
[line addAnimation:group forKey:#"line"];
}
Helper functions, for reference:
static CGFloat lengthOfPathWithPoints(NSArray<NSValue *> *points)
{
CGFloat totalDist = 0;
for (int i = 0; i < [points count]; i++) {
CGFloat xDist = [points[i % [points count]] CGPointValue].x - [points[(i + 1) % [points count]] CGPointValue].x;
CGFloat yDist = [points[i % [points count]] CGPointValue].y - [points[(i + 1) % [points count]] CGPointValue].y;
totalDist += sqrt(pow(xDist, 2) + pow(yDist, 2));
}
return totalDist;
}
static CGFloat strokeEndForIndex(NSArray<NSValue *> *points, int index)
{
// If it's the last index, just return 1 early
if (index == [points count] - 1) {
return 1.0;
}
// In the case where index = -1 (as it does for the initial erase animation fromValue), just return 0
if (index < 0) {
return 0.0;
}
CGFloat totalDist = 0;
for (int i = 0; i < index + 1; i++) {
CGFloat xDist = [points[i % [points count]] CGPointValue].x - [points[(i + 1) % [points count]] CGPointValue].x;
CGFloat yDist = [points[i % [points count]] CGPointValue].y - [points[(i + 1) % [points count]] CGPointValue].y;
totalDist += sqrt(pow(xDist, 2) + pow(yDist, 2));
}
return totalDist/lengthOfPathWithPoints(points);
}
static UIBezierPath *pathForPoints(NSArray<NSValue *> *points)
{
UIBezierPath *path = [UIBezierPath new];
[path moveToPoint:[[points firstObject] CGPointValue]];
for (int i = 0; i < [points count]; i++) {
[path addLineToPoint:[points[(i + 1) % 4] CGPointValue]];
}
return path;
}
Increase your duration property value and change your repeatCount = Float(Int.max) to something like this.
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.
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]];
}
}
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! :)
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