www.rinner.st

the blog/wiki/website/homepage/internetpräsenz of Stefan Rinner

Monday, Oct 14, 2013

So, you want to do something horrible. Something horrible like breaking all rules of good taste and HIG and have a custom font everywhere in your iOS7 app.

You could use iOS's appearance features for customizing but unfortunately you can't just specify a font name (as you would do in CSS: body {font-family:'Comic Sans';}) but you have to specify a UIFont which consists of a fontName and fontSize. So you could override the font of all UILabels, but then all of them wouldn't have the some font but the same size as well.

image

And you want to do it in a way that:

so, Swizzle!

First, get JRSwizzle

What you basically want is that every way a label is instantiated returns a label using your custom font.

#import "UILabel+Additions.h"
#import "JRSwizzle.h"
#import "UIFont+Additions.h"

@implementation UILabel (Additions)

+ (void)load
{
    [UILabel jr_swizzleMethod:@selector(setFont:) withMethod:@selector(setCustomFont:) error:nil];
	[UILabel jr_swizzleMethod:@selector(init) withMethod:@selector(initCustom) error:nil];
    [UILabel jr_swizzleMethod:@selector(initWithCoder:) withMethod:@selector(initCustomWithCoder:) error:nil];
	[UILabel jr_swizzleMethod:@selector(initWithFrame:) withMethod:@selector(initCustomWithFrame:) error:nil];
}

- (id)initCustom
{
    self = [self initCustom];
    if(self) {
        self.font = [UIFont fontWithName:FSCustomFontName size:self.font.pointSize];
    }
	return self;
}

- (id)initCustomWithCoder:(NSCoder *)aDecoder
{
	self = [self initCustomWithCoder:aDecoder];
    if(self) {
	    self.font = [UIFont fontWithName:FSCustomFontName size:self.font.pointSize];
    }
	return self;
}

- (id)initCustomWithFrame:(CGRect)frame
{
    self = [self initCustomWithFrame:frame];
    if(self) {
	    self.font = [UIFont fontWithName:FSCustomFontName size:self.font.pointSize];
    }
	return self;
}

- (void)setCustomFont:(UIFont *)font
{
    //overriding setFont is necessary to get the custom font into UIButtons
    if([font.fontName rangeOfString:@"MedulaOne"].location == 0) {
	    return [self setCustomFont:font];
    } else {
	    return [self setCustomFont:[UIFont fontWithName:FSCustomFontName size:font.pointSize]];
    }
}

@end

And you want to override all methods in UIFont which return fonts.

#import "UIFont+Additions.h"
#import "JRSwizzle.h"

@implementation UIFont (Additions)

+ (void)load 
{
	[UIFont jr_swizzleClassMethod:@selector(preferredFontForTextStyle:) withClassMethod:@selector(customFontForTextStyle:) error:nil];
    [UIFont jr_swizzleClassMethod:@selector(fontWithName:size:) withClassMethod:@selector(customFontWithName:size:) error:nil];
    [UIFont jr_swizzleClassMethod:@selector(systemFontOfSize:) withClassMethod:@selector(customFontOfSize:) error:nil];
    [UIFont jr_swizzleClassMethod:@selector(boldSystemFontOfSize:) withClassMethod:@selector(customBoldFontOfSize:) error:nil];
}

+ (UIFont *)customFontWithName:(NSString *)fontName size:(CGFloat)fontSize
{
	if([fontName rangeOfString:@"MedulaOne"].location == 0) {
        return [self customFontWithName:fontName size:fontSize];
    } else {
	    return [UIFont fontWithName:FSCustomFontName size:fontSize];
    }
}

+ (UIFont *)customFontForTextStyle:(NSString *)style
{
    return [UIFont fontWithName:FSCustomFontName size:5];
}

+ (UIFont *)customFontOfSize:(CGFloat)fontSize;
{
    return [UIFont fontWithName:FSCustomFontName size:fontSize];
}

+ (UIFont *)customBoldFontOfSize:(CGFloat)fontSize;
{
    //TODO add something bold here
    return [UIFont fontWithName:FSCustomFontName size:fontSize];
}

@end

Caveats:

Download an example project here: FontSwizzling.zip