how to set CATextLayer text vertical position?

12k views Asked by At

I create UILabel and CATextLayer in my app using following code

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.textLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 90, 20, 20)];
    self.textLabel.text = @"1";
    self.textLabel.font = [UIFont systemFontOfSize:12];
    self.textLabel.backgroundColor = [UIColor redColor];
    [self.view addSubview:self.textLabel];

    self.textLayer = [CATextLayer layer];
    [self.textLayer setString:@"1"];
    [self.textLayer setFont:(__bridge CFTypeRef)([UIFont systemFontOfSize:12])];
    self.textLayer.backgroundColor = [UIColor greenColor].CGColor;
    self.textLayer.frame = CGRectMake(70, 90, 50, 20);
    [self.view.layer addSublayer:self.textLayer];
}

and the result is

enter image description here

The red one is UILabel, the green one is CALayer. I want to know how to vertical align the text inCALayer, as theUILabel` shows.

5

There are 5 answers

0
Suraj On

As per CATextLayer's documentation, there is no option to vertically align a text. The only option is to reposition the text layer so as to align well with the label. Here's the code to achieve the same

CGFloat offsetY = 0;
UIFont *textFont = [UIFont systemFontOfSize:14.0f];
//if system version is grater than 6
if(([[[UIDevice currentDevice] systemVersion] compare:@"6" options:NSNumericSearch] == NSOrderedDescending)){
    offsetY = (textFont.capHeight - textFont.xHeight);
}

self.textLayer = [CATextLayer layer];
self.textLayer.backgroundColor = [UIColor greenColor].CGColor;
self.textLayer.frame = CGRectMake(70, 90+offsetY, 50, 20);
[self.view.layer addSublayer:self.textLayer];
[self.textLayer setContentsScale:[UIScreen mainScreen].scale];
[self.textLayer setForegroundColor: [UIColor blackColor].CGColor];
[self.textLayer setString:@"1"];
[self.textLayer setFont:(__bridge CFTypeRef)(textFont)];
[self.textLayer setFontSize:14.0f];

The result is

enter image description here

3
Verticon On

Swift 3 version of @iamktothed's answer:

class VerticallyCenteredTextLayer : CATextLayer {

    // REF: http://lists.apple.com/archives/quartz-dev/2008/Aug/msg00016.html
    // CREDIT: David Hoerl - https://github.com/dhoerl
    // USAGE: To fix the vertical alignment issue that currently exists within the CATextLayer class.

    override func draw(in ctx: CGContext) {
        let fontSize = self.fontSize
        let height = self.bounds.size.height
        let deltaY = (height-fontSize)/2 - fontSize/10

        ctx.saveGState()
        ctx.translateBy(x: 0.0, y: deltaY)
        super.draw(in: ctx)
        ctx.restoreGState()
    }
}
1
iamktothed On

The correct answer is found here in Objective-C and works for iOS. It works by subclassing the CATextLayer and overriding the drawInContext function.

However, I've made some improvements to the code, as shown below, using David Hoerl's code as a basis. The changes come solely in recalculating the vertical position of the text.

Here is the code for Swift users:

class LCTextLayer : CATextLayer {

// REF: http://lists.apple.com/archives/quartz-dev/2008/Aug/msg00016.html
// CREDIT: David Hoerl - https://github.com/dhoerl 
// USAGE: To fix the vertical alignment issue that currently exists within the CATextLayer class. Change made to the yDiff calculation.

override init!() {
    super.init()
}

override init!(layer: AnyObject!) {
    super.init(layer: layer)
}

required init(coder aDecoder: NSCoder) {
    super.init(layer: aDecoder)
}

override func drawInContext(ctx: CGContext!) {
    let height = self.bounds.size.height
    let fontSize = self.fontSize
    let yDiff = (height-fontSize)/2 - fontSize/10

    CGContextSaveGState(ctx)
    CGContextTranslateCTM(ctx, 0.0, yDiff)
    super.drawInContext(ctx)
    CGContextRestoreGState(ctx)
}
}
1
Peter Lapisu On
{
    CATextLayer *label = [[CATextLayer alloc] init];
    [label setFont:@"Helvetica-Bold"];
    [label setFontSize:16];
    [label setFrame:NSMakeRect(0, 0, NSWidth(self.frame) / 2., NSHeight(self.frame))];
    [label setString:@"ON"];
    [label setAlignmentMode:kCAAlignmentCenter];

    [label setForegroundColor:[[NSColor whiteColor] CGColor]];

    _rootLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
    [label addConstraint:[CAConstraint
                          constraintWithAttribute:kCAConstraintMidY
                          relativeTo:@"superlayer"
                          attribute:kCAConstraintMidY]];
    [label addConstraint:[CAConstraint
                          constraintWithAttribute:kCAConstraintMidX relativeTo:@"superlayer" attribute:kCAConstraintMidX]];
    [_rootLayer addSublayer:label];

}
0
BonanzaDriver On

Actually, this is what works for me:

self.layer.addSublayer(textLayer)
textLayer.font = font
textLayer.fontSize = font.pointSize
textLayer.frame = CGRectMake(self.layer.bounds.origin.x, ((self.layer.bounds.height - textLayer.fontSize) / 2) - lineWidth, self.layer.bounds.width, self.layer.bounds.height)
textLayer.alignmentMode - kCAAlignmentCenter
textLayer.contentsScale = UIScreen.mainScreen().scale

LineWidth is what I use to "frame" my bounding area. This keeps my text centered vertically and horizontally.