3

I need to draw many lines (in the range of 50-75) in a screen and currently use the below function for it which works fine. After drawing 40-50 of these lines with the below code, the application slows down noticeably in my iPhone 4. To optimize I tried removing the line shadow it helped but still app wasn't running as smooth as I wanted. I need to optimize the below code, my first idea is to replace the cashapelayers with .png line images. But the new method should support line rotation, different length line with same width, and animation of drawing (it seems a lot for me to do with cgaffinetransforms). Any ideas that can help me?

+ (CAShapeLayer *) drawLineOnView:(UIView *) view BetweenPoint1:(CGPoint) point1 Point2:(CGPoint) point2 lineWidth:(CGFloat)lineWidth lineColor:(UIColor *) color Animated:(BOOL) animed
{
    CAShapeLayer *lineShape = [CAShapeLayer layer];
    CGMutablePathRef linePath = nil;
    linePath = CGPathCreateMutable();

    //lineShape.opacity = 0.6;
    lineShape.lineWidth = lineWidth;
    lineShape.lineCap = kCALineCapRound;

    if(color==nil) color = [UIColor orangeColor]; //Default value
    lineShape.shadowColor = [color CGColor];
    lineShape.shadowOpacity = 1.0;
    lineShape.shadowRadius = 5.0;
    lineShape.strokeColor = [color CGColor];

    CGPathMoveToPoint(linePath, NULL, point1.x, point1.y);
    CGPathAddLineToPoint(linePath, NULL, point2.x, point2.y);

    if(animed)
    {
        CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        pathAnimation.duration = 1.0;
        pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
        pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
        [lineShape addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
    }

    lineShape.path = linePath;
    CGPathRelease(linePath);
    [view.layer addSublayer:lineShape];

    return lineShape;
}

PARTLY SOLVED (Optimization never ends)

I broke down my line drawing function into 2 complementary parts and draw multiple lines into the one shape layer instead of creating new layers each time. It works much better if not great. Here is the updated code:

+ (CAShapeLayer *) createNewShapeLayerForDrawingLinesOnView:(UIView *) view lineWidth:(CGFloat)lineWidth lineColor:(UIColor *) color
{
    CAShapeLayer *lineShape = [CAShapeLayer layer];

    //lineShape.opacity = 0.6;
    lineShape.lineWidth = lineWidth;
    lineShape.lineCap = kCALineCapRound;

    if(color==nil) color = [UIColor orangeColor]; //Default value
    lineShape.shadowColor = [color CGColor];
    lineShape.shadowOpacity = 1.0;
    lineShape.shadowRadius = 5.0;
    lineShape.strokeColor = [color CGColor];

    [view.layer addSublayer:lineShape];
    return lineShape;
}

+ (void) addNewLineToShapeLayer:(CAShapeLayer *) shapeLayer BetweenPoint1:(CGPoint) point1 Point2:(CGPoint) point2
{
    CGMutablePathRef combinedPath = CGPathCreateMutableCopy(shapeLayer.path);

    CGMutablePathRef linePath = CGPathCreateMutable();

    CGPathMoveToPoint(linePath, NULL, point1.x, point1.y);
    CGPathAddLineToPoint(linePath, NULL, point2.x, point2.y);

    //No paths drawn before
    if(combinedPath == NULL)
    {
        combinedPath = linePath;
    }
    else
    {
        CGPathAddPath(combinedPath, NULL, linePath);
    }

    shapeLayer.path = combinedPath;
    CGPathRelease(linePath);
}
guenis
  • 2,520
  • 2
  • 25
  • 37

2 Answers2

4

While I understand the want to create multiple layers, it will be much more efficient to draw all the lines into one and to manage animations and rotations of a list of lines from there. You can do this in a shape layer with a combined path like(missing code marked with "..."):

CGMutablePathRef combinedPath = CGPathCreateMutableCopy(path.CGPath);
for(...)
    CGPathAddPath(combinedPath, NULL, [self makeNewPathFrom:...].CGPath);
myLayer.path = combinedPath;

Even faster, you can draw the list of lines directly onto the graphics context of a CALayer. This example for a view's drawRect: method is untested but should give you an idea:

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetLineWidth(context, lineWidth);
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); //red

for(MyLine *line in lines)
{
    CGContextMoveToPoint(context, point1.x, point1.y);
    CGContextAddLineToPoint(context, point2.x, point2.y);
}

If you need further optimization, you should look into OpenGL.

Peter DeWeese
  • 18,141
  • 8
  • 79
  • 101
1

You definitely do not want 75 layers, each with their own line. Are you sure you can't draw a single, more complex, path in a single layer?

Mark Bernstein
  • 2,090
  • 18
  • 23