iOS 如何全局修改项目字体

一. 引言

在产品或设计同学的走查过程中,经常会提出一个问题:在宽屏 iPhone 上,字体看起来好像变小了,需要进行适配。可能你会有点摸不着头脑------明明代码里写的都是 16pt,字体怎么会变小呢?

他们可能会说是屏幕分辨率高、屏幕宽度大之类的原因。先说结论:字体没有变小。在 iOS 里,字体的大小一直是 16pt,不会因为分辨率更高而变小。所谓"变小",其实是一种视觉错觉。

原因在于:随着屏幕宽度增加,内容布局相对变得更松散,人眼的感知就会觉得字体偏小。而 Retina 屏幕只是用更多像素去显示同样大小的文字,让字体更清晰,而不是更小。

不过他们的诉求应该还是确定的,就是希望把字体在宽手机上放大一点,所以我们还是要做一些调整。

二. iOS 字体渲染原理

在讲解解决方案之前呢,我们先看看 iOS 字体是如何渲染的。核心概念有两个:逻辑尺寸(pt)和物理像素(px)

2.1 pt 与 px 的区别

  • pt(point,点):iOS 中的逻辑尺寸单位。无论设备屏幕多大,16pt 的文字在逻辑上都是相同大小。
  • px(像素):屏幕的物理像素数量。Retina 屏幕用更多的像素去描绘同样大小的逻辑尺寸,从而让文字更清晰。

举个例子:

机型 屏幕宽度(pt) 屏幕像素(px) 字体 16pt 对应像素
iPhone 8 375pt 750px 32px(2x)
iPhone 15 Pro 393pt 1179px 48px(3x)

可以看到,逻辑尺寸(16pt)没有改变,只是物理像素更多了,字体更清晰而已。

2.2 为什么宽屏手机上字体"看起来小"?

即便逻辑尺寸没变,人的视觉仍然会有错觉,主要原因有两个:

1.屏幕宽度增加,布局变松散

  • 当屏幕宽度 > 375pt 时,原本紧凑的文字和控件之间的空间变大,文字在视觉上占比下降。
  • 结果就是"看起来比以前小"。

2.视觉感知规律

  • 人眼对大面积空间中的相对大小更敏感,小文字在宽屏上容易被周围空白"吞掉"。

所以,字体本身一直是 16pt,只是视觉上产生了"偏小"的错觉

iOS 字体大小由 pt 决定,不会因为分辨率高而变小,Retina 屏幕只是用更多像素描绘文字,让字体更清晰。

三. 如何全局修改字体大小?

在实际项目中,我们经常会遇到这样的问题:在宽屏 iPhone 上,原本写好的 16pt 字体看起来好像变小了。为了保证视觉一致性,我们需要在全项目范围内统一调整字体大小。不过,不同技术栈的项目实现方式有所不同,这与项目的前期建设密切相关。

下面我们从多种技术栈出发,讲解几种全局修改字体的方法。

3.1 Hook 字体方法(不推荐,但全局生效最快)

Hook 方法是通过 runtime 替换系统字体方法,让所有控件默认使用缩放后的字体。这样做的优点是:

  • 无论是 Objective-C、Swift,甚至第三方控件都可以生效
  • 不需要手动替换每个控件的字体

但是,这种方式也有明显缺点:

  • 系统内部控件字体也会被改,可能引发布局或显示问题
  • 第三方库控件可能出现意料之外的效果
  • 调试难度大,风险较高

代码实现如下:

objectivec 复制代码
#import <UIKit/UIKit.h>

@interface UIFont (Hook)

@end
objectivec 复制代码
#import "UIFont+Hook.h"
#import <objc/runtime.h>

#define kFontScale   ([UIScreen mainScreen].bounds.size.width > 375.0 ? 1.048 : 1.0)

@implementation UIFont (Hook)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        // systemFontOfSize:
        Method originalSystem = class_getClassMethod(self, @selector(systemFontOfSize:));
        Method swizzledSystem = class_getClassMethod(self, @selector(ph_systemFontOfSize:));
        method_exchangeImplementations(originalSystem, swizzledSystem);

        // boldSystemFontOfSize:
        Method originalBold = class_getClassMethod(self, @selector(boldSystemFontOfSize:));
        Method swizzledBold = class_getClassMethod(self, @selector(ph_boldSystemFontOfSize:));
        method_exchangeImplementations(originalBold, swizzledBold);

        // fontWithName:size:
        Method originalName = class_getClassMethod(self, @selector(fontWithName:size:));
        Method swizzledName = class_getClassMethod(self, @selector(ph_fontWithName:size:));
        method_exchangeImplementations(originalName, swizzledName);
    });
}

+ (UIFont *)ph_systemFontOfSize:(CGFloat)fontSize {
    return [self ph_systemFontOfSize:(fontSize * kFontScale)];
}

+ (UIFont *)ph_boldSystemFontOfSize:(CGFloat)fontSize {
    return [self ph_boldSystemFontOfSize:(fontSize * kFontScale)];
}

+ (UIFont *)ph_fontWithName:(NSString *)fontName size:(CGFloat)fontSize {
    return [self ph_fontWithName:fontName size:(fontSize * kFontScale)];
}

@end

3.2 Objective-C 项目常用方法:宏定义

在纯 OC 项目中,最常见、可控的做法是使用宏定义。例如我们项目中定义了如下宏:

objectivec 复制代码
#define kFontScale   ([UIScreen mainScreen].bounds.size.width > 375.0 ? 1.048 : 1.0)
#define kSystemFont(x)   [UIFont systemFontOfSize:(x * kFontScale)]
#define kBoldFont(x)     [UIFont boldSystemFontOfSize:(x * kFontScale)]
#define kNumberFont(x)   [UIFont bebasFontOfSize:(x * kFontScale)]

使用方式非常简单:

objectivec 复制代码
label.font = kSystemFont(16);
button.titleLabel.font = kBoldFont(18);

这种方式的优点是:

  • 简单明了,统一管理
  • 易于维护和修改比例

缺点是:

  • 需要在项目中统一替换原来的 systemFontOfSize 调用

总的来说,这是 OC 项目中最稳妥的全局字体适配方案。

3.3 Swift 项目统一方法

在 Swift 项目中,可以封装一个统一的字体 Helper,或者分了,例如我们的 ZMFontHelper:

Swift 复制代码
/// 设置字体
/// - Parameters:
///  - fontType: 字体类型
///  - fontSize: 字体大小
///  - fontWeight: 字体粗细
/// - Returns: UIFont
public class func font(fontType: ZMFontType = .system,
                       fontSize: CGFloat,
                       fontWeight: UIFont.Weight = .regular) -> UIFont {
    let scale: CGFloat = UIScreen.main.bounds.size.width > 375 ? 1.048 : 1.0
    let finalSize = fontSize * scale
    switch fontType {
    case .system:
        return UIFont.systemFont(ofSize: finalSize, weight: fontWeight)
    }
}

使用方式:

Swift 复制代码
label.font = ZMFontHelper.font(fontSize: 16)
button.titleLabel?.font = ZMFontHelper.font(fontSize: 18, fontWeight: .bold)

优点:

  • 面向对象,易于扩展
  • 可以兼顾 OC + Swift 混合项目(通过桥接)
  • 易于统一调整字体比例

3.4 SwiftUI 项目

在 SwiftUI 中,推荐使用自定义 ViewModifier 来实现全局字体适配:

Swift 复制代码
struct ScaledFont: ViewModifier {
    var size: CGFloat
    var scaledSize: CGFloat {
        UIScreen.main.bounds.width > 375 ? size * 1.048 : size
    }
    func body(content: Content) -> some View {
        content.font(.system(size: scaledSize))
    }
}

extension View {
    func scaledFont(_ size: CGFloat) -> some View {
        self.modifier(ScaledFont(size: size))
    }
}

使用方式:

Swift 复制代码
Text("Hello World")
    .scaledFont(16)

优点:

  • 全局统一,代码优雅
  • 易于维护和扩展
  • 完美契合 SwiftUI 生态

3.5 项目中每个地方都单独写字体

如果项目中每个控件都是单独设置字体(没有统一规范),目前只能通过搜索替换的方式逐步改成统一方法或宏/Helper。

  • 建议在后续项目中尽量使用统一方法
  • 避免重复手动修改,提高可维护性

四. 结语

在 iOS 项目中,字体大小并不会因为屏幕分辨率高而变小。所谓在宽屏手机上字体"变小",只是视觉错觉:屏幕宽度增加、布局相对变得更松散,导致人眼感知上觉得字体偏小。

理解这一点之后,我们就可以科学地在全项目范围内统一调整字体大小。无论是使用宏定义、Swift Helper、SwiftUI Modifier,还是在特殊情况下采用 Hook 方法,每种方案都有其适用场景。关键在于选择与项目技术栈和维护需求相匹配的方案,从而既满足设计师在大屏设备上的视觉需求,又保证项目的可维护性和一致性。

最终,全局字体适配不是简单地放大字号,而是基于 iOS 渲染原理和视觉平衡的合理调整,让用户在不同机型上都能获得最佳阅读体验。

相关推荐
songgeb4 小时前
iOS App进入后台时会发生什么
ios
笑尘pyrotechnic5 小时前
运行,暂停,检查:探索如何使用LLDB进行有效调试
ios·objective-c·lldb
metaRTC6 小时前
webRTC IPC客户端React Native版编程指南
react native·react.js·ios·webrtc·p2p·ipc
ajassi20009 小时前
开源 Objective-C IOS 应用开发(十八)音频的播放
ios·开源·objective-c
2501_915921439 小时前
Windows 系统下的 IPA 加密工具实战指南,如何在非 macOS 环境完成 IPA 混淆、加固与工程化处理
android·windows·macos·ios·小程序·uni-app·iphone
马拉萨的春天9 小时前
iOS的分类中为什么不能添加变量以及如何设置关联对象的弱引用效果
ios·分类·数据挖掘
ajassi200010 小时前
开源 Objective-C IOS 应用开发(十七)CAF音频的录制
ios·开源·objective-c
源码君miui520861 天前
JAVA国际版同城服务同城信息同城任务发布平台APP源码Android + IOS
android·java·ios
00后程序员张1 天前
接口调试从入门到精通,Fiddler抓包工具、代理配置与HTTPS抓包实战技巧
前端·ios·小程序·https·fiddler·uni-app·webview