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! :)
Related
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 have a class with a square made of bezierPaths, you can change the size of the square by pressing near a side and drag it.
Right now i can drag and release one time, the next time i get the following error:
2015-01-20 14:20:36.356 BezierPaths[196:3437] *** -[CALayer convertPoint:fromLayer:]: message sent to deallocated instance 0x1742251c0
Why doesnt it work the 2nd time?
This is the class:
#implementation EditBezierView
{
UIBezierPath *path;
UIImage *incrementalImage;
CGPoint pts[9];
NSMutableArray *pathPoints;
NSMutableArray *controlPoints;
BOOL pressedPoint;
int index;
float xStart;
float yStart;
int closestIndex;
CGPoint closestComparePoint;
bool moveControl;
int counter;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor clearColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:1.0];
pathPoints = [NSMutableArray array];
controlPoints = [NSMutableArray array];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setMultipleTouchEnabled:NO];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
counter = 0;
}
return self;
}
- (void)initValues
{
[pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake(47.0, 130.0)]];
[pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake(14.0, 230.0)]];
[controlPoints addObject:[NSValue valueWithCGPoint:CGPointMake(26.0, 150.0)]];
[pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake(22.0, 320.0)]];
[controlPoints addObject:[NSValue valueWithCGPoint:CGPointMake(9.0, 270.0)]];
[pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake(90.0, 420.0)]];
[controlPoints addObject:[NSValue valueWithCGPoint:CGPointMake(24.0, 380.0)]];
[pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake(230.0, 420.0)]];
[controlPoints addObject:[NSValue valueWithCGPoint:CGPointMake(170.0, 460.0)]];
[pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake(300.0, 320.0)]];
[controlPoints addObject:[NSValue valueWithCGPoint:CGPointMake(280.0, 390)]];
[pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake(307.0, 220.0)]];
[controlPoints addObject:[NSValue valueWithCGPoint:CGPointMake(315.0, 260.0)]];
[pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake(270.0, 130.0)]];
[controlPoints addObject:[NSValue valueWithCGPoint:CGPointMake(300.0, 160.0)]];
[self drawPaths];
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
[[UIColor blueColor] setStroke];
[path stroke];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
counter++;
NSLog(#"%d", counter);
[self.layer.sublayers makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
UITouch *currentTouch = [touches anyObject];
CGPoint startPt = [currentTouch locationInView:self];
if(moveControl)
{
float xOs = [(NSValue*)[controlPoints objectAtIndex:closestIndex] CGPointValue].x;
float yOs = [(NSValue*)[controlPoints objectAtIndex:closestIndex] CGPointValue].y;
[controlPoints replaceObjectAtIndex:closestIndex withObject:[NSValue valueWithCGPoint:CGPointMake(xOs - (xStart-startPt.x), yOs - (yStart - startPt.y))]];
}
else
{
float xOs = [(NSValue*)[pathPoints objectAtIndex:closestIndex] CGPointValue].x;
float yOs = [(NSValue*)[pathPoints objectAtIndex:closestIndex] CGPointValue].y;
[pathPoints replaceObjectAtIndex:closestIndex withObject:[NSValue valueWithCGPoint:CGPointMake(xOs - (xStart-startPt.x), yOs - (yStart - startPt.y))]];
}
[self drawPaths];
xStart = startPt.x;
yStart = startPt.y;
}
- (void)drawPaths
{
path = [UIBezierPath bezierPath];
for(int i = 1; i < [pathPoints count]; i++)
{
//Draw path
[path moveToPoint:((NSValue*)[pathPoints objectAtIndex:i-1]).CGPointValue];
[path addQuadCurveToPoint:((NSValue*)[pathPoints objectAtIndex:i]).CGPointValue controlPoint:((NSValue*)[controlPoints objectAtIndex:i-1]).CGPointValue];
//Draw points
if(i == 1)
{
[self addrectangle:((NSValue*)[pathPoints objectAtIndex:i-1]).CGPointValue];
[self addrectangle:((NSValue*)[pathPoints objectAtIndex:i]).CGPointValue];
[self addQuadrectangle:((NSValue*)[controlPoints objectAtIndex:i-1]).CGPointValue];
}
else
{
[self addrectangle:((NSValue*)[pathPoints objectAtIndex:i]).CGPointValue];
[self addQuadrectangle:((NSValue*)[controlPoints objectAtIndex:i-1]).CGPointValue];
}
}
[self setNeedsDisplay];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *currentTouch = [touches anyObject];
CGPoint startPt = [currentTouch locationInView:self];
closestComparePoint = startPt;
xStart = startPt.x;
yStart = startPt.y;
[self findNearestPoint];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//[self drawPath];
}
-(void)findNearestPoint
{
int pathClosestIndex = 0;
CGFloat xDist;
CGFloat yDist;
CGFloat Controldistance = 12401240;
CGFloat Pathdistance = 12401240;
for(int i = 0; i < [controlPoints count]; i++)
{
xDist = ABS([(NSValue*)[controlPoints objectAtIndex:i] CGPointValue].x - closestComparePoint.x);
yDist = ABS([(NSValue*)[controlPoints objectAtIndex:i] CGPointValue].y - closestComparePoint.y);
if(sqrt((xDist * xDist) + (yDist * yDist)) < Controldistance)
{
Controldistance = sqrt((xDist * xDist) + (yDist * yDist));
closestIndex = i;
}
}
for(int i = 0; i < [pathPoints count]; i++)
{
xDist = ABS([(NSValue*)[pathPoints objectAtIndex:i] CGPointValue].x - closestComparePoint.x);
yDist = ABS([(NSValue*)[pathPoints objectAtIndex:i] CGPointValue].y - closestComparePoint.y);
if(sqrt((xDist * xDist) + (yDist * yDist)) < Pathdistance)
{
Pathdistance = sqrt((xDist * xDist) + (yDist * yDist));
pathClosestIndex = i;
}
}
if(Pathdistance < Controldistance)
{
moveControl = NO;
closestIndex = pathClosestIndex;
}
else
{
moveControl = YES;
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
pressedPoint = NO;
}
-(void)removeRectangles{
[self.layer.sublayers makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
}
-(void)addrectangle:(CGPoint) center{
int offset = 8;
UIBezierPath *path2 = [UIBezierPath bezierPath];
[path2 moveToPoint:CGPointMake(center.x - offset, center.y - offset)];
// # Entry Point with 10px white frame
[path2 moveToPoint:CGPointMake(center.x - offset, center.y - offset)];
// # Keeping 10px frame with iPhone's 450 on y-axis
[path2 addLineToPoint:CGPointMake(center.x - offset, center.y + offset)];
// path2 Substracting 10px for frame on x-axis, and moving 450 in y-axis
[path2 addLineToPoint:CGPointMake(center.x + offset, center.y + offset)];
// # Moving up to 1st step 10px line, 310px on the x-axis
[path2 addLineToPoint:CGPointMake(center.x + offset, center.y - offset)];
// # Back to entry point
[path2 addLineToPoint:CGPointMake(center.x - offset, center.y - offset)];
// 4th Create a CAShapeLayer that uses that UIBezierPath:
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [path2 CGPath];
shapeLayer.strokeColor = [[UIColor blueColor] CGColor];
shapeLayer.lineWidth = 1.0;
shapeLayer.fillColor = [[UIColor clearColor] CGColor];
// 5th add shapeLayer as sublayer inside layer view
[self.layer addSublayer:shapeLayer];
}
-(void)addQuadrectangle:(CGPoint) center{
int offset = 4;
UIBezierPath *path2 = [UIBezierPath bezierPath];
[path2 moveToPoint:CGPointMake(center.x - offset, center.y - offset)];
// # Entry Point with 10px white frame
[path2 moveToPoint:CGPointMake(center.x - offset, center.y - offset)];
// # Keeping 10px frame with iPhone's 450 on y-axis
[path2 addLineToPoint:CGPointMake(center.x - offset, center.y + offset)];
// path2 Substracting 10px for frame on x-axis, and moving 450 in y-axis
[path2 addLineToPoint:CGPointMake(center.x + offset, center.y + offset)];
// # Moving up to 1st step 10px line, 310px on the x-axis
[path2 addLineToPoint:CGPointMake(center.x + offset, center.y - offset)];
// # Back to entry point
[path2 addLineToPoint:CGPointMake(center.x - offset, center.y - offset)];
// 4th Create a CAShapeLayer that uses that UIBezierPath:
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [path2 CGPath];
shapeLayer.strokeColor = [[UIColor redColor] CGColor];
shapeLayer.lineWidth = 1.0;
shapeLayer.fillColor = [[UIColor clearColor] CGColor];
// 5th add shapeLayer as sublayer inside layer view
[self.layer addSublayer:shapeLayer];
}
-(int)findOrigoInX
{
int closestToX = [(NSValue *)[pathPoints firstObject] CGPointValue].x;
for(int i = 1; i < [pathPoints count]; i++)
{
if([(NSValue *)[pathPoints objectAtIndex:i] CGPointValue].x < closestToX)
{
closestToX = [(NSValue *)[pathPoints objectAtIndex:i] CGPointValue].x;
}
}
return closestToX;
}
-(int)findOrigoInY
{
int closestToY = [(NSValue *)[pathPoints firstObject] CGPointValue].y;
for(int i = 1; i < [pathPoints count]; i++)
{
if([(NSValue *)[pathPoints objectAtIndex:i] CGPointValue].y > closestToY)
{
closestToY = [(NSValue *)[pathPoints objectAtIndex:i] CGPointValue].y;
}
}
return closestToY;
}
-(NSMutableArray*)generateKoordinateArray
{
NSLog(#"count in pathPoints : %lu", (unsigned long)[pathPoints count]);
NSMutableArray *storeArray = [[NSMutableArray alloc] init];
BezierCsvFormat *storeBezier = [[BezierCsvFormat alloc] init];
NSString *stringValueX;
NSString *stringValueY;
int xOffset = [self findOrigoInX];
NSLog(#"x offset : %d", xOffset);
int yOffset = [self findOrigoInY];
NSLog(#"y offset : %d", yOffset);
for(int i = 1; i < [pathPoints count]; i++)
{
storeBezier = [[BezierCsvFormat alloc] init];
stringValueX = [NSString stringWithFormat:#"%d", (int)[(NSValue *)[pathPoints objectAtIndex:i-1] CGPointValue].x - xOffset];
stringValueY = [NSString stringWithFormat:#"%d", ABS((int)[(NSValue *)[pathPoints objectAtIndex:i-1] CGPointValue].y - yOffset)];
[storeBezier setp0:[NSString stringWithFormat:#"%#,%#",stringValueX, stringValueY]];
stringValueX = [NSString stringWithFormat:#"%d", (int)[(NSValue *)[controlPoints objectAtIndex:i-1] CGPointValue].x - xOffset];
int multiplicator = 1;
if((int)[(NSValue *)[controlPoints objectAtIndex:i-1] CGPointValue].y > yOffset)
{
multiplicator = multiplicator * -1;
}
stringValueY = [NSString stringWithFormat:#"%d", multiplicator * ABS((int)[(NSValue *)[controlPoints objectAtIndex:i-1] CGPointValue].y - yOffset)];
[storeBezier setp1:[NSString stringWithFormat:#"%#,%#",stringValueX, stringValueY]];
stringValueX = [NSString stringWithFormat:#"%d", (int)[(NSValue *)[pathPoints objectAtIndex:i] CGPointValue].x - xOffset];
stringValueY = [NSString stringWithFormat:#"%d", ABS((int)[(NSValue *)[pathPoints objectAtIndex:i] CGPointValue].y - yOffset)];
[storeBezier setp2:[NSString stringWithFormat:#"%#,%#",stringValueX, stringValueY]];
[storeArray addObject:storeBezier];
}
NSLog(#"count in storeArray : %lu", (unsigned long)[storeArray count]);
return storeArray;
}
- (NSMutableArray*)printCoordinateSystem
{
NSMutableArray *tempArray = [self generateKoordinateArray];
return tempArray;
NSLog(#"count in tempArray : %lu", (unsigned long)[tempArray count]);
for(int i = 0; i < [tempArray count]; i++)
{
NSLog(#"p0 : %# p1 : %# p2 : %#", [(BezierCsvFormat*)[tempArray objectAtIndex:i] getp0], [(BezierCsvFormat*)[tempArray objectAtIndex:i] getp1], [(BezierCsvFormat*)[tempArray objectAtIndex:i] getp2]);
}
}
-(void)resetView{
#try {
[path removeAllPoints];
pathPoints = [NSMutableArray array];
[self.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
[self.layer.sublayers makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
[self setNeedsDisplay];
}
#catch (NSException *exception) {
[self resetView];
}
#finally {
}
}
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
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);
}];
}];
I am working on an alarm app in which I have to show an analogue clock. The functionality is to set time by clicking on the circumference of the circle displayed as the clock dial.
I have succesfully done this too. But the issue comes at 6 O'Clock or 30 min. The angle comes out to be nan for that which makes my smooth clock totally abrupt.
Here is the code.
myPath1 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(5, 5, 244, 244)];
[myPath1 closePath];
[myPath1 setLineWidth:20];
// Draw a shape Layer with Triangle Path
CAShapeLayer *shapeLayer1 = [CAShapeLayer layer];
shapeLayer1.fillColor = [UIColor clearColor].CGColor;
shapeLayer1.strokeColor = [UIColor clearColor].CGColor;
shapeLayer1.lineWidth = 20;
shapeLayer1.path = myPath1.CGPath;
// Add it to your view
[circleView.layer addSublayer:shapeLayer1];
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:circleView];
if([self containsPointOnStroke:currentPoint onPath:myPath1])
{
[self calculateAngle:currentPoint];
}
}
- (BOOL)containsPointOnStroke:(CGPoint)point onPath:(UIBezierPath *)path
{
CGPathRef cgPath = path.CGPath;
CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(cgPath, NULL, 10,
kCGLineCapRound, kCGLineJoinRound, 1);
BOOL pointIsNearPath = CGPathContainsPoint(strokedPath, NULL, point, NO);
CGPathRelease(strokedPath);
return pointIsNearPath;
}
//---- calculate angle from the point of touch taking center as the reference ----//
- (void) calculateAngle : (CGPoint)currentPoint
{
double distance = sqrt(pow((currentPoint.x - 127), 2.0) + pow((currentPoint.y - 5), 2.0));
float val = ((2 * 14884) - powf((distance), 2)) / (2 * 14884);
float angleVal = acosf(val) * 180/M_PI;
float timeval = angleVal;
//NSLog(#"angleVal : %f timeVal : %.2f", angleVal, timeval);
if (isnan(angleVal))
{
// if the value of the angle val is infinite
if(check == 0)
[hourBtn setTitle:#"06" forState:UIControlStateNormal];
else
[minBtn setTitle:#"30" forState:UIControlStateNormal];
}
else
{
if(check == 0)
{
if (currentPoint.x >= 127)
[hourBtn setTitle:[self getHour:timeval reverseTIme:NO] forState:UIControlStateNormal];
else
[hourBtn setTitle:[self getHour:timeval reverseTIme:YES] forState:UIControlStateNormal];
}
else
{
if (currentPoint.x >= 127)
[minBtn setTitle:[self getMin:timeval reverseTIme:NO] forState:UIControlStateNormal];
else
[minBtn setTitle:[self getMin:timeval reverseTIme:YES] forState:UIControlStateNormal];
}
}
[timeLbl setText:[NSString stringWithFormat:#"%02d:%02d",[hourBtn.titleLabel.text intValue], [minBtn.titleLabel.text intValue]]];
}
//---- get the hour time from the angle ----//
- (NSString *) getHour :(float) angle reverseTIme:(BOOL) reverseCheck
{
int minuteCount = angle * 2;
int quo = minuteCount / 60;
int temp = quo;
if(quo == 0)
quo = 12;
NSString *timeStr = [NSString stringWithFormat:#"%02d", quo];
quo = temp;
if (reverseCheck)
{
int revQuo = 11 - quo;
if(revQuo == 0)
revQuo = 12;
timeStr = [NSString stringWithFormat:#"%02d", revQuo];
}
return timeStr;
}
// get the min time from the angle
- (NSString *) getMin :(float) angle reverseTIme:(BOOL) reverseCheck
{
int minuteCount = angle;
int rem = minuteCount / 6;
NSString *timeStr = [NSString stringWithFormat:#"%02d", rem];
if (reverseCheck)
{
int revRem = 60 - rem;
if(revRem == 60)
revRem = 0;
timeStr = [NSString stringWithFormat:#"%02d", revRem];
}
//NSLog(#"timeStr : %#", timeStr);
return timeStr;
}