iOS: Draw a text at a point in a rectangle
Asked Answered
S

4

5

I'm using core text API to draw text on UI. (I don't know other methods, because most Google search results lead me to core text api).

I have read some tutorials online about using core text API. In those tutorial, I see step by step to config from matrix, attribute string to framesetter ... but none of them explain carefully meaning of each step, so I cannot modify by myself.

Below code is the function draw text on screen with (x,y) is the location which I want to draw. This piece of code describe clearly step by step do draw on screen. Nevertheless I don't know where to put x and y parameters, so text will start to draw at this point in rectangle.

// draw text on screen.
+ (void)drawText:(CGContextRef)context bound:(CGRect)rect text:(NSString *)text x:(float)x y:(float) y color:(UIColor *)color size:(float)textSize{
    CGContextSaveGState(context);
    // always remember to reset the text matrix before drawing.
    // otherwise the result will be unpredictable like using uninitialize memory
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0f, -1.0f));
    // Flip the coordinate system. because core Text uses different coordinate system with UI Kit
    CGContextTranslateCTM(context, 0, rect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // step 1: prepare attribute string
    NSDictionary *attributes;
    attributes = @{
            (NSString *) kCTFontAttributeName            : [UIFont fontWithName:@"Helvetica" size:textSize],
            (NSString *) kCTForegroundColorAttributeName : color
    };

    NSAttributedString *str = [[NSAttributedString alloc]
                               initWithString:text attributes:attributes];

    CFAttributedStringRef attrString = (__bridge CFAttributedStringRef)str;

    // step 2: create CTFFrameSetter
    CTFramesetterRef framesetter =
    CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);

    // step 3. create CGPath
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, rect);

    // step 4. get the frame
    // use the CGPath and CTFrameSetter to create CTFrame. then drawn it in currently context
    CTFrameRef frame = CTFramesetterCreateFrame
            (framesetter, CFRangeMake(0, 0), path, NULL);

    CTFrameDraw(frame, context);

    // step 5. release resource
    //CFRelease(attrString);
    CFRelease(framesetter);
    CFRelease(frame);
    CFRelease(path);

    CGContextRestoreGState(context);
}

Moreover, I see there are function: CGContextSetTextPosition This seem what I need, but where should I put in above code. I have tried some, but no succeed. Please tell me how to fix this.

Thanks :)

Subvene answered 11/8, 2014 at 18:19 Comment(2)
yes. i just simply want to draw text at one location. nothing special. I think this method is complicated too, but when search google about: "ios draw text ..." I always have results about Core Text api that so complicate. thanks :)Subvene
please see my answer below. if you put the code or invoke it from drawRect: it simple draw attributed text on the view.Khabarovsk
K
13

This is not CoreText solution but after your comment under your question I assume this would be a proper answer.

Basic text drawing on a view.

.h

#import <UIKit/UIKit.h>

@interface aView : UIView
@end

.m

#import "aView.h"

@implementation aView

- (void)drawText:(CGFloat)xPosition yPosition:(CGFloat)yPosition canvasWidth:(CGFloat)canvasWidth canvasHeight:(CGFloat)canvasHeight
{
    //Draw Text 
    CGRect textRect = CGRectMake(xPosition, yPosition, canvasWidth, canvasHeight);
    NSMutableParagraphStyle* textStyle = NSMutableParagraphStyle.defaultParagraphStyle.mutableCopy;
    textStyle.alignment = NSTextAlignmentLeft;

    NSDictionary* textFontAttributes = @{NSFontAttributeName: [UIFont fontWithName: @"Helvetica" size: 12], NSForegroundColorAttributeName: UIColor.redColor, NSParagraphStyleAttributeName: textStyle};

    [@"Hello, World!" drawInRect: textRect withAttributes: textFontAttributes];
}


- (void)drawRect:(CGRect)rect {
    [self drawText:0 yPosition:0 canvasWidth:200 canvasHeight:150];
}

@end
Khabarovsk answered 11/8, 2014 at 19:2 Comment(2)
thanks. your post helps me so much !!!! I don't know why most of search results point to core text api. that most of steps, I cannot understand clearly. thanks:)Subvene
@Subvene you are welcome, if you don't mind your question title should be edited. thank you.Khabarovsk
M
3

Starting with IOS 7 you can have the str render itself into the current context. To do this import UIKit. This will extend the defenition of NSAttributedString with drawAtPoint: method. So for example you might do something like this:

[str drawAtPoint:CGPointMake(x,y)]

For more info about this and other related methods please see the following: https://developer.apple.com/library/ios/documentation/uikit/reference/NSAttributedString_UIKit_Additions/Reference/Reference.html#//apple_ref/occ/instm/NSAttributedString/drawAtPoint:

Mastin answered 11/8, 2014 at 18:34 Comment(0)
R
1

On draw text selected view you can used UIView touch events as below. create global variable for Touch start point and Touch end Point as below

CGPoint startingPoint;
CGPoint endingPoint;

And then draw text or line as below

#pragma mark- Touch Event
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   UITouch *touch = [[event allTouches] anyObject];
   startingPoint = [touch locationInView:self];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{

}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
   UITouch *touch = [touches anyObject];
   endingPoint = [touch locationInView:self];
   [self makeLineLayer:self.layer lineFromPointA:startingPoint 
   toPointB:endingPoint];
}

-(void)makeLineLayer:(CALayer *)layer lineFromPointA:(CGPoint)pointA 
          toPointB:(CGPoint)pointB
{
   CAShapeLayer *line = [CAShapeLayer layer];
   UIBezierPath *linePath=[UIBezierPath bezierPath];
   [linePath moveToPoint: pointA];
   [linePath addLineToPoint:pointB];
   line.path=linePath.CGPath;
   line.fillColor = nil;
   line.opacity = 2.0;
   line.lineWidth = 4.0;
   line.strokeColor = [UIColor redColor].CGColor;
   [layer addSublayer:line];
}
Riband answered 10/2, 2020 at 7:37 Comment(0)
S
0

Although this thread is a bit old, but this is my work around for future visitors. This work requires a little bit modification and it starts moving the text to position (x, y).

You have to change step 3 like this:

// step 3. create CGPath
CGMutablePathRef path = CGPathCreateMutable();
CGRect newRect = rect;
newRect.origin.x = x;
newRect.origin.y = -y;
CGPathAddRect(path, NULL, newRect);

and the coordinate (x, y) will start working. The minus sign is due to the different axis of CoreTextAPI.

Also changed the TextMatrix to below for getting the correct transformation or it was giving a flipped Y-Axis text:

// always remember to reset the text matrix before drawing.
// otherwise the result will be unpredictable like using uninitialize memory
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0f, 1.0f));

Attached screenshot from IB (Designable):

Screenshot

Hope it helps!

Sidwel answered 22/6, 2017 at 8:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.