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.
And you want to do it in a way that:
- works with elements placed in xibs and storyboards
- works with UI elements like UIAlertViews or UIDatePickers
- supports dynamic type
- and is quite transparent to the code of the rest of the application.
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:
- This is hack I did in one afternoon - it's more of a steamroller approach
- Use this only if it's really necessary (like having a big brand department which insists on integrating their font) or any app where Comic Sans is a better choice than Helvetica
- Use a font which needs approximately the same space as Helvetica Neue - elements like UIDatePicker get truncated otherwise
- You gonna need some more custom logic if you want to use more than one custom font or do something similar like Apple's dynamic type and have different font weights for different sizes
- I probably missed a much saner approach of solving this problem - if you know any please tell me via @srinner
Download an example project here: FontSwizzling.zip