Draw text in circle overlay
Asked Answered
S

4

10

I'm trying to draw some circle overlays containing text on MKMapView. I have subclassed the MKCircleView, in which I put the following (based on this), but the text does not appear. The circles show up correctly. (Also tried the first response's solution, same result).

-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
   [super drawMapRect:mapRect zoomScale:zoomScale inContext:context];
   NSString * t= @"XXXXX\nXXXX" ;
   UIGraphicsPushContext(context);
   CGContextSaveGState(context); 
   [[UIColor redColor] set];
   CGRect overallCGRect = [self rectForMapRect:[self.overlay boundingMapRect]];
   NSLog(@"MKC :  %lf, %lf ----> %lf , %lf ", mapRect.origin.x ,mapRect.origin.y , overallCGRect.origin.x, overallCGRect.origin.y);
   [t drawInRect:overallCGRect withFont:[UIFont fontWithName:@"Arial" size:10.0] lineBreakMode:UILineBreakModeClip alignment:UITextAlignmentCenter];
   CGContextRestoreGState(context);
   UIGraphicsPopContext();
}

When debugging, I get values like these

MKC :  43253760.000000, 104071168.000000 ----> 1.776503 , 1.999245 
MKC :  43253760.000000, 104071168.000000 ----> -1.562442 , -2.043090

Are they normal ? What am I missing ?

Thanks.

Sewing answered 19/10, 2011 at 17:10 Comment(3)
Combining this with the answer below solved my question about how to draw the text. However my text is centered horizontally but vertically is drawn at the top of my MKCircleView. Is yours vertically and horizontally centered?Epperson
I was calculating the location of the text wrong. Hohum. :PEpperson
If anyone needs it, this is how i centered the text in the circle, considering the width and height of the text : CGSize size = [str sizeWithFont:[UIFont fontWithName:@"Arial" size:9] ]; CGPoint center = CGPointMake(circleRect.origin.x + circleRect.size.width /2,circleRect.origin.y + circleRect.size.height /2); CGPoint textstart = CGPointMake(center.x - size.width/2, center.y - size.height /2 );Sewing
B
12

I believe your code is working and the problem is that the text is not being scaled properly making it invisible.

Scale the font size based on the zoomScale using the MKRoadWidthAtZoomScale function:

[t drawInRect:overallCGRect withFont:[UIFont fontWithName:@"Arial" 
    size:(10.0 * MKRoadWidthAtZoomScale(zoomScale))] 
    lineBreakMode:UILineBreakModeClip alignment:UITextAlignmentCenter];

Also be sure to use a text color that's different from the underlying circle's color.

Note that using drawInRect will result in the text being restricted to inside the circle and may get truncated. If you want to always show all the text, you could use drawAtPoint instead.

Baryta answered 21/10, 2011 at 3:10 Comment(2)
Thanks a lot! It's working now :) I forgot to take into account the zoomlevel. I calculated the center of the circle and the point to begin from based on the text size and with drawAtPoint it shows up perfectly centered.Sewing
@Sewing Can you show the complete code on how and where you have used this?Spermatocyte
N
4

Combining the answers here and updating for IOS7:

-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
    [super drawMapRect:mapRect zoomScale:zoomScale inContext:context];

    UIGraphicsPushContext(context);
    CGContextSaveGState(context);
    [[UIColor blueColor] set];

    NSDictionary *fontAttributes = @{NSFontAttributeName:[UIFont systemFontOfSize:10.0f * MKRoadWidthAtZoomScale(zoomScale)]};
    CGSize size = [[[self overlay] title] sizeWithAttributes:fontAttributes];
    CGFloat height = ceilf(size.height);
    CGFloat width  = ceilf(size.width);

    CGRect circleRect = [self rectForMapRect:[self.overlay boundingMapRect]];
    CGPoint center = CGPointMake(circleRect.origin.x + circleRect.size.width /2, circleRect.origin.y + circleRect.size.height /2);
    CGPoint textstart = CGPointMake(center.x - width/2, center.y - height /2 );

    [[[self overlay] title] drawAtPoint:textstart withAttributes:fontAttributes];

    CGContextRestoreGState(context);
    UIGraphicsPopContext();
}
Nerveless answered 10/9, 2014 at 14:15 Comment(0)
L
2

It is most likely that your text is drawn to a rectangle which is not visible.

First thing I would do is try printing the values as %f instead of %lf, as those values look crazy. You should also print out the .size.width and .size.height for the two rects (mapRect and overallCGRect).

If that doesn't lead you to a sensible rectangle definition, then try defining a CGRect yourself like CGRectMake(0,0,100,20) and see if the text draws.

You can also try simply drawing a filled rectangle to the same overallCGRect that you are drawing your text into.

Ludwig answered 20/10, 2011 at 23:52 Comment(0)
I
0

Here is a Swift version for macOS - makes use of a custom MKOverlay and MKOverlayRenderer

    /// Usage
    /// Labels
    ///
    /// let label1 = makeLabel([CLLocationCoordinate2DMake(lat + self.dyW / 2.0, lon - self.dxW / 2.0),
    ///                        CLLocationCoordinate2DMake(lat + self.dyW / 2.0, lon + self.dxW / 2.0)])
    /// label1.isAbove = false
    /// label1.text = "\(yIndex+1), \(xIndex+1)"
    /// overlays.append(label1)
    /// mapView?.addOverlay(label1)
    ///
    /// let label2 = makeLabel([CLLocationCoordinate2DMake(lat - self.dyW / 2.0, lon - self.dxW / 2.0),
    ///                        CLLocationCoordinate2DMake(lat - self.dyW / 2.0, lon + self.dxW / 2.0)])
    /// label2.isAbove = true
    /// label2.text = "\((lat - self.dyW / 2.0).formatted(3)), \((lon - self.dxW / 2.0).formatted(3))"
    /// overlays.append(label2)
    /// mapView?.addOverlay(label2)
    
    class Label: MKPolyline {
        
        var text: String = "TEXT"
        var isAbove: Bool = true
        
        let textColor = NSColor.white.withAlphaComponent(0.8).cgColor
        
    }
    
    // Custom TextOverlay
    class LabelRenderer: MKOverlayRenderer {
          
        override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
            
            // Draw Text in here
    
            guard let label = overlay as? Label else {
                print("Error this is not a Label overlay !!")
                return
            }
            
            let rect = rect(for: overlay.boundingMapRect)
           
            let textSize = 400.0
            let margin = 100.0
            
            
            
            // Draw a border if required
            //let yPos        = label.isAbove ? rect.origin.y - textSize : rect.origin.y
            
            //let center      = CGPoint(x: rect.origin.x, y: yPos)
            //let drawRect = CGRect(origin: center, size: CGSize(width: width, height: textSize))
            //context.setFillColor(NSColor.cyan.withAlphaComponent(0.8).cgColor)
            //context.fill(drawRect)
            
            // Save the state because we have to flip things for drawing text
            context.saveGState()
            
            let textStart   = CGPoint(x: rect.origin.x + 100, y: label.isAbove ? (rect.origin.y + margin ) : (rect.origin.y) - textSize)
           
            context.setStrokeColor(label.textColor)
            context.setFillColor(label.textColor)
            
            context.setTextDrawingMode(.fill)
            let font = NSFont.systemFont(ofSize: textSize)
            let string = NSAttributedString(string: label.text, attributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: label.textColor])
            
            context.textPosition = textStart
            context.scaleBy(x: 1.0, y: -1.0)
            
            let line = CTLineCreateWithAttributedString(string)
            CTLineDraw(line, context)
            
            // Restore the state just incase there is some other drawing required
            context.restoreGState()
            
        }
    }
Invest answered 6/9, 2022 at 5:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.