11

On iOS, I load a custom font in my project by adding its file name (an .otf file) to the info.plist file and then using this line of code:

UIFont myFont * = [UIFont fontWithName:titleFontName size:titleFontSize];

I obtain the font that I can use in UILabels and UITextViews.

How could I obtain that this font is displayed only in small caps? If I use it in Photoshop, it's possible to turn on the small caps switch to have all words typeset in small caps (and so, I conclude that there is nothing missing with the font). How could I obtain a similar effect on iOS?

Converting my strings to uppercase is not a viable option for other reasons.

Further information : the font has only one member in its family, as I could understand by using the following code, there is no standalone small caps member in the family.

 for (NSString * familyName in [UIFont familyNames]) {

        NSLog(@"---------------- %@ ---------------", familyName);
        for (NSString * fontName in[UIFont fontNamesForFamilyName:familyName] )
           NSLog(@"- %@", fontName);
   }
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
madewulf
  • 1,870
  • 4
  • 26
  • 41

2 Answers2

18

Small caps are enabled in the font through an open type feature. In iOS 7 we can use a font descriptor to access open type features and enable small caps.

This question goes into how to turn on small caps using core text, but the same can be done for UIFonts and UIKit views just as easily. You'll need to create a UIFontDescriptor and set the UIFontDescriptorFeatureSettingsAttribute to an array of dictionaries for the features you want to enable.

Each font feature dictionary contains a key and value to specify the feature type, and a key and value for the feature selector. Depending on the font you're using, you'll need to find the correct values corresponding to small caps. You can find these in the array that the commented section logs.

UIFont Category

This category will generate a UIFont object with small caps enabled. You'll need to add the correct font name.

#import "UIFont+SmallCaps.h"
#import <CoreText/CoreText.h>

@implementation UIFont (SmallCaps)

+ (UIFont *) applicationSmallCapsFontWithSize:(CGFloat) size {
    /*
    // Use this to log all of the properties for a particular font
    UIFont *font = [UIFont fontWithName: fontName size: fontSize];
    CFArrayRef  fontProperties  =  CTFontCopyFeatures ( ( __bridge CTFontRef ) font ) ;
    NSLog(@"properties = %@", fontProperties);
    */

    NSArray *fontFeatureSettings = @[ @{ UIFontFeatureTypeIdentifierKey: @(kLowerCaseType),
                                         UIFontFeatureSelectorIdentifierKey : @(kLowerCaseSmallCapsSelector) } ];

    NSDictionary *fontAttributes = @{ UIFontDescriptorFeatureSettingsAttribute: fontFeatureSettings ,
                                      UIFontDescriptorNameAttribute: FONT_NAME } ;

    UIFontDescriptor *fontDescriptor = [ [UIFontDescriptor alloc] initWithFontAttributes: fontAttributes ];

    return [UIFont fontWithDescriptor:fontDescriptor size:size];
}

@end
Community
  • 1
  • 1
Anthony Mattox
  • 7,048
  • 6
  • 43
  • 59
  • Do you know why the system font: HelveticaNeue-Light doesn't work? – Legoless Mar 04 '14 at 10:27
  • 1
    @Legoless This doesn't just shrink down the capitals, but actually looks for small cap character that the font designer drew and included in the font file. It's dependent on whether those characters are included in the font, and in most cases they are not. Here's an image comparing true small caps and scaled down capitals http://en.wikipedia.org/wiki/File:True_vs_Scaled_Small_Caps.svg . – Anthony Mattox Mar 04 '14 at 12:55
  • That might be true, but where does Photoshop get the Small caps using the same font? – Legoless Mar 04 '14 at 14:41
  • 1
    @Legoless There are a couple different thing that could be happening in this case: 1) They are almost certainly different font files, and could have different features. 2) Photoshop could be faking it and shrinking down the regular caps. You can test this by comparing a 'capital' and 'lowercase' letter with small caps. With true small caps the letter forms will be shaped differently. 3) The font could have these features with a different identifier. Run the commented out log to see all the font features. – Anthony Mattox Mar 05 '14 at 03:44
  • 2
    Note: If the font doesn't have small caps you should never use fake small caps next to larger capitals. The difference in contrast will look very strange. If you want text in all small caps you can approximate the look by drawing an upper case string at a slightly smaller size and track out the letters (I think with `NSExpansionAttributeName`) a bit. – Anthony Mattox Mar 05 '14 at 03:50
  • Do you know how to do this with Avenir or AvenirNext? – Jason Silberman Jul 10 '14 at 19:39
  • remember that `fontFeatureSettings` are Array of dictionaries, not just dictionary. – Bartłomiej Semańczyk Jun 20 '16 at 15:56
10

Extension for UIFont in Swift:

extension UIFont {

    func smallCaps() -> UIFont {

        let settings = [[UIFontFeatureTypeIdentifierKey: kLowerCaseType, UIFontFeatureSelectorIdentifierKey: kLowerCaseSmallCapsSelector]]
        let attributes: [String: AnyObject] = [UIFontDescriptorFeatureSettingsAttribute: settings, UIFontDescriptorNameAttribute: fontName]

        return UIFont(descriptor: UIFontDescriptor(fontAttributes: attributes), size: pointSize)
    }
}

Usage:

label.font = UIFont(name: "SourceSansPro-Regular", size: 12)?.smallCaps()

Example:

enter image description here enter image description here

macOS version:

extension NSFont {
    func smallCaps() -> NSFont? {
        let settings = [[NSFontFeatureTypeIdentifierKey: kLowerCaseType, NSFontFeatureSelectorIdentifierKey: kLowerCaseSmallCapsSelector]]
        let attributes: [String: AnyObject] = [NSFontFeatureSettingsAttribute: settings as AnyObject, NSFontNameAttribute: fontName as AnyObject]

        return NSFont(descriptor: NSFontDescriptor(fontAttributes: attributes), size: pointSize)
    }
}

IMPORTANT:

  • not every font works with it, It's dependent on whether those characters are included in the font or not as @Anthony Mattox said.

  • remember to set the string as: Example, not EXAMPLE.

  • settings are Array<NDSictionary>, not NSDictonary.
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358