2012-04-23 20 views
16

Znalazłem odpowiedź na jak narysować tekst obrócony NSString drawInRect :, ale nie jestem pewien, jak to działa, ponieważ tylko rodzaj działa na mnie: https://discussions.apple.com/thread/1779814?start=0&tstart=0Drawing obrócony tekst z NSString drawInRect

mój kod wygląda jak:

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

Więc kiedy to wykorzystać, widzę tekst ciągniętą 45 stopni poniżej osi x. Chcę narysować tekst pionowo wzdłuż mojego liniowego elementu. Pomyślałem, że mogę to zrobić, używając M_PI/2 dla 90 stopni. Nie widzę jednak mojego tekstu. Próbowałem różnych transformacji dla rotacji i tylko niektóre wydają się działać jak M_PI/4 i M_PI/8. Myślę, że gdybym użył -M_PI/4, miałby tekst 45 stopni powyżej osi x, a M_PI/2 byłby 90 stopni poniżej osi x. Ale obaj pojawiają się z niczym.

Jakieś myśli? dzięki.

Odpowiedz

12

Myślę, że to, co robisz, to obracanie tekstu do położenia poza widokiem, ponieważ obraca kontekst, obracając się na początku.

Po obrocie należy wykonać tłumaczenie, aby przenieść kontekst z powrotem do widoku. W poniższym przykładzie obracam kontekst przeciwnie do ruchu wskazówek zegara o 90 stopni. Następnie tłumaczę tx kontekstu na odległość wysokości.

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

pamiętać, że można również uzyskać rozmiaru jak łańcuch zostanie wygenerowana, które mogą pomóc w sposób obliczeń dla wyrównania tekstu.

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

Wielkie dzięki. ale jeśli zmienię -M_PI_2 na M_PI_2. ten ciąg zniknie. CZEMU? – Jacky

+1

Mam to, jeśli zmienisz -M_PI_2 na M_PI_2, to również trzeba zmienić CGContextTranslateCTM (context, -rect.size.height, 0); do CGContextTranslateCTM (context, 0, -rect.size.height); – Jacky

15

Rozwiązuję ten problem w następujący sposób.

1) stwierdzenie kategorię na NSString

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

Kategoria ta przygotuje tekst z danego punktu centralnego, w jednej linii i z danej czcionki i kąta.

2) Realizacja tej kategorii to wygląda następująco:

@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) Teraz mogę go używać w moim sposobie [UIView drawRect:]. Na przykład, w następnym sposób:

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

Ładne rozwiązanie ... Znacznie łatwiej jest przekazać kilka parametrów i pozwolić, aby funkcja obsługiwała wszystko inne. – jsetting32

+0

Bardzo elegancki! sizeWithFont i drawAtPoint: withFont są teraz przestarzałe. Zastępowanie CGSize textSize ... wiersza kodu z tym kodem: NSDictionary * attributes = @ {NSFontAttributeName: specifiedFont}; CGSize textSize = [self sizeWithAttributes: attributes]; działa jak w reklamie. Wywołanie metody drawAtPoint również musi zostać zaktualizowane. Po prostu zamień "withFont: font" na "withAttributes: attributes". Jeszcze raz dziękuję za to czyste rozwiązanie. – Scooter

+0

Niesamowita odpowiedź !!! –

5

Rozwiązanie KoNew w 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)) 


    } 


} 

Sposób użycia:

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

podziękować za ten post i Chris's answer i Arkadiusz's answer at another post.

Wreszcie, rozwiązuję ten problem przez podklasę UILabel, o nazwie MyVerticalLabel, i zmuszam do rysowania tekstu w pionie.

@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

Oto zaktualizowana i uproszczona wersja odpowiedzi KoNEW. Zachowuje stan kontekstu graficznego jako bonus. Jest to również prosta funkcja zamiast rozszerzenia String.

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() 
} 
Powiązane problemy