XPFontSizeManage 是一个轻量级 iOS UIKit 库,用于在 App 内统一管理「普通 / 大号」两套 UI 配置,并在切换模式时自动刷新已绑定的控件。
以字号切换为主场景,同时可选支持颜色、边距、圆角等配套属性(适老化 / 关怀模式常见需求)。
适用于适老化、无障碍、设置页「字体大小」等场景。
- 双档模式:
normal/large,状态持久化到UserDefaults - 声明式配置:通过
MixFont、MixColor等描述两套资源,无需手写if/else - 自动刷新:切换模式后通过
NotificationCenter通知已注册的控件 - 低侵入:基于 Extension + 关联对象,无需子类化
UILabel/UIButton - 多控件支持:
UILabel、UITextView、UITextField、UIButton、UIView、CALayer - 监听可移除:支持显式移除监听,避免泄漏(见下方「内存与监听」)
| 项目 | 版本 |
|---|---|
| iOS | 9.0+ |
| Swift | 5.0+ |
| Xcode | 建议 12+ |
在 Podfile 中添加:
pod 'XPFontSizeManage'然后执行:
cd YourProject
pod installgit clone https://github.com/jamalping/XPFontSizeManage.git
cd XPFontSizeManage/Example
pod install
open XPFontSizeManage.xcworkspaceGitee 镜像:git clone https://gitee.com/jamalping/XPFontSizeManage.git
import XPFontSizeManage
// 在设置页或导航栏按钮中切换
XPFontSizeManager.switchFontSize()
// 或直接指定模式(会写入 UserDefaults 并广播通知)
XPFontSizeManager.fontSize = .large
// 查询当前模式
if XPFontSizeManager.isLargeFont {
// ...
}let label = UILabel()
label.text = "标题"
// 方式 A:直接属性
label.fontSize = MixFont(
normal: .systemFont(ofSize: 15),
other: .systemFont(ofSize: 22)
)
// 方式 B:xp 命名空间(与方式 A 等价)
label.xp.fontSize = MixFont(
normal: .systemFont(ofSize: 15),
other: .systemFont(ofSize: 22)
)
// 方式 C:基于差值快速构造大号字体
label.fontSize = XPFont(normal: .systemFont(ofSize: 15), 3) // other = 18ptUITextField、UITextView 用法与 UILabel 相同,均使用 fontSize 属性。
let button = UIButton(type: .system)
button.xpControlNormalFont = MixFont(normal: .systemFont(ofSize: 15), other: .systemFont(ofSize: 22))
button.xpControlSelectedFont = MixFont(normal: .boldSystemFont(ofSize: 16), other: .boldSystemFont(ofSize: 24))
button.xpControlNormalTitleColor = MixColor(normal: .white, other: .red)
button.xpControlSelectedTitleColor = MixColor(normal: .white, other: .purple)
button.xpControlNormalInset = MixImageEdgeInset(
normal: .zero,
other: UIEdgeInsets(top: 0, left: 24, bottom: 0, right: 0)
)demoView.fontSizeChangeCallBack = { size in
let isLarge = (size == .large)
demoView.alpha = isLarge ? 1.0 : 0.7
}view.layer.xpCornerRadius = MixedResource(normal: 8, other: 20)
view.layer.xpBorderWidth = MixedResource(normal: 2, other: 6)| 目标 | 实现方式 |
|---|---|
| 单一数据源 | XPFontSizeManager 持有全局档位,写入 UserDefaults |
| 声明式配置 | MixedResource<T> 描述 normal / large 两套资源,避免业务层散落 if/else |
| 自动刷新 | 模式变更 → NotificationCenter 广播 → 已绑定控件写回 UI 属性 |
| 低侵入接入 | UIKit Extension + Runtime 关联对象,无需子类化控件 |
| 可解除绑定 | fontSize = nil 或 removeAllFontSizeObservers() 移除监听 |
XPFontSizeManage/
├── XPFontSizeManage.podspec # CocoaPods 发布配置
├── XPFontSizeManage/Classes/
│ ├── XPFontSize.swift # xp 命名空间、XPFont 便捷构造
│ ├── Core/ # 与 UIKit 无关的核心层
│ │ ├── XPFontSizeManager.swift # 全局档位、持久化、通知
│ │ ├── MixedResource.swift # 双态资源泛型与类型别名
│ │ └── NotificationManager.swift # 观察者生命周期(内部)
│ └── UIKit/ # 控件扩展适配层
│ ├── UILabel+FontSize.swift
│ ├── UITextField+FontSize.swift
│ ├── UITextView+FontSize.swift
│ ├── UIButton+FontSize.swift
│ ├── UIView+FontSize.swift
│ └── CALayer+FontSize.swift
└── Example/ # 演示 App + 单元测试
CocoaPods 通过 s.source_files = "XPFontSizeManage/Classes/**/*" 将上述源码一并打入 Framework。
flowchart TB
subgraph App["宿主 App"]
Settings["设置页 / 开关"]
UI["UILabel · UIButton · UIView · CALayer …"]
end
subgraph Core["Core 层"]
Mgr["XPFontSizeManager<br/>FontSize · UserDefaults"]
Mix["MixedResource<T><br/>MixFont · MixColor · …"]
NM["NotificationManager<br/>(NSObject 扩展,内部)"]
end
subgraph UIKitExt["UIKit Extension 层"]
Assoc["关联对象存储 Mix* 配置"]
Obs["注册字号变更监听"]
Apply["unfold() 写回 font / color / inset …"]
end
Settings -->|"fontSize = .large"| Mgr
Mgr -->|"post fontSizeDidChangeNotification"| Obs
UI --> Assoc
Assoc --> Obs
Obs --> Mix
Mix --> Apply
Apply --> UI
NM -.-> Obs
| 层级 | 职责 | 对外可见 |
|---|---|---|
| Core | 模式状态机、双态资源模型、通知与观察者管理 | XPFontSizeManager、MixedResource 系列 |
| XPFontSize.swift | 链式 xp API、XPFont / XPBoldFont 语法糖 |
XPFontSizeCompatiable、xp |
| UIKit | 把配置挂到具体控件,监听后刷新真实属性 | 各控件 fontSize、xpControl* 等 |
Core 层不依赖具体控件类型;UIKit 层依赖 Core,单向向下。
- 枚举
FontSize:.normal/.large(两档,非连续缩放)。 fontSize赋值时:若档位未变则不重复发通知;否则更新内存 → 写UserDefaults→ 发送fontSizeDidChangeNotification。- 提供
switchFontSize()、isNormalFont、isLargeFont等便捷 API。
// 伪代码:根据当前全局档位解析实际值
func unfold() -> T {
switch XPFontSizeManager.fontSize {
case .normal: return normalResource
case .large: return otherResource
}
}常见类型别名:
| 类型 | 泛型参数 | 典型用途 |
|---|---|---|
MixFont |
UIFont |
文本字号 |
MixColor |
UIColor |
按钮标题色、对比度 |
MixImageEdgeInset |
UIEdgeInsets |
大图标按钮边距 |
MixedResource<CGFloat> |
CGFloat |
圆角、边框宽度 |
XPFont / XPBoldFont |
— | 基于差值或预设的快速构造 |
挂在任意 NSObject(含 UILabel、UIButton、CALayer 等)上,通过关联对象持有:
- 闭包监听:
UILabel.fontSizesetter 使用,token 存入tokens,deinit时移除。 - Selector 监听:
UIButton、UIView、CALayer使用#selector更新方法。 - 去重:同一对象重复设置属性不会重复注册同类型监听。
- 销毁:仅移除本库的
fontSizeDidChangeNotification,不影响业务其他通知。
业务代码一般无需直接使用该类。
控件扩展在设置双态属性时完成三件事:
unfold()立即应用到 UI(如label.font)。- 用关联对象保存
MixFont/MixColor等配置供后续刷新。 - 注册字号变更监听,在回调里再次
unfold()写回。
sequenceDiagram
participant User as 用户 / 设置页
participant Mgr as XPFontSizeManager
participant NC as NotificationCenter
participant Label as UILabel
Label->>Label: fontSize = MixFont(...)
Note over Label: 立即 unfold + 注册闭包监听
User->>Mgr: switchFontSize()
Mgr->>Mgr: UserDefaults 持久化
Mgr->>NC: fontSizeDidChangeNotification
NC->>Label: 回调触发
Label->>Label: font = fontSize.unfold()
| 控件 | 监听方式 | 存储内容 | 刷新目标 |
|---|---|---|---|
UILabel / UITextField / UITextView |
闭包 | MixFont |
font |
UIButton |
Selector | MixFont / MixColor / MixImageEdgeInset |
titleLabel、 imageEdgeInsets |
UIView |
Selector | fontSizeChangeCallBack |
业务自定义 |
CALayer |
Selector | MixedResource<CGFloat> |
cornerRadius、borderWidth |
UILabel 另提供 xp.fontSize 命名空间(XPFontSizeWrapper),与直接设置 fontSize 等价。
配置阶段:控件.xxx = Mix* / MixedResource
→ 关联对象保存 + 首次 unfold 写 UI + 注册监听
运行阶段:XPFontSizeManager.fontSize 变更
→ NotificationCenter 广播
→ 各控件监听回调
→ 读取关联对象中的 Mix*
→ unfold() 写回 UIKit 属性
卸载阶段:fontSize = nil 或 removeAllFontSizeObservers()
→ 移除监听 + 清除关联对象
| 目录 | 作用 |
|---|---|
Example/XPFontSizeManage/Demo/ |
分场景演示文本、按钮、Layer、移除监听 |
Example/Tests/ |
覆盖 XPFontSizeManager、MixFont.unfold()、UILabel 联动 |
Example 仅用于集成验证,不打进 Pod 产物。
| 类型 | 说明 |
|---|---|
FontSize |
.normal / .large |
XPFontSizeManager |
全局模式读写、switchFontSize() |
MixedResource<T> |
泛型双态资源,unfold() 按当前模式取值 |
MixFont |
MixedResource<UIFont> |
MixColor |
MixedResource<UIColor> |
MixImageEdgeInset |
MixedResource<UIEdgeInsets> |
XPFont |
便捷构造:XPFont(normal: font, delta) |
XPBoldFont |
预设粗体 13pt / 16pt |
XPControlFont |
按钮分状态字体配置辅助类型 |
| 控件 | 属性 / API |
|---|---|
UILabel |
fontSize、xp.fontSize、updateFontSize() |
UITextField |
fontSize |
UITextView |
fontSize |
UIButton |
xpControlNormalFont、xpControlSelectedFont、xpControlNormalTitleColor、xpControlSelectedTitleColor、xpControlNormalInset |
UIView |
fontSizeChangeCallBack |
CALayer |
xpCornerRadius、xpBorderWidth |
NSObject |
removeFontSizeSelectorObserver()、removeFontSizeCallbackObserver()、removeAllFontSizeObservers() |
业务层建议监听:
NotificationCenter.default.addObserver(
forName: XPFontSizeManager.fontSizeDidChangeNotification,
object: nil,
queue: .main
) { _ in
// 刷新自定义 UI
}- 为控件设置
fontSize(或 Button 的相关属性)时,会自动注册字体变化监听。 - 将
fontSize设为nil会移除闭包方式的监听并清除关联对象。 - 控件销毁时,
NotificationManager会在deinit中尝试移除观察者。 - 需要提前解除绑定时,可调用:
someLabel.removeAllFontSizeObservers()
someLabel.fontSize = nilExample 中的 「移除监听测试」 页面演示了上述行为。
仓库 Example/ 目录包含完整演示:
| Demo | 说明 |
|---|---|
| 首页列表 | 展示当前模式;列表 cell 绑定 MixFont 随模式放大 |
| Label / TextField / TextView | fontSize、xp.fontSize、XPFont 三种写法对比 |
| UIButton | 分状态字体、颜色、imageEdgeInsets(颜色为同色系微调) |
| UIView & CALayer | 回调 + 圆角/边框;进入页即显示当前模式 |
| 移除监听 | 动态绑定 / fontSize = nil / 切换字号 |
运行步骤见 安装 → 本地开发。
本库通过 应用内自定义档位(普通/大号)切换 UI,与 iOS 系统「辅助功能 → 更大字体」的 Dynamic Type 是不同机制。二者可以并存:系统字号影响 UIFontMetrics,本库则在业务层提供固定的两档布局方案。
若只需跟随系统字号,优先考虑 UIFontMetrics / adjustsFontForContentSizeCategory;若需要 App 内独立开关(如「关怀模式」),则适合使用本库。
| 0.0.x(旧) | 0.1.0(新) |
|---|---|
AAFont |
XPFont |
AABoldFont |
XPBoldFont |
aaControlNormalFont |
xpControlNormalFont |
aaControlSelectedFont |
xpControlSelectedFont |
aaControlNormalTitleColor |
xpControlNormalTitleColor |
aaControlSelectedTitleColor |
xpControlSelectedTitleColor |
aaControlNormalInset |
xpControlNormalInset |
aaCornerRadius |
xpCornerRadius |
aaBorderWidth |
xpBorderWidth |
- 目前仅支持 两档(
normal/large),不支持多级或连续缩放。 - 全局单例状态,暂不支持多窗口/多场景独立模式。
- 变更记录见 CHANGELOG.md。
- 版本号以 XPFontSizeManage.podspec 中
s.version为准。 - 可使用仓库根目录 upload.sh 发版;公有源设置
USE_TRUNK=1 ./upload.sh。 - 发布新版本时请打 Git tag(如
1.0.0),并与 podspec 版本一致。 - 若提交到 CocoaPods Trunk,需先执行
pod trunk register与pod trunk push。
jamalping — [email protected]
XPFontSizeManage 基于 MIT License 发布。