修改偏好内容大小后UICollectionView字体与SF Symbols不动态缩放的问题咨询
自定义字重Dynamic Type失效的本质
你最初实现的带自定义字重的字体生成逻辑,是从preferred字体描述符取固定pointSize后手动创建系统字体,仅靠UIFontMetrics做一次性缩放。这类生成的字体没有和对应文本样式建立系统层面的动态绑定关系,当系统内容大小变化时,UICollectionView复用的单元格不会被系统自动触发字体重算,就会出现滚动时部分单元格保留旧字号的问题。
你后续采用的「从系统preferredFont衍生字重特质」的实现(即通过withTraits方法在基础preferredFont的描述符上叠加字重特质、传入size: 0保留原有缩放比例)是正确的:因为基础字体本身和文本样式绑定,携带了动态缩放的追踪信息,通过字体描述符叠加特质时保留原有缩放比例,就能完全匹配系统原生动态字体的行为。
NSAttributedString中SF Symbols尺寸不更新的本质
你通过UIImage.SymbolConfiguration(textStyle:)生成的符号图片本身是支持动态缩放的,但一旦将生成的静态UIImage存入NSTextAttachment并构建为NSAttributedString,若还将图片缓存为静态对象,系统就不会自动在内容大小变化时重新生成对应尺寸的符号图,最终导致符号尺寸停留在旧值,重启应用时因为全量重建对象才会恢复正常。
自定义字重Dynamic Type的正确实现
- 统一复用基于原生
preferredFont衍生字重的实现逻辑,绝对不要在字体生成阶段提取固定pointSize创建独立的UIFont对象,确保所有衍生字体都携带和原文本样式的动态绑定关系。 - 在
UICollectionViewListCell的配置入口(updateConfigurations(using:)方法、或自定义contentView的traitCollectionDidChange(_:)方法)中,每次都根据当前traitCollection的preferredContentSizeCategory重新计算标签的font属性,不要把字体对象缓存到模型层仅创建一次。
NSAttributedString中SF Symbols的动态缩放实现
这部分不需要复杂的全局通知监听逻辑,按照以下方式即可实现自动适配:
- 放弃直接将静态UIImage存入NSTextAttachment的写法,利用系统原生的文本绑定能力生成符号附件:
- 构建NSTextAttachment时,为其设置
font属性绑定对应的文本样式,系统会自动根据当前的内容大小计算匹配的SF Symbols尺寸。 - 核心实现代码示例:
// 构建带动态缩放SF Symbol的富文本片段 let symbolAttachment = NSTextAttachment() // 核心:绑定对应文本样式的字体,让系统自动追踪动态大小变化 symbolAttachment.font = UIFont.preferredFont(forTextStyle: .title1) // 加载对应名称的SF Symbol if let symbolImage = UIImage(systemName: "star.fill") { symbolAttachment.image = symbolImage // 手动调整附件bounds匹配字体基线,避免垂直偏移 let capHeight = symbolAttachment.font!.capHeight let imageSize = symbolImage.size symbolAttachment.bounds = CGRect( x: 0, y: (capHeight - imageSize.height)/2, width: imageSize.width, height: imageSize.height ) } let symbolAttrString = NSAttributedString(attachment: symbolAttachment) - 构建NSTextAttachment时,为其设置
- 确保承载这个NSAttributedString的文本控件(UILabel、自定义文本视图等)开启动态字体支持:将控件的
adjustsFontForContentSizeCategory属性设置为true,系统会在traitCollection变化时自动触发重绘,连带重新计算NSTextAttachment内的SF Symbols尺寸。 - 无需手动监听
UIContentSizeCategory.didChangeNotification做全量刷新:系统会自动通过traitCollection的变更传递,驱动所有绑定了动态字体的控件完成更新,手动全局刷新反而会导致不必要的滚动偏移和性能损耗。
如果你的场景确实需要自定义Symbol参数(比如自定义权重、符号比例),只要在配置阶段把preferredContentSizeCategory和对应文本样式绑定,在控件的traitCollection更新方法中重新生成符号图片即可,不需要手动做全局事件监听。
内容的提问来源于stack exchange,提问作者user784637

