173

I have a custom font I want to use for everything displaying text in my app, labels, text views etc.

Is there a way to set the default font (labels by default use SystemFont) for the whole app?

Bhavin Ramani
  • 3,221
  • 5
  • 30
  • 41
Sam Jarman
  • 7,277
  • 15
  • 55
  • 100
  • 7
    Having massively looked in to this woe. We have honestly found the simplest thing to do is just make a (trivial) new class, for each control. So for UIButton, make SJButton. Don't forget to override both initWithFrame and initWithCode. For each control (say UIButton, etc) set the colours or whatever you like. Carefully pick up say the size (i.e. that will be the size SET ON THE STORYBOARD) in the int code, and then (as you prefer) use that size (and set, say, the font, colour - whatever). It's only a few lines of code, is incredibly tidy, and saves you vast time in the long run. – Fattie Jul 28 '14 at 13:04
  • @JoeBlow Thanks for posting your findings - was about to spend time looking to answer that myself – Jared Kove Aug 11 '16 at 10:07
  • @jj- Right. Don't forget too you certainly MUST use `IBDesignable` these days. Hope it helps. Also consider this interesting QA: http://stackoverflow.com/a/38000466/294884 – Fattie Aug 11 '16 at 10:19

17 Answers17

160

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.

Randall
  • 14,691
  • 7
  • 40
  • 60
  • 48
    Thanks for the response. I was able to get this to work. Do you know if there is a way to specify the font without specifying the font size? I have labels in my app that don't share the same font size. – Brandon Jan 26 '12 at 19:10
  • 23
    Can I do this without overriding the point size of every instance? – Michael Forrest May 17 '12 at 09:51
  • @Brandon you can specify the font without specifying the font size by setting the UIFont size to 0.0 – codeperson Jul 18 '12 at 04:03
  • 1
    @jonsibley. Are you sure about this? When I tried setting font size to 0.0 I get errors saying ' : CGAffineTransformInvert: singular matrix.' and the font doesn't appear at all. The docs say '[the size] must be greater than 0.0' See http://developer.apple.com/library/ios/#documentation/uikit/reference/UIFont_Class/Reference/Reference.html – Ben Clayton Oct 10 '12 at 11:22
  • 17
    `setFont:` method is deprecated – Anand Oct 30 '12 at 10:53
  • Is there any replacement for setFont now that it is deprecated? – M. Ryan Feb 22 '13 at 15:38
  • 12
    @Anand are you sure about this? I don't see it marked as deprecated in `UILabel`. It is deprecated for `UIButton` but it using the font for the `titleLabel` property instead which is a `UILabel`, so just setting the font with the appearance proxy for `UILabel` should be fine. – Adrian Schönig Feb 23 '13 at 23:43
  • 6
    @Anand it is not deprecated for UILabel. – Alastair Stuart Mar 20 '13 at 01:05
  • @AlastairStuart it's deprecated. – OMGPOP Jun 27 '13 at 14:37
  • 3
    The docs are clear, the _font_ property UILabel is not deprecated. If you are getting warnings it's because +appearance returns an _id_ and XCode is probably looking up the wrong base class (UITableViewCell, for example). http://developer.apple.com/library/ios/documentation/uikit/reference/UILabel_Class/Reference/UILabel.html#//apple_ref/occ/instp/UILabel/font – Ben Flynn Aug 11 '13 at 07:07
  • 1
    @Michael Forrest Were you able to avoid override the point size of every instance eventually? I have the same porblem. Setting font size to 0.0 doesn't work for me as well. – lppier Aug 15 '13 at 01:58
  • 1
    It really just does not work because of the size issue, unfortunately. (And after all, in some projects you want to be able to "set the size" in storyboard, and sometimes it's something else - maybe the colour.) I recommend just giving up and doing what I mention in the doing comment under the answer. It solves all problems. – Fattie Jul 28 '14 at 13:05
  • 1
    This method does not distinguish between different uses of UILabel's. For example in UITableView title and subtitle labels will use the same font and this is clearly not a way to customize you app. And appearanceWhenContainedIn: won't help either. There is no way to distinguish those to label that I'm aware of. – Sergey Skoblikov Feb 12 '15 at 18:08
  • 3
    To keep font, maybe this helps: `[[UILabel appearance] setFont:[UIFont fontWithName:@"YourFontName" size:[UILabel appearance].font.pointSize]];` – Nike Kov Jun 24 '16 at 08:13
139

Swift 5

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()
    }
    ...
}
nahung89
  • 7,745
  • 3
  • 38
  • 40
  • 2
    best answer!! automatically override the system font, brilliant – Kappe Nov 18 '16 at 19:11
  • This is great, but I am struggling to convert to Swift 2.3. In particular, this bit: let initCoderMethod = class_getInstanceMethod(self, Selector(init(coder:))) let myInitCoderMethod = class_getInstanceMethod(self, Selector(init(myCoder:))) – Lee Probert Nov 21 '16 at 18:51
  • No. Sorry. Am migrating over to 3 now anyway. – Lee Probert Jan 19 '17 at 12:25
  • @LeeProbert: No. Made it manually on all controls... I'm migrating to Swift 3, too... – Oliver Apel Feb 01 '17 at 15:29
  • @nahung89 any idea how to override the system style as well? for weight Medium/Thin/light etc – Perwyl Liu Feb 09 '17 at 05:53
  • @PerwylLiu i'm not sure but you can check in `default` if it returns value like `CTFontXXXUsage`. If it has then you could add it as a new case and implement you own customize. – nahung89 Feb 09 '17 at 06:13
  • @nahung89 i did a `print` for `fontAttribute`, it's `CTFontMediumUsage`. Thanks :) – Perwyl Liu Feb 15 '17 at 03:49
  • @nahung89 do you have any idea on how to remove the warnings for `let initCoderMethod = class_getInstanceMethod(self, Selector("initWithCoder:"))` – Perwyl Liu Feb 24 '17 at 03:52
  • @PerwylLiu as I know there is no way so far. Hope other guys can solve this warning. – nahung89 Feb 24 '17 at 04:34
  • 2
    If somebody has problems with the "NSCTFontUIUsageAttribute" line: `if let fontAttribute = fontDescriptor.fontAttributes[.nsctFontUIUsage] as? String {` did the trick for me. – DrDirk Nov 07 '17 at 19:23
  • if let fontAttribute = fontDescriptor.fontAttributes[UIFontDescriptor.AttributeName(rawValue: "NSCTFontUIUsageAttribute")] as? String { – Perwyl Liu Dec 28 '17 at 07:27
  • When I implement this, it's never getting through `if let fontDescriptor = aDecoder.decodeObject(forKey: "UIFontDescriptor") as? UIFontDescriptor {` (i.e., it always skips to the `else` clause). Also it makes me add `@objc` to all the `myXXSystemFont` methods and the `convenience init`. – Jonathan Tuzman Jan 23 '18 at 05:09
  • 1
    does not seems to work with `UILabel`s that set text-style (bold,headline,title, etc)... only works with font that have a specific size and system font set. @nahung89 – Aviran Aug 07 '18 at 12:10
  • 1
    This also switches some system UI fonts. For example, the keyboard suggestion list and the action sheet. Don't know if this will cause Apple to reject the app – Henry H Miao Nov 21 '18 at 01:35
  • @HenryHMiao No it won't. I have some apps which are already available on Appstore, i.e iAntiTheft. – nahung89 Nov 22 '18 at 14:26
  • 1
    Its been a year since this answer has been posted. Does anyone have a more "native" Apple way to achieve this? – Greg Hilston May 07 '19 at 18:01
  • How can I use "semibold" with your code?, I had tried something but not worked well. pls help me – midhun p Jul 22 '19 at 05:09
  • @midhunp you can use `CTFontDemiUsage` font attribute for checking semi-bold font. Also provide semi-bold-font name in `AppFontName` enumeration. It's pretty straight-forward dude :D – nahung89 Jul 23 '19 at 17:56
  • 1
    i have used above code and work perfect. but i have get one problem. i have 2 language "English" and "Thai". when i change thai language my password securetextfield not display text. thats why i want to stop above code for one textField. please help me out this problem. thanks. – Jay Bhalani Apr 11 '20 at 04:44
  • @nahung89 I'm also calling this function when I want to change the app font but in this case, the new font applies only after restarting the app, do you know why ? – Ahmad Mahmoud Saleh Oct 10 '21 at 18:59
75

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.

Bhavin Ramani
  • 3,221
  • 5
  • 30
  • 41
Hugues BR
  • 2,238
  • 1
  • 20
  • 26
  • 1
    Is this within apple guidelines? – Rambatino Aug 22 '13 at 13:48
  • It's more a hack. I haven't add my app register with this hack, as it's not using any private methods. If you want a more robust solution, I also suggest you to check this: https://github.com/0xced/FontReplacer – Hugues BR Aug 22 '13 at 17:28
  • 2
    This is fine. Apple just don't want you using undocumented functions. You are allowed to “swizzle” methods that are public. – mxcl Feb 06 '14 at 20:56
  • I have this implemented but it doesn't always work. For instance on UITableViewCell with default styles it doesn't apply my custom font. Actually I can see the method is called but the size I get is 0. Any ideas? – Fábio Oliveira Apr 09 '14 at 15:43
  • 4
    When a category has methods with the same signature as the class it's extending the behaviour is undefined. To replace class methods you should use method swizzling (which also isn't a good idea). – GreatWiz May 14 '14 at 15:06
  • (1) this is 1000% ok from Apple's point of view (2) using a category on a default like this, usually sort of "causes an argument" amongst programmers. for example, the very best, perhaps only decent, way to "hide the status bar" in an app on iOS7 is like this .. http://stackoverflow.com/a/21034908/294884 ... but you always get some programmers who argue (quite rightly) that such categories are "undefined" in current compilers (although it works 100% perfectly) Hope it helps someone – Fattie Jul 28 '14 at 12:50
  • Brilliant ! An excellent way to change the Font on all controls, even Navigation Bar titles. – Mike Gledhill Dec 05 '14 at 12:31
  • my question is if i implement this ,font size will also over rided. how to avoid this ? – Krutarth Patel May 09 '15 at 07:43
  • Thanks for this solution, great! – rebellion Jun 15 '15 at 13:58
  • 1
    As others have pointed out, this solution, while likely to be effective the majority of the time, is technically introducing the potential for undefined behavior. If you don't feel like risking it, method swizzling might be a better option. The answer here provides the same solution via swizzling: http://stackoverflow.com/questions/19542942/how-to-set-custom-font-family-as-system-font-in-ios-application – Nathan Hosselton Jul 31 '15 at 17:46
  • "This will replace the default implementation and most UIControls use systemFont." is just plain wrong and I don't understand why it gets so many up-votes (the fact that this might work doesn't make it right). A category is not a subclass, you're not guaranteed that a duplicated implementation (as this shouldn't be called an override) will be used by the compiler - it is undefined whether the compiler will choose the original implementation or what you put in the category. If you really want to pursue this sort of approach you should looking into runtime swizzling, as already pointed it out. – tanz May 31 '17 at 10:15
  • I disagree with that link to a swizzling solution, it attempts to override the load method which has the exact same risk as this answer. Do the swizzling somewhere else, not in a category. – malhal Sep 20 '17 at 02:04
  • This is done in a one liner here (Swift 4)... https://stackoverflow.com/a/51390172/3900139 – Rmalmoe Jul 17 '18 at 21:07
69

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) .

Apple_Magic
  • 477
  • 8
  • 26
Sandy Chapman
  • 11,133
  • 3
  • 58
  • 67
  • One downside to this that I've found is that when I initiate alerts with UIAlertController, the button with the .Cancel style is the same as the button with the .Default style (at least when using GillSans). Whereas normally .Cancel would be a regular weight font, and .Default would be bold. Any ideas? – Mason G. Zhwiti Apr 26 '15 at 15:26
  • Sorry, I meant .Cancel labels would normally be bold, and Default would normally be regular weight. – Mason G. Zhwiti Apr 26 '15 at 15:40
  • 2
    @MasonG.Zhwiti In that case, I'd likely set up the `UILabel` extension to take an additional font name for bold. Then in the `set` do a check to see if "Bold" is in the font name and ignore the set in one case, and use it in the other. I'll edit and add an example. – Sandy Chapman May 07 '15 at 12:03
  • @SandyChapman Thanks! I'm trying this new technique, and it makes sense, but it doesn't seem to be working for me. I'm using GillSans and GillSans-Bold on iOS 8.3 simulator. Have you tested this technique? – Mason G. Zhwiti May 07 '15 at 16:35
  • @MasonG.Zhwiti I've not tested this. I haven't done any Swift programming in several months. I'd suggest setting a breakpoint in where the fonts are set / loaded and ensure that they're being set properly. Also, the font names don't necessarily match the file names of the fonts, so that's something to watch out for. I'd suggest [using this](http://stackoverflow.com/a/8529661/1270148) to print out all the names of the fonts that are available within your app. – Sandy Chapman May 07 '15 at 17:03
  • 2
    @SandyChapman I figured out what's going on. The default fonts for iOS 8 tend to be either HelveticaNeueInterface-Regular or (for bold) HelveticaNeueInterface-MediumP4. So looking for "Bold" was never matching anything. I changed it to `rangeOfString("Medium")` and it worked. – Mason G. Zhwiti May 09 '15 at 17:03
  • For checking between bold and medium use `font.description` and check if the word medium and bold are in there... if bold is in there, then it's bold... if only medium is in there, then it's medium The downside of this solution is that it only works on UILabels... not on UITextView, ect... – Georg Aug 12 '15 at 13:48
  • This is brilliant! Not only does it solve the problem neatly and concisely, but it's explained a lot about UIAppearance too. – Andy Mortimer Nov 21 '15 at 08:34
  • the only & big issue with UILabel.appearance() is it changes all font with same style either bold or regular whatever u set .... – Apple_Magic Jun 06 '20 at 12:28
  • @iWatch that is what the question is asking for though. If you want to only apply to certain types, you can always subclass and do a MyLabel.appearance() instead to only apply it to MyLabel instances. – Sandy Chapman Jun 06 '20 at 19:21
22

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:

  • the code below, in its presented state, was never submitted to Apple approval;
  • there is a shorter version of it that passed Apple submission, that is without the - initWithCoder: override. However that won't cover all the cases;
  • the following code is present in a class I use to set the style of my app which is included by my AppDelegate class thus being available everywhere and to all UIFont instances;
  • I'm using Zapfino here just to make the changes much more visible;
  • any improvement you may find to this code is welcome.

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
malhal
  • 26,330
  • 7
  • 115
  • 133
Fábio Oliveira
  • 2,346
  • 21
  • 30
  • How you use this implementation on iOS6 which dont have UIFontDescriptor – Utku Yıldırım Apr 30 '14 at 14:27
  • I've used the decoder key "UIFontTraits" to check if the font provided is bold or italic and replace it with my own variations. Got it from this gist here https://gist.github.com/Daij-Djan/5046612. – Fábio Oliveira May 05 '14 at 13:12
  • Thanks for answer i used another solution for now. I will check it when i need it again :) – Utku Yıldırım May 06 '14 at 07:30
  • 2
    thank @FábioOliveira, it works like a charm! Just one more thing that you need to put #import on the header, otherwise you will get error by using 'Method' class (error I get in XCode 6) – nahung89 Oct 10 '14 at 06:12
  • For some reason, on iOS 8, modals (`UIAlertController`) don't change font. – Randomblue Apr 16 '15 at 05:13
  • @Fábio Oliveira, your code is better, Apart from this please note that if there are multiple font types set in your project, and you want to SET ONLY ONE then change your this line code : else { fontName = descriptor.fontAttributes[@"NSFontNameAttribute"]; } to else { fontName =@"yourFontName"; } – Ravi Apr 18 '17 at 16:16
  • I have added following conditions and now this covers all the cases, might be helpful for someone. else if ([descriptor.fontAttributes[@"NSCTFontUIUsageAttribute"] isEqualToString:@"CTFontDemiUsage"]) { fontName = FOSemiBoldFontName; } else if ([descriptor.fontAttributes[@"NSCTFontUIUsageAttribute"] isEqualToString:@"CTFontMediumUsage"]) { fontName = FOSemiBoldFontName; } else if ([descriptor.fontAttributes[@"NSCTFontUIUsageAttribute"] isEqualToString:@"CTFontHeavyUsage"]) { fontName = FOBoldFontName; } – Mubeen Qazi Aug 15 '18 at 08:26
14

To complete Sandy Chapman's answer, here is a solution in Objective-C (put this category anywhere you want to change UILabel Appearance):

@implementation UILabel (FontOverride)
- (void)setSubstituteFontName:(NSString *)name UI_APPEARANCE_SELECTOR {
    self.font = [UIFont fontWithName:name size:self.font.pointSize];
}
@end

The interface file, should have this method declared publicly to be used later from places like your app delegate:

@interface UILabel (FontOverride)
  - (void)setSubstituteFontName:(NSString *)name UI_APPEARANCE_SELECTOR;
@end

Then, you can change the Appearance with:

[[UILabel appearance] setSubstituteFontName:@"SourceSansPro-Light"];
Damien Debin
  • 2,812
  • 25
  • 41
  • 1
    Hi, do you mean "anywhere", that code would have to be added in every view controller, and have every UILabel used in the controller, where you wished to change the font? – Jules Jun 20 '15 at 19:17
  • No, you have to put this code **once**, anywhere in your project ; in your _appdelegate_ for instance. – Damien Debin Jun 22 '15 at 11:51
  • 1
    @DamienDebin I want to use bold font for bold but this changes bold to light. Any way around? – Zeeshan Oct 19 '15 at 11:23
  • Actually, it works, But should it be "Category" or "Extention"? The difference explained here: http://stackoverflow.com/questions/7136124/class-extension-vs-class-category – Darius Miliauskas Dec 08 '15 at 19:42
5

I created my own conversion of typography for Swift 4 after reviewing a few posts, it covers most of the cases, such as:

1st Add fonts to project estructure and to .plist file (with the same name):

<key>UIAppFonts</key>
<array>
    <string>Typo-Light.ttf</string>
    <string>Typo-Regular.ttf</string>
    <string>Typo-Semibold.ttf</string>
    <string>Typo-LightItalic.ttf</string>
</array>

Then

struct Resources {

    struct Fonts {
        //struct is extended in Fonts
    }
}

extension Resources.Fonts {

    enum Weight: String {
        case light = "Typo-Light"
        case regular = "Typo-Regular"
        case semibold = "Typo-Semibold"
        case italic = "Typo-LightItalic"
    }
}

extension UIFontDescriptor.AttributeName {
    static let nsctFontUIUsage = UIFontDescriptor.AttributeName(rawValue: "NSCTFontUIUsageAttribute")
}

extension UIFont {

    @objc class func mySystemFont(ofSize: CGFloat, weight: UIFont.Weight) -> UIFont {
        switch weight {
        case .semibold, .bold, .heavy, .black:
            return UIFont(name: Resources.Fonts.Weight.semibold.rawValue, size: ofSize)!

        case .medium, .regular:
            return UIFont(name: Resources.Fonts.Weight.regular.rawValue, size: ofSize)!

        default:
            return UIFont(name: Resources.Fonts.Weight.light.rawValue, size: ofSize)!
        }
    }

    @objc class func mySystemFont(ofSize size: CGFloat) -> UIFont {
        return UIFont(name: Resources.Fonts.Weight.light.rawValue, size: size)!
    }

    @objc class func myBoldSystemFont(ofSize size: CGFloat) -> UIFont {
        return UIFont(name: Resources.Fonts.Weight.semibold.rawValue, size: size)!
    }

    @objc class func myItalicSystemFont(ofSize size: CGFloat) -> UIFont {
        return UIFont(name: Resources.Fonts.Weight.italic.rawValue, 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", "CTFontMediumUsage":
            fontName = Resources.Fonts.Weight.regular.rawValue
        case "CTFontEmphasizedUsage", "CTFontBoldUsage", "CTFontSemiboldUsage","CTFontHeavyUsage", "CTFontBlackUsage":
            fontName = Resources.Fonts.Weight.semibold.rawValue
        case "CTFontObliqueUsage":
            fontName = Resources.Fonts.Weight.italic.rawValue
        default:
            fontName = Resources.Fonts.Weight.light.rawValue
        }
        self.init(name: fontName, size: fontDescriptor.pointSize)!
    }

    class func overrideDefaultTypography() {
        guard self == UIFont.self else { return }

        if let systemFontMethodWithWeight = class_getClassMethod(self, #selector(systemFont(ofSize: weight:))),
            let mySystemFontMethodWithWeight = class_getClassMethod(self, #selector(mySystemFont(ofSize: weight:))) {
            method_exchangeImplementations(systemFontMethodWithWeight, mySystemFontMethodWithWeight)
        }

        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:))),
            let myInitCoderMethod = class_getInstanceMethod(self, #selector(UIFont.init(myCoder:))) {
            method_exchangeImplementations(initCoderMethod, myInitCoderMethod)
        }
    }
}

Finally call to created method at Appdelegate like next:

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {

        UIFont.overrideDefaultTypography()
        return true
    }
}
Javi AP
  • 392
  • 3
  • 8
4

COMMENT FOR SWIFT 3.0 AND SWIFT WARNING

You can remove the warning message in line:

let initCoderMethod = class_getInstanceMethod(self, Selector("initWithCoder:"))

By replacing it with:

let initCoderMethod = class_getInstanceMethod(self, #selector(UIFontDescriptor.init(coder:)))
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
ucotta
  • 557
  • 4
  • 7
  • Lol. I didn't know that Swift allow using this. Btw, I will update it to above answer. Thanks you @ucotta! – nahung89 Apr 03 '17 at 08:45
4

For Swift 5

All the above answers are correct but i have done in little different way that is according to device size. Here, in ATFontManager class, i have made default font size which is define at the top of the class as defaultFontSize, this is the font size of iphone plus and you can changed according to your requirement.

class ATFontManager: UIFont{
    
    class func setFont( _ iPhone7PlusFontSize: CGFloat? = nil,andFontName fontN : String = FontName.helveticaNeue) -> UIFont{
        
        let defaultFontSize : CGFloat = 16
        
        switch ATDeviceDetector().screenType {
            
        case .iPhone4:
            if let fontSize = iPhone7PlusFontSize{
                return UIFont(name: fontN, size: fontSize - 5)!
            }
            return UIFont(name: fontN, size: defaultFontSize - 5)!
            
        case .iPhone5:
            if let fontSize = iPhone7PlusFontSize{
                return UIFont(name: fontN, size: fontSize - 3)!
            }
            return UIFont(name: fontN, size: defaultFontSize - 3)!
            
        case .iPhone6AndIphone7, .iPhoneUnknownSmallSize:
            if let fontSize = iPhone7PlusFontSize{
                return UIFont(name: fontN, size: fontSize - 2)!
            }
            return UIFont(name: fontN, size: defaultFontSize - 2)!
            
        case .iPhone6PAndIPhone7P, .iPhoneUnknownBigSize:
            
            return UIFont(name: fontN, size: iPhone7PlusFontSize ?? defaultFontSize)!
        case .iPhoneX, .iPhoneXsMax:
            
            return UIFont(name: fontN, size: iPhone7PlusFontSize ?? defaultFontSize)!
          
        case .iPadMini:
            if let fontSize = iPhone7PlusFontSize{
                return UIFont(name: fontN, size: fontSize + 2)!
            }
            return UIFont(name: fontN, size: defaultFontSize + 2)!
            
        case .iPadPro10Inch:
            if let fontSize = iPhone7PlusFontSize{
                return UIFont(name: fontN, size: fontSize + 4)!
            }
            return UIFont(name: fontN, size: defaultFontSize + 4)!
            
        case .iPadPro:
            if let fontSize = iPhone7PlusFontSize{
                return UIFont(name: fontN, size: fontSize + 6)!
            }
            return UIFont(name: fontN, size: defaultFontSize + 6)!
            
        case .iPadUnknownSmallSize:
            
            return UIFont(name: fontN, size: defaultFontSize + 2)!
            
        case .iPadUnknownBigSize:
            
            return UIFont(name: fontN, size: defaultFontSize + 4)!
            
        default:
            
            return UIFont(name: fontN, size: iPhone7PlusFontSize ?? 16)!
        }
    }
}
     

I have added certain font name, for more you can add the font name and type here.

   enum FontName : String {
        case HelveticaNeue = "HelveticaNeue"
        case HelveticaNeueUltraLight = "HelveticaNeue-UltraLight"
        case HelveticaNeueBold = "HelveticaNeue-Bold"
        case HelveticaNeueBoldItalic = "HelveticaNeue-BoldItalic"
        case HelveticaNeueMedium = "HelveticaNeue-Medium"
        case AvenirBlack = "Avenir-Black"
        case ArialBoldMT = "Arial-BoldMT"
        case HoeflerTextBlack = "HoeflerText-Black"
        case AMCAPEternal = "AMCAPEternal"
    }

This class refers to device detector in order to provide appropriate font size according to device.

class ATDeviceDetector {
    
    var iPhone: Bool {
        
        return UIDevice().userInterfaceIdiom == .phone
    }
    
    var ipad : Bool{
        
        return UIDevice().userInterfaceIdiom == .pad
    }
    
    let isRetina = UIScreen.main.scale >= 2.0
    
    
    enum ScreenType: String {
        
        case iPhone4
        case iPhone5
        case iPhone6AndIphone7
        case iPhone6PAndIPhone7P
        case iPhoneX
        
        case iPadMini
        case iPadPro
        case iPadPro10Inch
        
        case iPhoneOrIPadSmallSizeUnknown
        case iPadUnknown
        case unknown
    }
    
    
    struct ScreenSize{
        
        static let SCREEN_WIDTH         = UIScreen.main.bounds.size.width
        static let SCREEN_HEIGHT        = UIScreen.main.bounds.size.height
        static let SCREEN_MAX_LENGTH    = max(ScreenSize.SCREEN_WIDTH,ScreenSize.SCREEN_HEIGHT)
        static let SCREEN_MIN_LENGTH    = min(ScreenSize.SCREEN_WIDTH,ScreenSize.SCREEN_HEIGHT)
    }
    
    
    var screenType: ScreenType {
        
        switch ScreenSize.SCREEN_MAX_LENGTH {
            
        case 0..<568.0:
            return .iPhone4
        case 568.0:
            return .iPhone5
        case 667.0:
            return .iPhone6AndIphone7
        case 736.0:
            return .iPhone6PAndIPhone7P
        case 812.0:
            return .iPhoneX
        case 568.0..<812.0:
            return .iPhoneOrIPadSmallSizeUnknown
        case 1112.0:
            return .iPadPro10Inch
        case 1024.0:
            return .iPadMini
        case 1366.0:
            return .iPadPro
        case 812.0..<1366.0:
            return .iPadUnknown
        default:
            return .unknown
        }
    }
}

How to use. Hope it will help.

//for default 
label.font = ATFontManager.setFont()

//if you want to provide as your demand. Here **iPhone7PlusFontSize** variable is denoted as font size for *iphone 7plus and iphone 6 plus*, and it **ATFontManager** class automatically handle.
label.font = ATFontManager.setFont(iPhone7PlusFontSize: 15, andFontName: FontName.HelveticaNeue.rawValue)
Community
  • 1
  • 1
Amrit Tiwari
  • 922
  • 7
  • 21
3

None of these solutions works universally throughout the app. One thing I found to help manage the fonts in Xcode is opening the Storyboard as Source code (Control-click storyboard in Files navigator > "Open as" > "Source"), and then doing a find-and-replace.

Gingi
  • 2,149
  • 1
  • 18
  • 34
3

Font type always be set in code and nib/storyboard.

For the code,just like Hugues BR said,do it in catagory can solve the problem.

For the nib/storyboard,we can Method Swizzling awakeFromNib to change font type since UI element from nib/storyboard always call it before show in the screen.

I suppose you know Aspects.It's a library for AOP programing,based on Method Swizzling. We create catagory for UILabel,UIButton,UITextView to implement it.

UILabel:

#import "UILabel+OverrideBaseFont.h"
#import "Aspects.h"

@implementation UILabel (OverrideBaseFont)

+ (void)load {
    [[self class]aspect_hookSelector:@selector(awakeFromNib) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) {
        UILabel* instance = [aspectInfo instance];
        UIFont* font = [UIFont fontWithName:@"HelveticaNeue-light" size:instance.font.pointSize];
        instance.font = font;
    }error:nil];
}

@end

UIButton:

#import "UIButton+OverrideBaseFont.h"
#import "Aspects.h"

@implementation UIButton (OverrideBaseFont)

+ (void)load {
    [[self class]aspect_hookSelector:@selector(awakeFromNib) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) {
        UIButton* instance = [aspectInfo instance];
        UILabel* label = instance.titleLabel;
        UIFont* font = [UIFont fontWithName:@"HelveticaNeue-light" size:label.font.pointSize];
        instance.titleLabel.font = font;
    }error:nil];
}

@end

UITextField:

#import "UITextField+OverrideBaseFont.h"
#import "Aspects.h"

@implementation UITextField (OverrideBaseFont)

+ (void)load {
    [[self class]aspect_hookSelector:@selector(awakeFromNib) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) {
        UITextField* instance = [aspectInfo instance];
        UIFont* font = [UIFont fontWithName:@"HelveticaNeue-light" size:instance.font.pointSize];
        instance.font = font;
    }error:nil];
}

@end

UITextView:

#import "UITextView+OverrideBaseFont.h"
#import "Aspects.h"

@implementation UITextView (OverrideBaseFont)

+ (void)load {
    [[self class]aspect_hookSelector:@selector(awakeFromNib) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) {
        UITextView* instance = [aspectInfo instance];
        UIFont* font = [UIFont fontWithName:@"HelveticaNeue-light" size:instance.font.pointSize];
        instance.font = font;
    }error:nil];
}

@end

That's all,you can change HelveticaNeue-light to a macro with your font name.

Community
  • 1
  • 1
2

For Xamarin.iOS inside AppDelegate's FinishedLaunching() put code like this :-

UILabel.Appearance.Font= UIFont.FromName("Lato-Regular", 14);

set font for the entire application and Add 'UIAppFonts' key on Info.plist , the path should be the path where your font file .ttf is situated .For me it was inside 'fonts' folder in my project.

<key>UIAppFonts</key>
    <array>
        <string>fonts/Lato-Regular.ttf</string>
    </array>
Annu
  • 449
  • 6
  • 18
  • 1
    this works, but has no use - because now all the text in your app is of the same size 14... There's probably no way to override it in such way that it will have it's original elements font size – Async- Feb 03 '21 at 13:42
2

As @Randall in his answer mentioned iOS 5.0+ UIAppearance proxy can be used to customize the appearance of all instances of a class read more.

UILabel.appearance().font = .systemFont(ofSize: 17, weight: .regular)
esesmuedgars
  • 139
  • 1
  • 9
2

Probably not, you will probably have the set the font on your control yourself, but you can make the process easier by centralizing where you get the font types from, for example have the app delegate or some other common class have a method that returns the font, and anything needing to set the font can call that method, that will help in case you need to change your font, youd change it in one place rather than everywhere you set the fonts...Another alternative can be to make subclasses of your UI Elements that will automatically set the font, but that might be overkill..

Daniel
  • 22,363
  • 9
  • 64
  • 71
  • for the record, this is what I did, but @Randall needed the rep, and provided a good answer. I just need to support less than 5.0 – Sam Jarman Jan 03 '12 at 07:40
  • 4
    I disagree with what you did. The answer you selected is not valid when your question is tagged iphone-sdk-4.0. – Paulo Casaretto Apr 27 '12 at 13:08
  • @Sam Jarman, Randall's answer below is correct - can you mark it that way for future visitors? – Bill Dec 04 '13 at 15:08
1

NUI is an alternative to the UIAppearance proxy. It gives you control over the font (and many other attributes) of a large number of UI element types throughout your application by simply modifying a style sheet, which can be reused across multiple applications.

After adding a NUILabel class to your labels, you could easily control their font in the style sheet:

LabelFontName    String    Helvetica

If you have labels with different font sizes, you could control their sizes using NUI's Label, LargeLabel, and SmallLabel classes, or even quickly create your own classes.

Tom
  • 1,007
  • 12
  • 13
1

Am using like this type of font class in swift. Using font extension class.

enum FontName: String {

  case regular      = "Roboto-Regular"

}

//MARK: - Set Font Size
enum FontSize: CGFloat {
    case size = 10

}
extension UIFont {

    //MARK: - Bold Font
  class var regularFont10: UIFont {
        return UIFont(name: FontName.regular.rawValue, size:FontSize.size.rawValue )!
    }
}
Karthick C
  • 1,519
  • 17
  • 16
-1

We have achieved the same in Swift -Xcode 7.2 using Parent View Controller and Child view controller (Inheritance).

File - New - Cocoa Touch class - ParentViewController.

    import UIKit
    import Foundation

    class ParentViewController: UIViewController {

        var appUIColor:UIColor = UIColor.redColor()
        var appFont:UIFont = UIFont(name: "Copperplate", size: 20)!

        override func viewDidLoad() {
            super.viewDidLoad()
        }
        func addStatusBar()
        {
            let view = UIView(frame:
                CGRect(x: 0.0, y: 0.0, width: UIScreen.mainScreen().bounds.size.width, height: 20.0)
            )
            view.backgroundColor = appUIColor
            self.view.addSubview(view)
        }
    }    

Make child view controllers and associate with a StoryBoard VC, add a textLabel.

    import UIKit

    class FontTestController: ParentViewController {
        @IBOutlet var testLabel: UILabel!

        override func viewDidLoad() {
            super.viewDidLoad()
            testLabel.font =  appFont
            testLabel.textColor = appUIColor
        }

OR Make a custom UILabel Class(Sub classing method) and associate required labels to it.

import Foundation
import UIKit

class CustomFontLabel: UILabel {
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        backgroundColor = ParentViewController().appUIColor
        font = ParentViewController().appFont
        textColor = UIColor.blackColor()
    }
}

Note: The Font and colour declared in Parent VC are implemented in CustomFontLabel . The advantage is we can alter the properties of uilabel/any view all together in some simple changes in Parent VC.

2)'for' looping UIView for sub views. It works only on a particular VC.

    override func viewWillLayoutSubviews() {
            for view in self.view.subviews  {
                if view.isKindOfClass(UITextField) {
                UITextField.appearance().font =  UIFont(name: "Copperplate", size: 20)
                }
                if view.isKindOfClass(UILabel) {
                    UILabel.appearance().font =  UIFont(name: "Copperplate", size: 20)    
                }               
            }       
        }
Sergio Toledo Piza
  • 793
  • 1
  • 6
  • 27
Alvin George
  • 14,148
  • 92
  • 64