¿Alinear verticalmente el text en un CATextLayer?

Estoy trabajando en un CATextLayer que quiero usar tanto en Mac como en iOS. ¿Puedo controlar la alignment vertical del text dentro de la capa?

En este caso particular, quiero centrarlo verticalmente, pero la información sobre otras alineaciones verticales también sería interesante.

EDIT: Encontré esto , pero no puedo hacer que funcione.

La respuesta correcta, como ya has encontrado, está aquí en Objective-C y funciona para iOS . Funciona CATextLayer el CATextLayer y anulando la function drawInContext .

Sin embargo, he realizado algunas mejoras en el código , como se muestra a continuación, utilizando el código de David Hoerl como base. Los cambios se producen únicamente al recalcular la position vertical del text representado por el yDiff . Lo he probado con mi propio código.

Aquí está el código para los usuarios de Swift:

 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) } requinetworking 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) } } 

Tal vez tarde para responder, pero puede calcular el tamaño del text y luego establecer la position de textLayer. También debe poner el modo textLayer textAligment en "center"

 CGRect labelRect = [text boundingRectWithSize:view.bounds.size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{ NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue" size:17.0] } context:nil]; CATextLayer *textLayer = [CATextLayer layer]; [textLayer setString:text]; [textLayer setForegroundColor:[UIColor networkingColor].CGColor]; [textLayer setFrame:labelRect]; [textLayer setFont:CFBridgingRetain([UIFont fontWithName:@"HelveticaNeue" size:17.0].fontName)]; [textLayer setAlignmentMode:kCAAlignmentCenter]; [textLayer setFontSize:17.0]; textLayer.masksToBounds = YES; textLayer.position = CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds)); [view.layer addSublayer:textLayer]; 

Es una respuesta tardía, pero tengo la misma pregunta en estos días, y he resuelto el problema con la siguiente investigación.

La alignment vertical depende del text que necesite dibujar y la fuente que está utilizando, por lo que no hay una solución de una manera para hacerlo vertical en todos los casos.

Pero aún podemos calcular el punto medio vertical para diferentes casos.

Según Apple's About Text Handling en iOS , necesitamos saber cómo se dibuja el text.

Por ejemplo, estoy tratando de alinear verticalmente para cadenas entre semana: Sun, Mon, Tue, …

Para este caso, la altura del text depende de la altura del casquillo , y no hay descendencia para estos caracteres. Entonces, si necesitamos alinear estos texts con el medio, podemos calcular el desplazamiento del carácter superior del casquillo, por ejemplo, la position de la parte superior del carácter "S".

De acuerdo con la siguiente figura:

higo

El espacio superior para el carácter capital "S" sería

 font.ascender - font.capHeight 

Y el espacio inferior para el carácter capital "S" sería

 font.descender + font.leading 

Entonces, tenemos que mover un poco "S" a la parte superior al:

 y = (font.ascender - font.capHeight + font.descender + font.leading + font.capHeight) / 2 

Eso equivale a:

 y = (font.ascender + font.descender + font.leading) / 2 

Entonces puedo hacer que el text se alinee verticalmente en el medio.

Conclusión:

  1. Si su text no incluye ningún carácter superior a la línea de base, por ejemplo, "p", "j", "g" y ningún carácter sobre la parte superior de la altura de la tapa, por ejemplo, "f". Puede usar la fórmula anterior para alinear el text verticalmente.

     y = (font.ascender + font.descender + font.leading) / 2 
  2. Si su text incluye un carácter debajo de la línea de base, por ejemplo, "p", "j", y ningún carácter excede la parte superior de la altura de la tapa, por ejemplo, "f". Entonces la fórmula vertical sería:

     y = (font.ascender + font.descender) / 2 
  3. Si su text incluye no incluye caracteres dibujados debajo de la línea de base, por ejemplo, "j", "p", e incluye caracteres dibujados sobre la línea de altura del tapón, por ejemplo, "f". Entonces y sería:

     y = (font.descender + font.leading) / 2 
  4. Si todos los caracteres se presentaran en su text, entonces y es igual a:

     y = font.leading / 2 

Gracias a @iamktothed, funciona. La siguiente es la versión 3 rápida:

 class CXETextLayer : CATextLayer { override init() { super.init() } override init(layer: Any) { super.init(layer: layer) } requinetworking init(coder aDecoder: NSCoder) { super.init(layer: aDecoder) } override func draw(in ctx: CGContext) { let height = self.bounds.size.height let fontSize = self.fontSize let yDiff = (height-fontSize)/2 - fontSize/10 ctx.saveGState() ctx.translateBy(x: 0.0, y: yDiff) super.draw(in: ctx) ctx.restreGState() } } 

Versión Swift 3 para cadenas regulares y atribuidas.

 class ECATextLayer: CATextLayer { override open func draw(in ctx: CGContext) { let yDiff: CGFloat let fontSize: CGFloat let height = self.bounds.height if let attributedString = self.string as? NSAttributedString { fontSize = attributedString.size().height yDiff = (height-fontSize)/2 } else { fontSize = self.fontSize yDiff = (height-fontSize)/2 - fontSize/10 } ctx.saveGState() ctx.translateBy(x: 0.0, y: yDiff) super.draw(in: ctx) ctx.restreGState() } } 

Por lo tanto, no hay una forma "directa" de hacer esto, pero puede lograr lo mismo utilizando métricas de text:

http://developer.apple.com/library/ios/#documentation/UIKit/Reference/NSString_UIKit_Additions/Reference/Reference.html

… por ejemplo, encuentre el tamaño del text y luego use esa información para colocarlo donde desee en la capa primaria. Espero que esto ayude.

Necesita saber dónde CATextLayer pondrá la línea de base de su text. Una vez que sepa eso, compense el sistema de coorderadas dentro de la capa, es decir, ajuste bounds.origin.y por la diferencia entre donde la línea de base normalmente se encuentra y donde quiere que sea, dadas las métricas de la fuente.

CATextLayer es un poco un cuadro negro y encontrar dónde se sentará la línea base es un poco complicado – vea mi respuesta aquí para iOS – No tengo idea de cuál es el comportamiento en Mac.

El código de gbk funciona. A continuación se actualiza el código de gbk para XCode 8 beta 6. Actual a partir del 1 de octubre de 2016

Paso 1. Subclass CATextLayer. En el código siguiente, he nombrado a la subclass "MyCATextLayer" fuera de la class de controller de vista, copyr / pegar el código siguiente.

 class MyCATextLayer: 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() } requinetworking init(coder aDecoder: NSCoder) { super.init(layer: aDecoder) } override func draw(in ctx: CGContext) { let height = self.bounds.size.height let fontSize = self.fontSize let yDiff = (height-fontSize)/2 - fontSize/10 ctx.saveGState() ctx.translateBy(x: 0.0, y: yDiff) super.draw(in: ctx) ctx.restreGState() } } 

Paso 2. Dentro de la class de controller de vista en su file ".swift", cree su CATextLabel. En el ejemplo de código he llamado a la subclass "MyDopeCATextLayer".

 let MyDopeCATextLayer: MyCATextLayer = MyCATextLayer() 

Paso 3. Configura tu nuevo CATextLayer con el text / color / límites / marco deseado.

 MyDopeCATextLayer.string = "Hello World" // displayed text MyDopeCATextLayer.foregroundColor = UIColor.purple.cgColor //color of text is purple MyDopeCATextLayer.frame = CGRect(x: 0, y:0, width: self.frame.width, height: self.frame.height) MyDopeCATextLayer.font = UIFont(name: "HelveticaNeue-UltraLight", size: 5) //5 is ignonetworking, set actual font size using ".fontSize" (below) MyDopeCATextLayer.fontSize = 24 MyDopeCATextLayer.alignmentMode = kCAAlignmentCenter //Horizontally centers text. text is automatically centenetworking vertically because it's set in subclass code MyDopeCATextLayer.contentsScale = UIScreen.main.scale //sets "resolution" to whatever the device is using (prevents fuzzyness/blurryness) 

Paso 4. hecho

El código para Swift 3, basado en el código @iamktothed

Si usa una cadena atribuida para establecer las properties de la fuente, entonces puede usar el tamaño de function () de NSAttributedString para calcular la altura de la cadena. Creo que este código también resuelve los problemas descritos por @Enix

 class LCTextLayer: CATextLayer { override init() { super.init() } override init(layer: Any) { super.init(layer: layer) } requinetworking init(coder aDecoder: NSCoder) { super.init(layer: aDecoder) } override open func draw(in ctx: CGContext) { if let attributedString = self.string as? NSAttributedString { let height = self.bounds.size.height let stringSize = attributedString.size() let yDiff = (height - stringSize.height) / 2 ctx.saveGState() ctx.translateBy(x: 0.0, y: yDiff) super.draw(in: ctx) ctx.restreGState() } } } 

Como mejor puedo decir, la respuesta a mi pregunta es "No."