2012-04-23 13 views
16

He encontrado esta respuesta sobre cómo dibujar texto girado con NSString drawInRect :, pero no estoy seguro de cómo funciona, ya que sólo tipo de funciona para mí: https://discussions.apple.com/thread/1779814?start=0&tstart=0Dibujo texto girado con NSString drawInRect

mi código de miradas como:

  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]; 

De modo que cuando uso esto, veo que el texto se dibuja 45 grados por 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.

Respuesta

12

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

Después de su rotación, necesita 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 se puede obtener el tamaño de cómo se procesará la cadena, que puede ayudar a hacer los cálculos para la alineación del texto.

CGSize stringSize = [string sizeWithFont:font]; 
+0

Muchas gracias. pero si cambio el -M_PI_2 a M_PI_2. esta cadena desaparecerá ¿POR QUÉ? – Jacky

+1

Lo tengo, si cambia -M_PI_2 a M_PI_2, también es necesario cambiar CGContextTranslateCTM (context, -rect.size.height, 0); a CGContextTranslateCTM (context, 0, -rect.size.height); – Jacky

15

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 punto central dado, en una línea y con dado de fuente y el ángulo.

2) La aplicación de esta categoría es el siguiente aspecto:

@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 lo puedo usar en mi método [UIView drawRect:]. Por ejemplo, en una siguiente manera:

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

Buena solución ... Mucho más fácil pasar algunos parámetros y dejar que la función maneje todo lo demás – jsetting32

+0

¡Muy elegante! sizeWithFont y drawAtPoint: withFont ahora están en desuso sin embargo. Reemplazar su línea de código CGSize textSize ... con este código: NSDictionary * attributes = @ {NSFontAttributeName: specifiedFont}; CGSize textSize = [self sizeWithAttributes: attributes]; funciona como se anuncia. La llamada al método drawAtPoint también debe actualizarse. Simplemente reemplace 'withFont: font' por 'withAttributes: attributes'. Gracias de nuevo por esta solución limpia. – Scooter

+0

Awesome answer !!! –

5

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


    } 


} 

de 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!) 
0

Gracias por este post, y Chris's answer y Arkadiusz's answer at another post.

Finalmente, resuelvo este problema mediante una subclase UILabel, llamada MyVerticalLabel, y hago que dibuje 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 
5

Aquí 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: p.x, y: p.y) 
    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.restoreGState() 
}