Search code examples
iosobjective-ccore-graphics

Draw VU meter using Core Graphics in iOS


I'm trying to draw a somewhat similar image to this, using Core Graphics:

enter image description here

I was able to draw the main arc, but I am not able to understand, how to divide arc into parts/draw graduation on arc? My current code to draw the arc is:

[path addArcWithCenter:point radius:radius startAngle:DEGREES_TO_RADIANS(specific_start_angle) endAngle:DEGREES_TO_RADIANS(specific_end_angle) clockwise:NO];

I tried using strokeWithBlendMode but I am facing problem with position of graduations or ticks.


Solution

  • Teja's solution will work fine for you, but it does require that you calculate your own start and end points for the graduations.

    I suggest you create a function that let's you draw the graduations at a given angle of the arc, that will calculate the start and end points of the graduations, given a length.

    static inline void drawGraduation(CGPoint center, CGFloat radius, CGFloat angle, CGFloat length, CGFloat width, CGColorRef color) {
    
        CGContextRef c = UIGraphicsGetCurrentContext();
    
        CGFloat radius2 = radius+length; // The radius of the end points of the graduations
        CGPoint p1 = (CGPoint){cos(angle)*radius+center.x, sin(angle)*radius+center.y}; // the start point of the graduation
        CGPoint p2 = (CGPoint){cos(angle)*radius2+center.x, sin(angle)*radius2+center.y}; // the end point of the graduation
    
        CGContextMoveToPoint(c, p1.x, p1.y);
        CGContextAddLineToPoint(c, p2.x, p2.y);
    
        CGContextSetStrokeColorWithColor(c, color);
        CGContextSetLineWidth(c, width);
    
        CGContextStrokePath(c);
    }
    

    You can then call this in a for loop (or however you want to do it) when you draw your arc for the main scale of your VU meter. You can also easily customise the color, width and length of given graduations at given intervals (for example, this code gives a thicker & longer red line every 5 graduations).

    - (void)drawRect:(CGRect)rect {
    
        CGRect r = self.bounds;
    
        CGFloat startAngle = -M_PI*0.2; // start angle of the main arc
        CGFloat endAngle = -M_PI*0.8; // end angle of the main arc
    
        NSUInteger numberOfGraduations = 16;
        CGPoint center = (CGPoint){r.size.width*0.5, r.size.height*0.5}; // center of arc
        CGFloat radius = (r.size.width*0.5)-20; // radius of arc
    
        CGFloat maxGraduationWidth = 1.5; // the maximum graduation width
        CGFloat maxGraduationWidthAngle = maxGraduationWidth/radius; // the maximum graduation width angle (used to prevent the graduations from being stroked outside of the main arc)
    
        // draw graduations
        CGFloat deltaArc = (endAngle-startAngle+maxGraduationWidthAngle)/(numberOfGraduations-1); // the change in angle of the arc
        CGFloat startArc = startAngle-(maxGraduationWidthAngle*0.5); // the starting angle of the arc
    
        for (int i = 0; i < numberOfGraduations; i++) {
            if (i % 5 == 0) {
                drawGraduation(center, radius, startArc+(i*deltaArc), 14, 1.5, [UIColor redColor].CGColor); // red graduation every 5 graduations.
            } else {
                drawGraduation(center, radius, startArc+(i*deltaArc), 10, 1, [UIColor blackColor].CGColor);
            }
    
        }
    
        // draw main arc
        UIBezierPath* mainArc = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:NO];
        [[UIColor blackColor] setStroke];
        mainArc.lineWidth = 2;
        [mainArc stroke];
    
    }
    

    Output

    enter image description here


    Full project: https://github.com/hamishknight/VU-Meter-Arc