How to make subscripts and superscripts using NSAttributedString?

I need to make subscripts for chemistry formulas (H2O, Na^2+, etc)?

Is this possible to do with NSAttributedString, or is there an alternative/easier way to make subscripts?


Solution 1:

Here's what I did in iOS 6. First add the CoreText, and QuartzCore frameworks. Then import:

#import <QuartzCore/QuartzCore.h>
#import <CoreText/CTStringAttributes.h>
#import <CoreText/CoreText.h>

I made a small function that inputs a plain NSString and exports a NSMutableAttributedString with the last character in superscript. This can be modified to allow setting superscript or subscript, change kCTSuperscriptAttributeName value to -1. Also you could add a variable to specify where to put the superscript in the string. Right now it just assumes the end of the string.

- (NSMutableAttributedString *)plainStringToAttributedUnits:(NSString *)string;
{
    NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:string];
    UIFont *font = [UIFont systemFontOfSize:10.0f];
    UIFont *smallFont = [UIFont systemFontOfSize:9.0f];

    [attString beginEditing];
    [attString addAttribute:NSFontAttributeName value:(font) range:NSMakeRange(0, string.length - 2)];
    [attString addAttribute:NSFontAttributeName value:(smallFont) range:NSMakeRange(string.length - 1, 1)];
    [attString addAttribute:(NSString*)kCTSuperscriptAttributeName value:@"1" range:NSMakeRange(string.length - 1, 1)];
    [attString addAttribute:(NSString*)kCTForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, string.length - 1)];
    [attString endEditing];
    return attString;
}

Now when I want to use it I can do the following to put it in a UITextField:

    NSString *qlwUnitsPlainText = @"m3";
    self.quantityLoadWeightUnits_textField.attributedText = [self plainStringToAttributedUnits:qlwUnitsPlainText];

I hope this helps somebody else, there's not many examples out there!

Solution 2:

This is possible to do with NSAttributedString. The attribute constant you're looking for depends on your platform. For Mac OS X it is NSSuperscriptAttributeName and on iOS it is kCTSuperscriptAttributeName. Pass in a negative value for subscript.

The only caveat is that UILabel on iOS can't draw NSAttributedStrings (yet, fingers crossed for iOS 6). You would need to draw the text using Core Text or find some third party replacement for UILabel that can draw an NSAttributedString.

Solution 3:

On iOS, I had missed the kCTSuperscriptAttributeName constant but had good results with font size and "baseline". It gives you a little more control too for less obedient fonts:

+ (NSAttributedString *)attributedStringForText:(NSString *)normalText andSuperscript:(NSString *)superscriptText textSize:(CGFloat)textSize
{
    UIFont *normalFont = [Styles mainFontWithSize:textSize];
    UIFont *superFont = [Styles mainFontWithSize:textSize / 2];

    NSMutableAttributedString *finalStr = [[NSMutableAttributedString alloc] initWithString:normalText attributes:@{NSFontAttributeName: normalFont}];

    NSAttributedString *superStr = [[NSAttributedString alloc] initWithString:superscriptText attributes:@{NSFontAttributeName: superFont, NSBaselineOffsetAttributeName:@(textSize/2)}];

    [finalStr appendAttributedString:superStr];

    return finalStr;
}