CocoaPods trunk is moving to be read-only. Read more on the blog, there are 4 months to go.

XPFontSizeManage 1.0.0

XPFontSizeManage 1.0.0

Maintained by jamalping.



  • By
  • jamalping

XPFontSizeManage

Platform Version License Swift CI

XPFontSizeManage 是一个轻量级 iOS UIKit 库,用于在 App 内统一管理「普通 / 大号」两套 UI 配置,并在切换模式时自动刷新已绑定的控件。

字号切换为主场景,同时可选支持颜色、边距、圆角等配套属性(适老化 / 关怀模式常见需求)。

适用于适老化、无障碍、设置页「字体大小」等场景。


特性

  • 双档模式normal / large,状态持久化到 UserDefaults
  • 声明式配置:通过 MixFontMixColor 等描述两套资源,无需手写 if/else
  • 自动刷新:切换模式后通过 NotificationCenter 通知已注册的控件
  • 低侵入:基于 Extension + 关联对象,无需子类化 UILabel / UIButton
  • 多控件支持UILabelUITextViewUITextFieldUIButtonUIViewCALayer
  • 监听可移除:支持显式移除监听,避免泄漏(见下方「内存与监听」)

系统要求

项目 版本
iOS 9.0+
Swift 5.0+
Xcode 建议 12+

安装

CocoaPods

Podfile 中添加:

pod 'XPFontSizeManage'

然后执行:

cd YourProject
pod install

本地开发 / 调试 Example

git clone https://github.com/jamalping/XPFontSizeManage.git
cd XPFontSizeManage/Example
pod install
open XPFontSizeManage.xcworkspace

Gitee 镜像:git clone https://gitee.com/jamalping/XPFontSizeManage.git


快速开始

1. 切换全局字号模式

import XPFontSizeManage

// 在设置页或导航栏按钮中切换
XPFontSizeManager.switchFontSize()

// 或直接指定模式(会写入 UserDefaults 并广播通知)
XPFontSizeManager.fontSize = .large

// 查询当前模式
if XPFontSizeManager.isLargeFont {
    // ...
}

2. 为文本控件绑定字体

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 = 18pt

UITextFieldUITextView 用法与 UILabel 相同,均使用 fontSize 属性。

3. UIButton:分状态字体 / 颜色 / 图片边距

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

4. UIView:自定义刷新逻辑

demoView.fontSizeChangeCallBack = { size in
    let isLarge = (size == .large)
    demoView.alpha = isLarge ? 1.0 : 0.7
}

5. CALayer:圆角与边框

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 = nilremoveAllFontSizeObservers() 移除监听

仓库与模块结构

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&lt;T&gt;<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
Loading
层级 职责 对外可见
Core 模式状态机、双态资源模型、通知与观察者管理 XPFontSizeManagerMixedResource 系列
XPFontSize.swift 链式 xp API、XPFont / XPBoldFont 语法糖 XPFontSizeCompatiablexp
UIKit 把配置挂到具体控件,监听后刷新真实属性 各控件 fontSizexpControl*

Core 层不依赖具体控件类型;UIKit 层依赖 Core,单向向下。

核心组件说明

1. XPFontSizeManager — 全局状态中心

  • 枚举 FontSize.normal / .large(两档,非连续缩放)。
  • fontSize 赋值时:若档位未变则不重复发通知;否则更新内存 → 写 UserDefaults → 发送 fontSizeDidChangeNotification
  • 提供 switchFontSize()isNormalFontisLargeFont 等便捷 API。

2. MixedResource<T> — 双态资源模型

// 伪代码:根据当前全局档位解析实际值
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 基于差值或预设的快速构造

3. NotificationManager — 监听生命周期(内部)

挂在任意 NSObject(含 UILabelUIButtonCALayer 等)上,通过关联对象持有:

  • 闭包监听UILabel.fontSize setter 使用,token 存入 tokensdeinit 时移除。
  • Selector 监听UIButtonUIViewCALayer 使用 #selector 更新方法。
  • 去重:同一对象重复设置属性不会重复注册同类型监听。
  • 销毁:仅移除本库的 fontSizeDidChangeNotification,不影响业务其他通知。

业务代码一般无需直接使用该类。

UIKit 接入模式

控件扩展在设置双态属性时完成三件事:

  1. unfold() 立即应用到 UI(如 label.font)。
  2. 用关联对象保存 MixFont / MixColor 等配置供后续刷新。
  3. 注册字号变更监听,在回调里再次 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()
Loading
控件 监听方式 存储内容 刷新目标
UILabel / UITextField / UITextView 闭包 MixFont font
UIButton Selector MixFont / MixColor / MixImageEdgeInset titleLabelimageEdgeInsets
UIView Selector fontSizeChangeCallBack 业务自定义
CALayer Selector MixedResource<CGFloat> cornerRadiusborderWidth

UILabel 另提供 xp.fontSize 命名空间(XPFontSizeWrapper),与直接设置 fontSize 等价。

数据流小结

配置阶段:控件.xxx = Mix* / MixedResource
    → 关联对象保存 + 首次 unfold 写 UI + 注册监听

运行阶段:XPFontSizeManager.fontSize 变更
    → NotificationCenter 广播
    → 各控件监听回调
    → 读取关联对象中的 Mix*
    → unfold() 写回 UIKit 属性

卸载阶段:fontSize = nil 或 removeAllFontSizeObservers()
    → 移除监听 + 清除关联对象

与 Example / 测试的关系

目录 作用
Example/XPFontSizeManage/Demo/ 分场景演示文本、按钮、Layer、移除监听
Example/Tests/ 覆盖 XPFontSizeManagerMixFont.unfold()UILabel 联动

Example 仅用于集成验证,不打进 Pod 产物。


API 参考

核心类型

类型 说明
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 fontSizexp.fontSizeupdateFontSize()
UITextField fontSize
UITextView fontSize
UIButton xpControlNormalFontxpControlSelectedFontxpControlNormalTitleColorxpControlSelectedTitleColorxpControlNormalInset
UIView fontSizeChangeCallBack
CALayer xpCornerRadiusxpBorderWidth
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 = nil

Example 中的 「移除监听测试」 页面演示了上述行为。


Example 工程

仓库 Example/ 目录包含完整演示:

Demo 说明
首页列表 展示当前模式;列表 cell 绑定 MixFont 随模式放大
Label / TextField / TextView fontSizexp.fontSizeXPFont 三种写法对比
UIButton 分状态字体、颜色、imageEdgeInsets(颜色为同色系微调)
UIView & CALayer 回调 + 圆角/边框;进入页即显示当前模式
移除监听 动态绑定 / fontSize = nil / 切换字号

运行步骤见 安装 → 本地开发


与系统 Dynamic Type 的关系

本库通过 应用内自定义档位(普通/大号)切换 UI,与 iOS 系统「辅助功能 → 更大字体」的 Dynamic Type 是不同机制。二者可以并存:系统字号影响 UIFontMetrics,本库则在业务层提供固定的两档布局方案。

若只需跟随系统字号,优先考虑 UIFontMetrics / adjustsFontForContentSizeCategory;若需要 App 内独立开关(如「关怀模式」),则适合使用本库。


从 0.0.x 迁移到 0.1.0

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.podspecs.version 为准。
  • 可使用仓库根目录 upload.sh 发版;公有源设置 USE_TRUNK=1 ./upload.sh
  • 发布新版本时请打 Git tag(如 1.0.0),并与 podspec 版本一致。
  • 若提交到 CocoaPods Trunk,需先执行 pod trunk registerpod trunk push

作者

jamalping[email protected]

许可证

XPFontSizeManage 基于 MIT License 发布。