I have an custom UIView. I keep on the lines and points when the finger touches moves. Then I make points to CGPath with this method:
- (void)makeCGPath
{
CGMutablePathRef path = CGPathCreateMutable();
if (lines&& lines.count>0)
{
for (int i = 0; i < lines.count; i++)
{
CGMutablePathRef linePath = CGPathCreateMutable();
NSArray *tempArray = [lines objectAtIndex:i];
CGPoint p = [[tempArray objectAtIndex:0]CGPointValue];
CGPathMoveToPoint(linePath, NULL, p.x, p.y);
for (int j = 1; j < tempArray.count; j++)
{
p = [[tempArray objectAtIndex:j]CGPointValue];
CGPathAddLineToPoint(linePath, NULL, p.x, p.y);
}
CGPathAddPath(path, NULL, linePath);
CGPathRelease(linePath);
}
}
if (points&& points.count > 0)
{
CGMutablePathRef pointPath = CGPathCreateMutable();
CGPoint p = [[points objectAtIndex:0]CGPointValue];
CGPathMoveToPoint(pointPath, NULL, p.x, p.y);
for (int i = 1; i < points.count;i++ )
{
p = [[points objectAtIndex:i]CGPointValue];
CGPathAddLineToPoint(pointPath, NULL, p.x, p.y);
}
CGPathAddPath(path, NULL, pointPath);
CGPathRelease(pointPath);
}
drawPath = CGPathCreateCopy(path);
[self setNeedsDisplay];
[lines removeAllObjects];
[points removeAllObjects];
}
And my drawRect is look like this follow.
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 3);
[[UIColor blackColor]set];
CGContextAddPath(ctx, drawPath);
CGContextStrokePath(ctx);
}
It seems so strange that it displays only the last line on the screen. What's wrong with it?
How can I keep all of these paths on screen?
I have the exact same problem. It's not working on iOS 5.0 but it does work on iOS 5.1 and more. I'm still looking for the fix.
UPADTE:
I fixed the issue using UIBezierPath. You can draw all your paths using Besier and then convert it to CGPath this way:
UIBezierPath *path = [[UIBezierPath alloc] init];
// Add all the lines you need
[path addLineToPoint:p];
// Or points
[path moveToPoint:p];
// Append paths
[path appendPath:linePath];
// Make a CGPath from UIBezierPath
CGPath myCGPath = path.CGPath;
Try this code, it should work but I haven't tested it:
- (void)makeCGPath
{
UIBezierPath *path = [[UIBezierPath alloc] init];
if (lines && lines.count>0)
{
for (int i = 0; i < lines.count; i++)
{
UIBezierPath *linePath = [[UIBezierPath alloc] init];
NSArray *tempArray = [lines objectAtIndex:i];
CGPoint p = [[tempArray objectAtIndex:0]CGPointValue];
[linePath addLineToPoint:p];
for (int j = 1; j < tempArray.count; j++)
{
p = [[tempArray objectAtIndex:j]CGPointValue];
[linePath addLineToPoint:p];
}
[path appendPath:linePath];
}
}
if (points && points.count > 0)
{
UIBezierPath *pointPath = [[UIBezierPath alloc] init];
CGPoint p = [[points objectAtIndex:0]CGPointValue];
[pointPath moveToPoint:p];
for (int i = 1; i < points.count;i++ )
{
p = [[points objectAtIndex:i]CGPointValue];
[pointPath moveToPoint:p];
}
[path appendPath:pointPath];
}
drawPath = path.CGPath;
[self setNeedsDisplay];
[lines removeAllObjects];
[points removeAllObjects];
}
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 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 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 {
}
}
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);
}];
}];