【问题标题】:Drawing rotated text with NSString drawInRect使用 NSString drawInRect 绘制旋转文本
【发布时间】:2012-04-23 23:49:00
【问题描述】:

我找到了关于如何使用 NSString drawInRect: 绘制旋转文本的答案,但我不确定它是如何工作的,因为它只对我有用:https://discussions.apple.com/thread/1779814?start=0&tstart=0

我的代码如下:

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

所以当我使用它时,我看到文本在 x 轴下方 45 度处绘制。我想沿着我的线性渐变垂直绘制文本。所以我想我可以通过使用 M_PI/2 90 度来做到这一点。我没有看到我的文字。我尝试了不同的旋转变换,只有一些看起来像 M_PI/4 和 M_PI/8。我认为如果我使用 -M_PI/4 它将使文本在 x 轴上方 45 度,而 M_PI/2 将在 x 轴下方 90 度。但两者都一无所获。

有什么想法吗?谢谢。

【问题讨论】:

    标签: iphone quartz-graphics


    【解决方案1】:

    我用下一个方法解决这个问题。

    1) 在 NSString 上声明类别

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

    此类别将使用给定的中心点在一行中以给定的字体和角度绘制文本。

    2) 这个类的实现如下:

    @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) 现在我可以在我的[UIView drawRect:] 方法中使用它。 例如,在下一个方式:

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

    【讨论】:

    • 不错的解决方案...传递一些参数并让函数处理其他所有事情要容易得多
    • 非常优雅! sizeWithFont 和 drawAtPoint:withFont 现在已被弃用。用以下代码替换您的 CGSize textSize... 代码行: NSDictionary *attributes = @{NSFontAttributeName: specifiedFont}; CGSize textSize = [self sizeWithAttributes:attributes];像宣传的那样工作。 drawAtPoint 方法调用也需要更新。只需将 'withFont:font' 替换为 'withAttributes:attributes'。再次感谢这个干净的解决方案。
    【解决方案2】:

    我认为正在发生的事情是您将文本旋转到视图之外的位置,因为它通过旋转原点来旋转上下文。

    旋转后,您需要进行平移以将上下文移回视图中。在下面的示例中,我将上下文逆时针旋转 90 度。然后我将上下文的 tx 转换为高度的距离。

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

    请注意,您还可以获取如何呈现字符串的大小,这有助于计算文本的对齐方式。

        CGSize stringSize = [string sizeWithFont:font]; 
    

    【讨论】:

    • 非常感谢。但如果我将 -M_PI_2 更改为 M_PI_2。该字符串将消失。为什么?
    • 我知道了,如果将 -M_PI_2 改为 M_PI_2 ,还需要更改 CGContextTranslateCTM(context, -rect.size.height, 0); to CGContextTranslateCTM(context, 0, -rect.size.height);
    【解决方案3】:

    这是 KoNEW 答案的更新和简化版本。它保留了图形上下文的状态作为奖励。这也是一个简单的函数,而不是字符串扩展。

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

    【讨论】:

      【解决方案4】:

      KoNew 在 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))
      
      
          }
      
      
      }
      

      使用方法:

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

      【讨论】:

        【解决方案5】:

        感谢这篇文章,以及Chris's answerArkadiusz's answer at another post

        最后,我通过一个名为MyVerticalLabelUILabel 子类解决了这个问题,并让它绘制垂直文本。

        @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
        

        【讨论】:

          【解决方案6】:

          我发现上面 Todd 的答案非常有帮助。但是,我经常想相对于文本的边缘之一(顶部、左侧等)定位和旋转文本。所以,我扩展了他的回答。另外,使用了属性文本,所以可以独立处理字体、颜色等。

          enum StringOrigin: Int { case
              upperLeft, upperCenter, upperRight,
              centerLeft, centerCenter, centerRight,
              lowerLeft, lowerCenter, lowerRight }
          
          func drawRotatedAttrString(_ s: NSAttributedString, p: CGPoint, angle: CGFloat, origin: StringOrigin) {
              let width = s.size().width
              let height = s.size().height
          
              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 offset to handle "origin" choice
              switch origin {
              case .upperLeft:
                  s.draw(at: .zero)
              case .upperCenter:
                  s.draw(at: CGPoint(x: -0.5 * width, y: 0))
              case .upperRight:
                  s.draw(at: CGPoint(x: -width, y: 0))
              case .centerLeft:
                  s.draw(at: CGPoint(x: 0, y: -0.5 * height))
              case .centerCenter:
                  s.draw(at: CGPoint(x: -0.5 * width, y: -0.5 * height))
              case .centerRight:
                  s.draw(at: CGPoint(x: -width, y: -0.5 * height))
              case .lowerLeft:
                  s.draw(at: CGPoint(x: 0, y: -height))
              case .lowerCenter:
                  s.draw(at: CGPoint(x: -0.5 * width, y: -height))
              case .lowerRight:
                  s.draw(at: CGPoint(x: -width, y: -height))
              }
              // Restore the original coordinate system.
              c.restoreGState()
          }
          

          【讨论】:

            猜你喜欢
            • 2014-08-09
            • 1970-01-01
            • 2010-12-14
            • 1970-01-01
            • 2017-06-13
            • 2015-09-17
            • 2014-05-18
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多