It seems to be possible in iOS 5 using the UIAppearance proxy.
[[UILabel appearance] setFont:[UIFont fontWithName:@"YourFontName" size:17.0]];
That will set the font to be whatever your custom font is for all UILabels in your app. You'll need to repeat it for each control (UIButton, UILabel, etc.).
Remember you'll need to put the UIAppFonts value in your info.plist and include the name of the font you're including.
Base on Fábio Oliveira's answer (https://stackoverflow.com/a/23042694/2082851), I make my own swift 4.
In short, this extension exchanges default functions init(coder:)
, systemFont(ofSize:)
, boldSystemFont(ofSize:)
, italicSystemFont(ofSize:)
with my custom methods.
Note that it's not fully implement, but you can exchange more methods base on my implementation.
import UIKit
struct AppFontName {
static let regular = "CourierNewPSMT"
static let bold = "CourierNewPS-BoldMT"
static let italic = "CourierNewPS-ItalicMT"
}
extension UIFontDescriptor.AttributeName {
static let nsctFontUIUsage = UIFontDescriptor.AttributeName(rawValue: "NSCTFontUIUsageAttribute")
}
extension UIFont {
static var isOverrided: Bool = false
@objc class func mySystemFont(ofSize size: CGFloat) -> UIFont {
return UIFont(name: AppFontName.regular, size: size)!
}
@objc class func myBoldSystemFont(ofSize size: CGFloat) -> UIFont {
return UIFont(name: AppFontName.bold, size: size)!
}
@objc class func myItalicSystemFont(ofSize size: CGFloat) -> UIFont {
return UIFont(name: AppFontName.italic, size: size)!
}
@objc convenience init(myCoder aDecoder: NSCoder) {
guard
let fontDescriptor = aDecoder.decodeObject(forKey: "UIFontDescriptor") as? UIFontDescriptor,
let fontAttribute = fontDescriptor.fontAttributes[.nsctFontUIUsage] as? String else {
self.init(myCoder: aDecoder)
return
}
var fontName = ""
switch fontAttribute {
case "CTFontRegularUsage":
fontName = AppFontName.regular
case "CTFontEmphasizedUsage", "CTFontBoldUsage":
fontName = AppFontName.bold
case "CTFontObliqueUsage":
fontName = AppFontName.italic
default:
fontName = AppFontName.regular
}
self.init(name: fontName, size: fontDescriptor.pointSize)!
}
class func overrideInitialize() {
guard self == UIFont.self, !isOverrided else { return }
// Avoid method swizzling run twice and revert to original initialize function
isOverrided = true
if let systemFontMethod = class_getClassMethod(self, #selector(systemFont(ofSize:))),
let mySystemFontMethod = class_getClassMethod(self, #selector(mySystemFont(ofSize:))) {
method_exchangeImplementations(systemFontMethod, mySystemFontMethod)
}
if let boldSystemFontMethod = class_getClassMethod(self, #selector(boldSystemFont(ofSize:))),
let myBoldSystemFontMethod = class_getClassMethod(self, #selector(myBoldSystemFont(ofSize:))) {
method_exchangeImplementations(boldSystemFontMethod, myBoldSystemFontMethod)
}
if let italicSystemFontMethod = class_getClassMethod(self, #selector(italicSystemFont(ofSize:))),
let myItalicSystemFontMethod = class_getClassMethod(self, #selector(myItalicSystemFont(ofSize:))) {
method_exchangeImplementations(italicSystemFontMethod, myItalicSystemFontMethod)
}
if let initCoderMethod = class_getInstanceMethod(self, #selector(UIFontDescriptor.init(coder:))), // Trick to get over the lack of UIFont.init(coder:))
let myInitCoderMethod = class_getInstanceMethod(self, #selector(UIFont.init(myCoder:))) {
method_exchangeImplementations(initCoderMethod, myInitCoderMethod)
}
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
// Avoid warning of Swift
// Method 'initialize()' defines Objective-C class method 'initialize', which is not guaranteed to be invoked by Swift and will be disallowed in future versions
override init() {
super.init()
UIFont.overrideInitialize()
}
...
}
There is also another solution which will be to override systemFont.
Just create a category
UIFont+SystemFontOverride.h
#import <UIKit/UIKit.h>
@interface UIFont (SystemFontOverride)
@end
UIFont+SystemFontOverride.m
@implementation UIFont (SystemFontOverride)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+ (UIFont *)boldSystemFontOfSize:(CGFloat)fontSize {
return [UIFont fontWithName:@"fontName" size:fontSize];
}
+ (UIFont *)systemFontOfSize:(CGFloat)fontSize {
return [UIFont fontWithName:@"fontName" size:fontSize];
}
#pragma clang diagnostic pop
@end
This will replace the default implementation and most UIControls use systemFont.
If you're using Swift, you can create a UILabel extension:
extension UILabel {
@objc var substituteFontName : String {
get { return self.font.fontName }
set { self.font = UIFont(name: newValue, size: self.font.pointSize) }
}
}
And then where you do your appearance proxying:
UILabel.appearance().substituteFontName = applicationFont
There is equivalent Objective-C code using UI_APPEARANCE_SELECTOR
on a property with the name substituteFontName
.
Addition
For the case where you'd want to set bold and regular fonts separately:
extension UILabel {
@objc var substituteFontName : String {
get { return self.font.fontName }
set {
if self.font.fontName.range(of:"Medium") == nil {
self.font = UIFont(name: newValue, size: self.font.pointSize)
}
}
}
@objc var substituteFontNameBold : String {
get { return self.font.fontName }
set {
if self.font.fontName.range(of:"Medium") != nil {
self.font = UIFont(name: newValue, size: self.font.pointSize)
}
}
}
}
Then for your UIAppearance proxies:
UILabel.appearance().substituteFontName = applicationFont
UILabel.appearance().substituteFontNameBold = applicationFontBold
Note: if you're finding that the bold substitution isn't working, it's possible the default font name doesn't contain "Medium". Switch out that string for another match as needed (thanks to Mason in the comments below) .
Developing from Hugues BR answer but using method swizzling I've arrived to a solution that is successfully changing all the fonts to a desired font in my app.
An approach with Dynamic Type should be what you should look for on iOS 7. The following solution is not using Dynamic Type.
Notes:
- initWithCoder:
override. However that won't cover all the cases;This solution uses two different methods to achieve the final result. The first is override the UIFont class methods + systemFontWithSize:
and similar with ones that use my alternatives (here I use "Zapfino" to leave no doubts that the replacement was successful).
The other method is to override - initWithCoder:
method on UIFont to replace any occurrence of CTFontRegularUsage
and similar by my alternatives.
This last method was necessary because I've found that UILabel
objects encoded in NIB files don't check the + systemFontWithSize:
methods to get their system font and instead encode them as UICTFontDescriptor
objects.
I've tried to override - awakeAfterUsingCoder:
but somehow it was getting called for every encoded object in my storyboard and causing crashes. Overriding - awakeFromNib
wouldn't allow me to read the NSCoder
object.
#import <objc/runtime.h>
NSString *const FORegularFontName = @"Zapfino";
NSString *const FOBoldFontName = @"Zapfino";
NSString *const FOItalicFontName = @"Zapfino";
#pragma mark - UIFont category
@implementation UIFont (CustomFonts)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+ (void)replaceClassSelector:(SEL)originalSelector withSelector:(SEL)modifiedSelector {
Method originalMethod = class_getClassMethod(self, originalSelector);
Method modifiedMethod = class_getClassMethod(self, modifiedSelector);
method_exchangeImplementations(originalMethod, modifiedMethod);
}
+ (void)replaceInstanceSelector:(SEL)originalSelector withSelector:(SEL)modifiedSelector {
Method originalDecoderMethod = class_getInstanceMethod(self, originalSelector);
Method modifiedDecoderMethod = class_getInstanceMethod(self, modifiedSelector);
method_exchangeImplementations(originalDecoderMethod, modifiedDecoderMethod);
}
+ (UIFont *)regularFontWithSize:(CGFloat)size
{
return [UIFont fontWithName:FORegularFontName size:size];
}
+ (UIFont *)boldFontWithSize:(CGFloat)size
{
return [UIFont fontWithName:FOBoldFontName size:size];
}
+ (UIFont *)italicFontOfSize:(CGFloat)fontSize
{
return [UIFont fontWithName:FOItalicFontName size:fontSize];
}
- (id)initCustomWithCoder:(NSCoder *)aDecoder {
BOOL result = [aDecoder containsValueForKey:@"UIFontDescriptor"];
if (result) {
UIFontDescriptor *descriptor = [aDecoder decodeObjectForKey:@"UIFontDescriptor"];
NSString *fontName;
if ([descriptor.fontAttributes[@"NSCTFontUIUsageAttribute"] isEqualToString:@"CTFontRegularUsage"]) {
fontName = FORegularFontName;
}
else if ([descriptor.fontAttributes[@"NSCTFontUIUsageAttribute"] isEqualToString:@"CTFontEmphasizedUsage"]) {
fontName = FOBoldFontName;
}
else if ([descriptor.fontAttributes[@"NSCTFontUIUsageAttribute"] isEqualToString:@"CTFontObliqueUsage"]) {
fontName = FOItalicFontName;
}
else {
fontName = descriptor.fontAttributes[@"NSFontNameAttribute"];
}
return [UIFont fontWithName:fontName size:descriptor.pointSize];
}
self = [self initCustomWithCoder:aDecoder];
return self;
}
+ (void)load
{
[self replaceClassSelector:@selector(systemFontOfSize:) withSelector:@selector(regularFontWithSize:)];
[self replaceClassSelector:@selector(boldSystemFontOfSize:) withSelector:@selector(boldFontWithSize:)];
[self replaceClassSelector:@selector(italicSystemFontOfSize:) withSelector:@selector(italicFontOfSize:)];
[self replaceInstanceSelector:@selector(initWithCoder:) withSelector:@selector(initCustomWithCoder:)];
}
#pragma clang diagnostic pop
@end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With