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

XPThemeSize 2.0.0

XPThemeSize 2.0.0

Maintained by jamalping.



  • By
  • jamalping

XPThemeSize

Platform Version License Swift CI

XPThemeSizeXPFontSizeManage 的 2.0 演进:在「普通 / 大号」字号维度之上,增加浅色 / 深色外观维度,由 XPThemeSizeManager 统一解析为 XPThemeSnapshot,一次通知驱动 UI 刷新。

适用于关怀模式、无障碍、App 内「字体大小 + 深浅色」设置等场景。

1.x 用户: 继续使用 pod 'XPFontSizeManage'(已标记弃用)或迁移至 XPThemeSize + Legacy 子模块,见 从 1.0 迁移

设计规格: docs/superpowers/specs/2026-05-26-xpthemesize-design.md


特性

  • 双维度: appearance(light / dark / 跟随系统)× contentSize(normal / large)
  • 单一数据源: XPThemeSizeManager.shared.currentSnapshot 聚合颜色、字体、圆角等指标
  • 模块化 Subspec: Core / Theme / FontSize / UIKit / Legacy,按需集成
  • 声明式资源: MixFontMixColorMixedResource 按当前 Snapshot 自动 unfold()
  • 低侵入: UIKit Extension + 关联对象,无需子类化控件
  • 1.0 兼容: Legacy 子模块保留 XPFontSizeManager 等 API

系统要求

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

安装

CocoaPods(推荐子模块)

# 新项目:UIKit = Theme + FontSize + 控件扩展
pod 'XPThemeSize/UIKit'

# 从 1.x 过渡:在 UIKit 基础上增加兼容层
pod 'XPThemeSize/Legacy'

# 按需精简
pod 'XPThemeSize/Core'      # 仅 Manager + Snapshot
pod 'XPThemeSize/Theme'     # 深浅色 + trait 监听(依赖 Core)
pod 'XPThemeSize/FontSize'  # MixFont / XPFont(依赖 Core)

多子模块合并安装(Example 用法):

pod 'XPThemeSize', :subspecs => ['Core', 'FontSize', 'Theme', 'UIKit', 'Legacy']

本地调试 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

AppDelegate(跟随系统深浅色)

安装 ThemeUIKit 后,在启动时安装 trait 监听:

import XPThemeSize

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // ...
    window?.makeKeyAndVisible()
    if let window = window {
        XPThemeSizeAppearanceObserver.install(on: window)
    }
    return true
}

快速开始

1. 切换字号与外观

import XPThemeSize

let manager = XPThemeSizeManager.shared

// 字号(持久化)
manager.contentSize = .large
manager.switchContentSize()

// 外观偏好(持久化)
manager.appearancePreference = .dark   // .system / .light / .dark

// 当前快照
let snap = manager.currentSnapshot
view.backgroundColor = snap.colors.background
label.font = snap.typography.font(for: .body)

2. 监听变更

NotificationCenter.default.addObserver(
    forName: XPThemeSizeManager.themeSizeDidChangeNotification,
    object: nil,
    queue: .main
) { _ in
    let snap = XPThemeSizeManager.shared.currentSnapshot
    // 刷新自定义 UI
}

3. 绑定控件(与 1.0 类似)

label.fontSize = MixFont(
    normal: .systemFont(ofSize: 15),
    other: .systemFont(ofSize: 22)
)
label.fontSize = XPFont(normal: .systemFont(ofSize: 15), 3)

button.xpControlNormalFont = MixFont(normal: .systemFont(ofSize: 15), other: .systemFont(ofSize: 22))
view.layer.xpCornerRadius = MixedResource(normal: 8, other: 20)

view.themeChangeCallback = { contentSize in
    // contentSize: .normal | .large
}

4. 自定义全局色板与排版(Snapshot)

didFinishLaunching 最早注册;传 nil 恢复内置默认。

// 设计稿导出 [tokenName: UIColor](或自行解析 JSON/Plist)
XPThemeSizeManager.registerColorTokensProvider { appearance in
    let table = DesignColorLoader.tokens(for: appearance)
    let fallback = appearance == .dark ? XPColorTokens.defaultDark : XPColorTokens.defaultLight
    // 标准字段映射后,其余 key 自动进 extra
    return XPColorTokens.fromDesignTokens(table, mapping: .default, fallback: fallback)
}

// 使用:标准字段 + 设计扩展 token
let colors = XPThemeSizeManager.shared.currentSnapshot.colors
view.backgroundColor = colors.background
cardView.backgroundColor = colors.color(named: "card_bg")
// 或 colors.extra["card_bg"]

// 设计 key 与代码不一致时,改 mapping 即可:
let mapping = XPColorTokenMapping(
    background: "page_bg",
    textPrimary: "text_primary",
    textSecondary: "text_secondary",
    accent: "brand",
    separator: "divider"
)

let appScale = XPTypographyScale(
    body: XPStyleSizePair(normal: 16, large: 20),
    title: XPStyleSizePair(normal: 18, large: 24),
    headline: XPStyleSizePair(normal: 20, large: 28),
    caption: XPStyleSizePair(normal: 12, large: 14)
)
XPThemeSizeManager.registerTypographyProvider { size in
    XPTypography(contentSize: size, scale: appScale)
}

注册后 currentSnapshot.colors / typography 使用自定义逻辑;色板或排版变化会触发 themeSizeDidChangeNotification

内置默认:XPColorTokens.defaultLight / defaultDarkXPTypographyScale.default


架构设计

双维度模型

appearance(外观)     ×     contentSize(内容尺寸)
.system → light|dark        .normal | .large
         ↓
   XPThemeSnapshot(颜色 / 字体 / 圆角等指标)
         ↓
   themeSizeDidChangeNotification(去重后单次广播)

模块依赖

flowchart TB
    subgraph App["宿主 App"]
        Settings["设置页"]
        UI["UILabel · UIButton · CALayer …"]
    end

    subgraph Core["Core"]
        Mgr["XPThemeSizeManager"]
        Snap["XPThemeSnapshot"]
    end

    subgraph Theme["Theme"]
        Resolver["XPThemeAppearanceResolver"]
        Observer["XPThemeSizeAppearanceObserver"]
        Tokens["XPColorTokens"]
    end

    subgraph FontSize["FontSize"]
        Mix["MixedResource · MixFont · XPFont"]
    end

    subgraph UIKitExt["UIKit"]
        Ext["控件 Extension"]
        NM["NotificationManager"]
    end

    subgraph Legacy["Legacy 可选"]
        Shim["XPFontSizeManager shim"]
    end

    Settings --> Mgr
    Mgr --> Snap
    Theme --> Mgr
    FontSize --> Mgr
    UI --> Ext
    Ext --> Mix
    Ext --> NM
    NM --> Mgr
    Legacy --> Mgr
Loading

仓库目录

XPFontSizeManage/                 # 仓库名保持不变
├── XPThemeSize.podspec           # 2.0 主 Pod
├── XPFontSizeManage.podspec      # 1.x(弃用说明)
├── Sources/
│   ├── Core/                     # XPThemeSizeManager、Snapshot
│   ├── Theme/                    # 外观解析、trait 监听、ColorTokens
│   ├── FontSize/                 # MixedResource、XPFont
│   ├── UIKit/                    # 控件扩展
│   └── Legacy/                   # 1.x API 兼容
├── XPFontSizeManage.podspec      # 1.0 已废弃(源码见 git tag 1.0.0)
└── Example/

单独安装 Subspec 时的默认

已安装 缺失维度的默认
FontSize appearance 固定 light
Theme contentSize 固定 normal
UIKit(含 Theme + FontSize) 用户配置 + 可跟随系统外观

从 XPFontSizeManage 1.0 迁移到 XPThemeSize 2.0

1.0 2.0
import XPFontSizeManage import XPThemeSize
pod 'XPFontSizeManage' pod 'XPThemeSize/UIKit'
XPFontSizeManager.fontSize XPThemeSizeManager.shared.contentSize
XPFontSizeManager.switchFontSize() XPThemeSizeManager.shared.switchContentSize()
XPFontSizeManager.isLargeFont XPThemeSizeManager.shared.isLargeContent
fontSizeDidChangeNotification themeSizeDidChangeNotification
FontSize XPContentSize(或 Legacy 下仍可用 FontSize typealias)
fontSizeChangeCallBack themeChangeCallback(参数为 XPContentSize
无深浅色 appearancePreference + currentSnapshot.colors

最小改动路径: 先增加 pod 'XPThemeSize/Legacy',仅替换 import 与 Pod 名,旧 API 仍可编译;再逐步改用 XPThemeSizeManager 与 Snapshot。


Example 工程

Demo 说明
主题与字号设置 appearancePreference + contentSize + Snapshot 预览
Label / TextField / TextView fontSizexp.fontSizeXPFont
UIButton 分状态字体、颜色、imageEdgeInsets
UIView & CALayer themeChangeCallback、圆角/边框
移除监听 动态绑定与 fontSize = nil
1.0 兼容 API XPFontSizeManager / FontSize

API 速查(2.0)

类型 说明
XPThemeSizeManager 单例:偏好读写、currentSnapshot、通知
XPThemeSnapshot 当前生效主题快照
XPAppearance / XPAppearancePreference 有效外观 / 用户偏好
XPContentSize .normal / .large
MixFont / MixColor / MixedResource 双态资源,unfold() 读 Snapshot
XPThemeSizeAppearanceObserver 窗口 trait → Manager 刷新

控件扩展与 1.0 基本一致,见 快速开始


内存与监听

  • 设置 fontSize 或 Button 相关属性时自动注册 themeSizeDidChangeNotification 监听。
  • fontSize = nilremoveAllFontSizeObservers() 可解除绑定。
  • Example「移除监听」页演示完整流程。

与系统 Dynamic Type 的关系

本库提供 App 内固定两档 字号(normal / large),与系统「辅助功能 → 更大字体」的 Dynamic Type 不同机制,可并存。MVP 不跟随系统 Dynamic Type;后续可在 spec 中扩展 contentSizePreference


发布与版本


作者

jamalping[email protected]

许可证

基于 MIT License 发布。