我有一个自定义字体,希望在我的应用程序中显示的所有文本(标签,文本视图等)都使用该字体。
有没有一种方法可以为整个应用程序设置默认字体(标签默认使用 SystemFont)?
看起来在iOS 5中使用UIAppearance代理是可能的。
[[UILabel appearance] setFont:[UIFont fontWithName:@"YourFontName" size:17.0]];
这将为你的应用程序中的所有UILabel设置字体为你自定义的字体。 你需要为每个控件(UIButton,UILabel等)重复此步骤。
请注意,您需要在info.plist中添加UIAppFonts值并包含您要添加的字体的名称。
setFont:
方法已被弃用。 - AnandUILabel
中标记为已弃用。它对于UIButton
而言是被弃用的,但是它仍然可以使用在titleLabel
属性上的字体,该属性对应的是一个UILabel
,因此只需使用UILabel
的外观代理来设置字体就可以了。 - Adrian Schönig根据 Fábio Oliveira 的回答 (https://dev59.com/12oy5IYBdhLWcg3wMLSj#23042694),我制作了自己的 Swift 4 扩展。
简而言之,这个扩展更改了默认函数 init(coder:)
、systemFont(ofSize:)
、boldSystemFont(ofSize:)
和 italicSystemFont(ofSize:)
,用我的自定义方法代替它们。
请注意,这并不是完全实现,但你可以根据我的实现交换更多的方法。
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()
}
...
}
if let fontAttribute = fontDescriptor.fontAttributes[.nsctFontUIUsage] as? String {
,这个方法对我很有帮助。 - DrDirk还有另外一种解决方案,那就是覆盖系统字体。
只需创建一个分类
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
这将替换默认实现,大多数 UIControls 使用 systemFont。
如果您正在使用Swift,可以创建一个UILabel扩展:
extension UILabel {
@objc var substituteFontName : String {
get { return self.font.fontName }
set { self.font = UIFont(name: newValue, size: self.font.pointSize) }
}
}
然后在你进行外观代理的地方:
UILabel.appearance().substituteFontName = applicationFont
有等价的Objective-C代码,使用UI_APPEARANCE_SELECTOR
在名为substituteFontName
的属性上。
补充
如果您想单独设置粗体和正常字体:
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)
}
}
}
}
然后针对您的UIAppearance代理:
UILabel.appearance().substituteFontName = applicationFont
UILabel.appearance().substituteFontNameBold = applicationFontBold
注意:如果您发现粗体替换无效,则可能默认字体名称不包含“Medium”。根据需要将该字符串替换为另一个匹配项(感谢下面评论中的Mason)。rangeOfString("Medium")
,然后它就可以工作了。 - Mason G. Zhwiti参考Hugues BR的回答,但是使用方法交换(Method Swizzling)的方式,我成功地把我的应用中所有字体都改为了期望的字体。
iOS 7上应该采用Dynamic Type方法。以下解决方案不使用Dynamic Type。
注意事项:
- initWithCoder:
覆写。然而这并不能涵盖所有情况;这个解决方案使用了两种不同的方法来达到最终结果。第一种是覆盖UIFont类方法+ systemFontWithSize:
和使用我的替代方法的类似方法(在这里我使用“Zapfino”以确保替换成功)。
另一种方法是覆盖UIFont的- initWithCoder:
方法,将任何出现的CTFontRegularUsage
和类似的内容都替换为我的替代方法。
这种方法是必要的,因为我发现在NIB文件中编码的UILabel
对象不会检查+ systemFontWithSize:
方法来获取它们的系统字体,而是将它们编码为UICTFontDescriptor
对象。
我尝试覆盖- awakeAfterUsingCoder:
,但不知何故它为我的Storyboard中的每个编码对象都调用了一次,导致崩溃。覆盖- awakeFromNib
则无法读取NSCoder
对象。
#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
UIAlertController
)不会更改字体。 - Randomblue为了补充 Sandy Chapman的回答,这里给出了一个使用Objective-C的解决方案(将这个类别放在你想要更改 UILabel
Appearance
的任何地方):
@implementation UILabel (FontOverride)
- (void)setSubstituteFontName:(NSString *)name UI_APPEARANCE_SELECTOR {
self.font = [UIFont fontWithName:name size:self.font.pointSize];
}
@end
接口文件应该公开声明此方法,以便稍后从诸如您的应用程序委托之类的位置使用:接口文件应该公开声明此方法,以便稍后从诸如您的应用程序委托之类的位置使用:
@interface UILabel (FontOverride)
- (void)setSubstituteFontName:(NSString *)name UI_APPEARANCE_SELECTOR;
@end
然后,您可以使用以下方式更改 外观
:
[[UILabel appearance] setSubstituteFontName:@"SourceSansPro-Light"];
<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>
那么
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)
}
}
}
最后,在Appdelegate
中调用创建的方法,如下所示:
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
UIFont.overrideDefaultTypography()
return true
}
}
适用于Swift 5
以上所有答案都是正确的,但我按照设备大小的方式进行了一些不同的处理。在ATFontManager类中,我设置了默认字体大小,该大小在类的顶部定义为defaultFontSize,这是iPhone Plus的字体大小,您可以根据自己的要求进行更改。
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)!
}
}
}
我已经添加了某个字体名称,如果需要更多,可以在此处添加字体名称和类型。
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"
}
这个类是指设备检测器,目的是根据设备提供合适的字体大小。
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
}
}
}
如何使用。希望这可以帮助。
//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)
针对Swift 3.0和Swift警告的注释
您可以在以下行中删除警告信息:
let initCoderMethod = class_getInstanceMethod(self, Selector("initWithCoder:"))
用以下内容替换它:
let initCoderMethod = class_getInstanceMethod(self, #selector(UIFontDescriptor.init(coder:)))
这些解决方案并不适用于整个应用程序。我发现在Xcode中帮助管理字体的一件事是以源代码方式打开Storyboard(在文件导航器中控制点击storyboard,然后选择“打开为”>“源代码”),然后进行查找和替换。
IBDesignable
。希望能有所帮助。另外,考虑一下这个有趣的QA:https://dev59.com/questions/JZjga4cB1Zd3GeqPIVHJ#38000466 - Fattie