2012-04-23 9 views
16

Ho trovato questa risposta su come disegnare testo ruotato con NSString drawInRect :, ma non sono sicuro di come funziona in quanto unico tipo di funziona per me: https://discussions.apple.com/thread/1779814?start=0&tstart=0Disegno ruotata di testo con NSString drawInRect

miei sguardi di codice Mi piace:

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

Così quando lo uso, vedo il testo disegnato a 45 gradi sotto l'asse x. Voglio disegnare il testo verticalmente lungo il mio lineareartern. Quindi ho pensato di poterlo fare usando M_PI/2 per 90 gradi. Non vedo il mio testo però. Ho provato diverse trasformazioni per la rotazione e solo alcune sembrano funzionare come M_PI/4 e M_PI/8. Penserei che se avessi usato -M_PI/4 avrebbe il testo 45 gradi sopra l'asse x e M_PI/2 sarebbe 90 gradi sotto l'asse x. Ma entrambi si presentano con niente.

Qualche idea? Grazie.

risposta

12

Penso che quello che sta succedendo è che stai ruotando il testo in una posizione esterna alla vista perché ruota il contesto ruotando sull'origine.

Dopo la rotazione è necessario eseguire una traslazione per spostare nuovamente il contesto nella vista. Nell'esempio seguente, ruoto il contesto in senso antiorario di 90 gradi. Poi traduco il tx del contesto la distanza dell'altezza.

- (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); 
} 

Si noti che è anche possibile ottenere la dimensione di come sarà reso la stringa, che può aiutare a fare i calcoli per l'allineamento del testo.

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

Grazie mille. ma se cambio il -M_PI_2 in M_PI_2. questa stringa scomparirà. PERCHÉ? – Jacky

+1

Ho capito, se cambia -M_PI_2 a M_PI_2, è anche necessario cambiare CGContextTranslateCTM (context, -rect.size.height, 0); a CGContextTranslateCTM (context, 0, -rect.size.height); – Jacky

15

Ho risolto questo problema in modo successivo.

1) dichiarare categoria sul NSString

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

Questa categoria sarà disegnare testo con dato punto centrale, in una linea e con il dato font e l'angolo.

2) L'attuazione di questa categoria è simile 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) Ora posso usarlo nel mio metodo [UIView drawRect:]. Per esempio, in un certo senso prossimo:

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

Bella soluzione ... Molto più semplice passare alcuni parametri e lasciare che la funzione gestisca tutto il resto – jsetting32

+0

Molto elegante! sizeWithFont e drawAtPoint: withFont sono ora deprecati. Sostituire la tua riga di codice CGSize textSize ... con questo codice: NSDictionary * attributes = @ {NSFontAttributeName: specifiedFont}; CGSize textSize = [auto sizeWithAttributes: attributi]; funziona come pubblicizzato. Anche la chiamata al metodo drawAtPoint deve essere aggiornata. Sostituisci semplicemente "withFont: font" con "withAttributes: attributes". Grazie ancora per questa soluzione pulita. – Scooter

+0

Risposta stupenda !!! –

5

soluzione di KoNew a 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)) 


    } 


} 

Per utilizzare:

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

Grazie per questo post, e Chris's answer e Arkadiusz's answer at another post.

Infine, risolvere questo problema con una sottoclasse UILabel, denominata MyVerticalLabel, e farlo disegnare testo verticale.

@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

Ecco una versione aggiornata e semplificata della risposta di KoNEW. Conserva lo stato del contesto grafico come un bonus. Questa è anche una semplice funzione invece di un'estensione di stringa.

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