Question

This is the first time I've used core graphics and text so could be doing something very simple thats incorrect. I've been reading the documentation to try and see where i'm going wrong but can't see it so wonder if someone can help me spot the issue.

I'm trying to draw a simple little banner of information at the top of the screen but the text i want to appear in the banner is not drawn in the correct place.

In my view i'm creating my control as follows:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.banner = [[Banner alloc] initWithFrame:CGRectMake(0,20,768,200)];
    [self.view addSubview:self.banner];
}

The controls code is as follows:

#import <CoreText/CoreText.h>

#import "Banner.h"

@implementation Banner

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1];
    }
    return self;
}


// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Get the graphics context.
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Draw the banner border.
    CGContextSaveGState(context);

    UIColor *strokeColor = [UIColor blackColor];
    CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
    CGContextSetLineWidth(context, 5);
    CGContextStrokeRect(context, CGRectMake(2,2,self.frame.size.width -4,100));

    CGContextRestoreGState(context);


    // Setup the text label & value.
    NSString *addressLabelText = @"Address";
    NSString *addressValue = @"123 Letsby Avenue\nSome Place\nSome City\nSome County\nSome Postcode";

    UIFont *font = [UIFont fontWithName:@"Helvetica" size:14.0];

    CGSize labelSize = [self getBoundsForString:addressLabelText usingFont:font];
    CGSize valueSize = [self getBoundsForString:addressValue usingFont:font];

    // So I want to Draw the label at X:10 Y:10 and the Value At X:65 Y:10
    // So we get Something like:
    // =============================
    // || Address 123 letsby avenue
    // ||         Some Place
    // ||         Some City
    // ||         Some County
    // ||         Some Postcode
    // =============================

    CGRect labelBounds = CGRectMake(10,10,labelSize.width,labelSize.height);
    // Move the address value over the margin width(10) + the label width + some spacing(5)
    CGRect valueBounds = CGRectMake(10 + labelSize.width + 5, 10, valueSize.width, valueSize.height);

    [self drawString: addressLabelText withFont: font inRect:labelBounds usingContext:context];
    [self drawString: addressValue withFont: font inRect:valueBounds usingContext:context];

    // The text hasn't drawn where i thought it should. Draw a rect there to make sure i had the right bounds
    UIColor *rectFillColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.3];

    CGContextSaveGState(context);

    CGContextSetFillColorWithColor(context, [rectFillColor CGColor]);
    CGContextFillRect(context, labelBounds);
    CGContextFillRect(context, valueBounds);

    CGContextRestoreGState(context);
    //Hmm that is drawing in the right place. Whats going wrong with drawing the text.

    [self setNeedsDisplay];

}

-(void) drawString:(NSString*) string withFont:(UIFont*)font inRect:(CGRect)rect usingContext:(CGContextRef) context
{
    CGContextSaveGState(context);

    // flipping the coordinate system.
    CGContextTranslateCTM(context, 0, 200); // The control is rendered in a frame 200 high.
    CGContextScaleCTM(context, 1.0, -1.0);

    CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    // Define the path we are going to draw the text on.
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, rect);

    CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
    CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), (CFStringRef)string);

    // Create the framesetter with the attributed string.
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
    CFRelease(attrString);

    // Create a frame.
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);

    // Draw the specified frame in the given context.
    CTFrameDraw(frame, context);

    CGContextRestoreGState(context);

    CFRelease(frame);
    CFRelease(path);
    CFRelease(framesetter);
}

-(CGSize) getBoundsForString:(NSString *)string usingFont:(UIFont *)font
{
    return [string boundingRectWithSize:CGSizeMake(999,999)
                   options:NSStringDrawingUsesLineFragmentOrigin
                   attributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]
                   context:nil].size;
}


@end

The result of this is some blue rects drawn in the banner where i would expect the text to be but the text actually appears outside of the banner and I'm puzzled why.

https://www.dropbox.com/s/q2ap9tl4okaka49/Banner.png

Was it helpful?

Solution

I've found what seems to work for my code above... changing this:

-(void) drawString:(NSString*) string withFont:(UIFont*)font inRect:(CGRect)rect usingContext:(CGContextRef) context
{
    CGContextSaveGState(context);

    // flipping the coordinate system.
    CGContextTranslateCTM(context, 0, 200); // The control is rendered in a frame 200 high.
    CGContextScaleCTM(context, 1.0, -1.0);

    CGContextSetTextMatrix(context, CGAffineTransformIdentity);

to

-(void) drawString:(NSString*) string withFont:(UIFont*)font inRect:(CGRect)rect usingContext:(CGContextRef) context
{
    CGContextSaveGState(context);

    // flipping the coordinate system.
    CGContextTranslateCTM(context, 0, rect.size.height+(2*rect.origin.y));
    CGContextScaleCTM(context, 1.0, -1.0);

The line following line doesn't seem to change anything.

    CGContextSetTextMatrix(context, CGAffineTransformIdentity);

And changing this line:

    CGContextTranslateCTM(context, 0, 200);

to

    CGContextTranslateCTM(context, 0, rect.size.height+(2*rect.origin.y));

places the text correctly where i'd expect it to be.... using Xamarin and the equivalent c# I have to invert the result of the height + 2Y result :-S

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top