Dibujar texto girado con NSString drawInRect

Encontré esta respuesta sobre cómo dibujar texto rotado con NSString drawInRect :, pero no estoy seguro de cómo funciona, ya que solo funciona para mí: https://discussions.apple.com/thread/1779814?start=0&tstart = 0

Mi código se ve así:

CGContextSaveGState(context); CGContextDrawLinearGradient(context, gradient, CGPointMake(0, centY - halfWidth), CGPointMake(0, centY + halfWidth), 0); // Add text CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); NSString *str = @"some test string"; CGAffineTransform transform1 = CGAffineTransformMakeRotation(M_PI/4); CGContextConcatCTM(context, transform1); CGContextTranslateCTM(context, 0, 0); UIFont *font = [UIFont systemFontOfSize:16.0]; [str drawInRect:CGRectMake(0, 0, 200, 100) withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:UIBaselineAdjustmentNone]; 

Entonces, cuando uso esto, veo que el texto se dibuja 45 grados debajo del eje x. Quiero dibujar el texto verticalmente a lo largo de mi gradiente lineal. Entonces pensé que podría hacer eso usando M_PI / 2 por 90 grados. No obstante, no veo mi texto. He intentado diferentes transformaciones para la rotación, y solo algunas parecen funcionar como M_PI / 4 y M_PI / 8. Yo pensaría que si usara -M_PI / 4 tendría el texto a 45 grados por encima del eje xy M_PI / 2 estaría 90 grados por debajo del eje x. Pero ambos aparecen sin nada.

¿Alguna idea? Gracias.

Creo que lo que sucede es que estás girando el texto a una ubicación fuera de la vista porque gira el contexto girando sobre el origen.

Después de tu rotación, necesitas hacer una traducción para mover el contexto a la vista. En el siguiente ejemplo, giro el contexto en sentido antihorario 90 grados. Luego traduzco el tx del contexto la distancia de la altura.

 - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // Create the gradient CGColorRef startColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0].CGColor; CGColorRef endColor = [UIColor colorWithRed:200.0/255.0 green:200.0/255.0 blue:200.0/255.0 alpha:1.0].CGColor; NSArray *colors = [NSArray arrayWithObjects:(id)startColor, (id)endColor, nil]; CGFloat locations[] = { 0.0, 1.0 }; CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef) colors, locations); CGContextDrawLinearGradient(context, gradient, CGPointMake(rect.origin.x + rect.size.width / 2, rect.origin.y), CGPointMake(rect.origin.x + rect.size.width / 2, rect.origin.y + rect.size.height), 0); // Create text CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor); NSString *string = @"some test string"; UIFont *font = [UIFont systemFontOfSize:16.0]; // Rotate the context 90 degrees (convert to radians) CGAffineTransform transform1 = CGAffineTransformMakeRotation(-M_PI_2); CGContextConcatCTM(context, transform1); // Move the context back into the view CGContextTranslateCTM(context, -rect.size.height, 0); // Draw the string [string drawInRect:rect withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentLeft]; // Clean up CGGradientRelease(gradient); CGColorSpaceRelease(colorSpace); CGContextRestoreGState(context); } 

Tenga en cuenta que también puede obtener el tamaño de cómo se representará la cadena, lo que puede ayudar a hacer cálculos para la alineación del texto.

  CGSize stringSize = [string sizeWithFont:font]; 

Resuelvo este problema de la siguiente manera.

1) Declarar categoría en NSString

 @interface NSString (NMSchemeItemDraw) -(void) drawWithBasePoint:(CGPoint)basePoint andAngle:(CGFloat)angle andFont:(UIFont*)font; @end 

Esta categoría dibujará texto con un punto central dado, en una línea y con una fuente y un ángulo dados.

2) La implementación de esta categoría es similar a:

 @implementation NSString (NMSchemeItemDraw) -(void) drawWithBasePoint:(CGPoint)basePoint andAngle:(CGFloat)angle andFont:(UIFont *)font{ CGSize textSize = [self sizeWithFont:font]; CGContextRef context = UIGraphicsGetCurrentContext(); CGAffineTransform t = CGAffineTransformMakeTranslation(basePoint.x, basePoint.y); CGAffineTransform r = CGAffineTransformMakeRotation(angle); CGContextConcatCTM(context, t); CGContextConcatCTM(context, r); [self drawAtPoint:CGPointMake(-1 * textSize.width / 2, -1 * textSize.height / 2) withFont:font]; CGContextConcatCTM(context, CGAffineTransformInvert(r)); CGContextConcatCTM(context, CGAffineTransformInvert(t)); } @end 

3) Ahora puedo usarlo en mi [UIView drawRect:] . Por ejemplo, de una manera siguiente:

  -(void)drawRect:(CGRect)rect{ NSString* example = @"Title"; [example drawWithBasePoint:CGPointMake(0.0f, 0.0f) andAngle:M_PI andFont:[UIFont boldSystemFontOfSize:16.0]]; } 

Aquí hay una versión actualizada y simplificada de la respuesta de KoNEW. Conserva el estado del contexto gráfico como una bonificación. Esta es también una función simple en lugar de una extensión de cadena.

 func drawRotatedText(_ text: String, at p: CGPoint, angle: CGFloat, font: UIFont, color: UIColor) { // Draw text centered on the point, rotated by an angle in degrees moving clockwise. let attrs = [NSFontAttributeName: font, NSForegroundColorAttributeName: color] let textSize = text.size(attributes: attrs) let c = UIGraphicsGetCurrentContext()! c.saveGState() // Translate the origin to the drawing location and rotate the coordinate system. c.translateBy(x: px, y: py) c.rotate(by: angle * .pi / 180) // Draw the text centered at the point. text.draw(at: CGPoint(x: -textSize.width / 2, y: -textSize.height / 2), withAttributes: attrs) // Restore the original coordinate system. c.restreGState() } 

La solución de KoNew en Swift:

 extension NSString { func drawWithBasePoint(basePoint:CGPoint, angle:CGFloat, font:UIFont) { var attrs: NSDictionary = [ NSFontAttributeName: font ] var textSize:CGSize = self.sizeWithAttributes(attrs as [NSObject : AnyObject]) // sizeWithAttributes is only effective with single line NSString text // use boundingRectWithSize for multi line text var context: CGContextRef = UIGraphicsGetCurrentContext() var t:CGAffineTransform = CGAffineTransformMakeTranslation(basePoint.x, basePoint.y) var r:CGAffineTransform = CGAffineTransformMakeRotation(angle) CGContextConcatCTM(context, t) CGContextConcatCTM(context, r) self.drawAtPoint(CGPointMake(-1 * textSize.width / 2, -1 * textSize.height / 2), withAttributes: attrs as [NSObject : AnyObject]) CGContextConcatCTM(context, CGAffineTransformInvert(r)) CGContextConcatCTM(context, CGAffineTransformInvert(t)) } } 

Usar:

 let fieldFont = UIFont(name: "Helvetica Neue", size: 14) myNSString.drawWithBasePoint(CGPointMake(bounds.width/2, bounds.height/2), angle: CGFloat(-M_PI_2), font: fieldFont!) 

Gracias por esta publicación, y la respuesta de Chris , y la respuesta de Arkadiusz en otra publicación .

Finalmente, UILabel este problema mediante una subclase UILabel , llamada MyVerticalLabel , y la hago dibujar texto vertical.

 @implementation MyVerticalLabel // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code CGContextRef context = UIGraphicsGetCurrentContext(); CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI_2); CGContextConcatCTM(context, transform); CGContextTranslateCTM(context, -rect.size.height, 0); // The drawing rect should be applied with transform to. CGRect newRect = CGRectApplyAffineTransform(rect, transform); newRect.origin = CGPointZero; NSMutableParagraphStyle *textStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy]; textStyle.lineBreakMode = self.lineBreakMode; textStyle.alignment = self.textAlignment; // Apply current label attributes for drawing function. NSDictionary *attributeDict = @{ NSFontAttributeName : self.font, NSForegroundColorAttributeName : self.textColor, NSParagraphStyleAttributeName : textStyle, }; [self.text drawInRect:newRect withAttributes:attributeDict]; } @end