futureelite7
futureelite7

Reputation: 11502

How to draw vertical text in UILabel

I'm currently working on drawing vertical Chinese text in a label. Here's what I am trying to achieve, albeit with Chinese Characters:

Express

I've been planning to draw each character, rotate each character 90 degrees to the left, then rotating the entire label via affine transformations to get the final result. However, it feels awfully complicated. Is there an easier way to draw the text without complicated CoreGraphics magic that I'm missing?

Upvotes: 28

Views: 22720

Answers (7)

Dzmitry Sotnikov
Dzmitry Sotnikov

Reputation: 80

import UIKit

class VerticalLabel : UILabel {
    
    private var _text : String? = nil
    
    override var text : String? {
        get {
            return _text
        }
        set {
            self.numberOfLines = 0
            self.textAlignment = .center
            self.lineBreakMode = .byWordWrapping
            _text = newValue
            if let t = _text {
                var s = ""
                for c in t {
                    s += "\(c)\n"
                }
                super.text = s
            }
        }
    }
    
}

Upvotes: 0

Kuralay Biehler
Kuralay Biehler

Reputation: 67

Swift 5

More easy way with CGAffineTransform

import UIKit
class ViewController: UIViewController {

@IBOutlet weak var verticalText: UILabel

    override func viewDidLoad() {

        verticalText.transform = CGAffineTransform(rotationAngle:CGFloat.pi/2)
    }
}

Upvotes: 1

AechoLiu
AechoLiu

Reputation: 18408

This is another way to draw vertical text, by subclassing UILabel. But it is some kind different of what the question want.

Objective-C

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

    CGRect newRect = CGRectApplyAffineTransform(rect, transform);
    newRect.origin = CGPointZero;

    NSMutableParagraphStyle *textStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
    textStyle.lineBreakMode = self.lineBreakMode;
    textStyle.alignment = self.textAlignment;

    NSDictionary *attributeDict =
    @{
      NSFontAttributeName : self.font,
      NSForegroundColorAttributeName : self.textColor,
      NSParagraphStyleAttributeName : textStyle,
      };
    [self.text drawInRect:newRect withAttributes:attributeDict];
}
@end

A sample image is following:

A sample image

Swift

It can put on the storyboard, and watch the result directly. Like the image, it's frame will contain the vertical text. And text attributes, like textAlignment, font, work well too.

A vertical text example

@IBDesignable
class MyVerticalLabel: UILabel {

    override func drawRect(rect: CGRect) {
        guard let text = self.text else {
            return
        }

        // Drawing code
        let context = UIGraphicsGetCurrentContext()

        let transform = CGAffineTransformMakeRotation( CGFloat(-M_PI_2))
        CGContextConcatCTM(context, transform)
        CGContextTranslateCTM(context, -rect.size.height, 0)

        var newRect = CGRectApplyAffineTransform(rect, transform)
        newRect.origin = CGPointZero

        let textStyle = NSMutableParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle
        textStyle.lineBreakMode = self.lineBreakMode
        textStyle.alignment = self.textAlignment

        let attributeDict: [String:AnyObject] = [
            NSFontAttributeName: self.font,
            NSForegroundColorAttributeName: self.textColor,
            NSParagraphStyleAttributeName: textStyle,
        ]

        let nsStr = text as NSString
        nsStr.drawInRect(newRect, withAttributes: attributeDict)
    }

}

Swift 4

override func draw(_ rect: CGRect) {
    guard let text = self.text else {
        return
    }

    // Drawing code
    if let context = UIGraphicsGetCurrentContext() {
        let transform = CGAffineTransform( rotationAngle: CGFloat(-Double.pi/2))
        context.concatenate(transform)
        context.translateBy(x: -rect.size.height, y: 0)
        var newRect = rect.applying(transform)
        newRect.origin = CGPoint.zero

        let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
        textStyle.lineBreakMode = self.lineBreakMode
        textStyle.alignment = self.textAlignment

        let attributeDict: [NSAttributedStringKey: AnyObject] = [NSAttributedStringKey.font: self.font, NSAttributedStringKey.foregroundColor: self.textColor, NSAttributedStringKey.paragraphStyle: textStyle]

        let nsStr = text as NSString
        nsStr.draw(in: newRect, withAttributes: attributeDict)
    }
}

Upvotes: 6

AXE
AXE

Reputation: 8465

Tried the method offered by Simha.IC but it didn't work well for me. Some characters are thinner than others and get placed two on a line. E.g.

W
ai
ti
n
g

The solution for me was to create a method that transforms the string itself into a multiline text by adding \n after each character. Here's the method:

- (NSString *)transformStringToVertical:(NSString *)originalString
{
    NSMutableString *mutableString = [NSMutableString stringWithString:originalString];
    NSRange stringRange = [mutableString rangeOfString:mutableString];

    for (int i = 1; i < stringRange.length*2 - 2; i+=2)
    {
        [mutableString insertString:@"\n" atIndex:i];
    }

    return mutableString;
}

Then you just setup the label like this:

label.text = [self transformStringToVertical:myString];
CGRect labelFrame = label.frame;
labelFrame.size.width  = label.font.pointSize;
labelFrame.size.height = label.font.lineHeight * myString.length;
label.frame = labelFrame;

Enjoy!

Upvotes: 12

Loquatious
Loquatious

Reputation: 1775

Well, You can do like below:

labelObject.numberOfLines = 0;
labelObject.lineBreakMode = NSLineBreakByCharWrapping;

and setFrame with -- height:100, width:20 It will work fine..

Upvotes: 37

Sanket Pandya
Sanket Pandya

Reputation: 1095

It works

UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 30, 100)];
lbl.transform = CGAffineTransformMakeRotation((M_PI)/2);

Upvotes: 22

Jasper Blues
Jasper Blues

Reputation: 28766

If you would like to rotate the whole label (including characters), you can do so as follows:

  1. First add the QuartzCore library to your project.
  2. Create a label:

    UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 300.0, 30.0)];
    [label setText:@"Label Text"];
    
  3. Rotate the label:

    [label setTransform:CGAffineTransformMakeRotation(-M_PI / 2)];
    

Depending on how you'd like to position the label you may need to set the anchor point. This sets the point around which a rotation occurs. Eg:

    [label.layer setAnchorPoint:CGPointMake(0.0, 1.0)];

Upvotes: 8

Related Questions