1

I have made a lot of investigation in this topic, but yet, I haven't found a way to set a custom font for an entire IOS app easily. I need to change the whole font for a very large app and I already did it really easily in Android.

Answers with the solutions seem to be either outdated for objective-C, or simply too complicated for this task.

So far I have found 3 main ways to do this:

  1. Traditional approach: using enums or extensions. The problem is that if the app is already advanced is really tedious to apply the font to every single view.
  2. Using extensions of views and .appearance(). The problem is that it sets the font to every single view and you may want to have one UILabel with one font and another with other font.
  3. Using a "Hack" (seems like a good solution) The problem is that its risky and not future proof, cause maybe in the future apple will reject apps like this in the appstore. this approach changes some system UI fonts too so its more like a hack than a native solution.

This solutions are the ones I found, but they are not exactly something flexible. Android has a great way to do it (just add a font family in xml and tadaa, the system picks the right one automatically) and I believe IOS may have it.

Is there any other way? Or else, whats the recommended approach.

Community
  • 1
  • 1
Pedro Antonio
  • 250
  • 1
  • 5
  • 17
  • 1
    As far as I recall, using a .appearance() looks like the best idea when it comes to completely reshape an app. You can locate all the appearance in one file so it is easier to operate change among all the app in the future. With UIAppearance you can define rules for when to apply the theme (e.g. apply only to UILabel if contained in a UITableViewCell) – Olympiloutre Jun 12 '20 at 08:46

2 Answers2

4

Based on this site and making some adjustments

First: You must add your fonts in your project and add them in the info.plist file: https://codewithchris.com/common-mistakes-with-adding-custom-fonts-to-your-ios-app/

File: UIFont.swift

import Foundation
import UIKit

struct AppFontName {
  static let regular = "Montserrat-Regular"
  static let bold = "Montserrat-Bold"
  static let lightAlt = "Montserrat-Light"
}
//customise font
extension UIFontDescriptor.AttributeName {
  static let nsctFontUIUsage = UIFontDescriptor.AttributeName(rawValue: "NSCTFontUIUsageAttribute")
}

extension UIFont {

  @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.lightAlt, 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.lightAlt
    default:
        fontName = AppFontName.regular
    }
    self.init(name: fontName, size: fontDescriptor.pointSize)!
  }

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

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

AppDelegate.swift

override init() {
    super.init()
    UIFont.overrideInitialize()
}

Usage:

label.font = UIFont.boldSystemFont(ofSize: 16)
label.fontUIFont.systemFont(ofSize: 12)
Benjamin RD
  • 11,516
  • 14
  • 87
  • 157
  • Thanks @MrMins I guess something like this is the best option. I wish apple made it simpler! – Pedro Antonio Jun 19 '20 at 20:55
  • @PedroAntonio right, maybe the most simple way is overriding the `systemFont` method. – Benjamin RD Jun 19 '20 at 21:32
  • but do you think is safe for the appstore? I have never used method swizzling – Pedro Antonio Jun 19 '20 at 21:34
  • 1
    Yes @PedroAntonio , is totally safe and I have some projects approved by the AppStore using the code. Also the code is very similar for all the `swift` versions, so is easily updated – Benjamin RD Jun 19 '20 at 21:35
  • HI, Thanks for the nice solution. but this approach also override pickerview font. and text in picker view not appear proper because we use diffrent font rather than systom font. – Bhavesh.iosDev Dec 02 '20 at 12:02
2

You can read this article: Set default custom font for entire app – Swift 5

Or you can use this:

Fonts.swift

Once you import the swift file to your project, just change the default font in this line: class var defaultFontFamily: String { return "Georgia" }

and to use it, you have to register the class in your didFinishLaunchingWithOptions method in the AppDelegate using:

UIFont. initialize()

Benjamin RD
  • 11,516
  • 14
  • 87
  • 157