Swift中根据通知单元格类型生成富文本字符串的实现方案
解决方案:本地化模板+富文本标记实现多语言适配的通知标题样式
核心思路
放弃拆分单词的低效方式,改用本地化字符串模板+自定义标记结合枚举关联数据的方案,同时用结构体封装不同通知类型的动态参数,既解决多语言词序适配问题,又能灵活控制高亮样式。
步骤1:重构枚举与参数结构体
将原枚举的关联值从单一图片扩展为包含所有动态数据的结构体,让每种通知类型的所需参数更清晰:
// 封装各通知类型的动态参数 struct NotificationContent { struct EventDue { let eventName: String let dueTime: String let image: UIImage } struct EventCreated { let eventName: String let image: UIImage } struct EventChanged { let eventName: String let changeDesc: String let image: UIImage } struct InviteDeclined { let userName: String let eventName: String let image: UIImage } struct AdminMessage { let messageTitle: String let avatar: UIImage } struct Invite { let inviterName: String let eventName: String let avatar: UIImage } } // 重构后的枚举,关联对应参数结构体 enum NotificationType { case eventDue(NotificationContent.EventDue) case eventCreated(NotificationContent.EventCreated) case eventChanged(NotificationContent.EventChanged) case inviteDeclined(NotificationContent.InviteDeclined) case adminMessage(NotificationContent.AdminMessage) case invite(NotificationContent.Invite) var image: UIImage? { switch self { case .eventDue(let content): return content.image case .eventCreated(let content): return content.image case .eventChanged(let content): return content.image case .inviteDeclined(let content): return content.image case .adminMessage(let content): return content.avatar case .invite(let content): return content.avatar } } }
步骤2:添加多语言本地化模板
在Localizable.strings中为每种通知类型定义带标记的模板,用*标记需要高亮的部分,同时适配不同语言的词序:
// 中文模板 "notification.event_due" = "您的 *%@* 将于 *%@* 开始"; "notification.event_created" = "已创建活动 *%@*"; "notification.event_changed" = "活动 *%@* 已更新:*%@*"; "notification.invite_declined" = "*%@* 拒绝了您的 *%@* 邀请"; "notification.admin_message" = "管理员通知:*%@*"; "notification.invite" = "*%@* 邀请您参加 *%@*"; // 德语模板(适配词序) "notification.event_due" = "Ihre *%@* beginnt um *%@*";
步骤3:ViewModel中生成富文本标题
在NotificationViewModel中,根据枚举类型匹配模板、替换参数,再解析标记生成带样式的富文本:
class NotificationViewModel { private let notificationType: NotificationType private let defaultTextColor = UIColor.gray private let highlightTextColor = UIColor.black private let font = UIFont.systemFont(ofSize: 16) init(type: NotificationType) { self.notificationType = type } var formattedTitle: NSAttributedString { let templateString: String let arguments: [CVarArg] // 匹配对应模板与参数 switch notificationType { case .eventDue(let content): templateString = NSLocalizedString("notification.event_due", comment: "") arguments = [content.eventName, content.dueTime] case .eventCreated(let content): templateString = NSLocalizedString("notification.event_created", comment: "") arguments = [content.eventName] case .eventChanged(let content): templateString = NSLocalizedString("notification.event_changed", comment: "") arguments = [content.eventName, content.changeDesc] case .inviteDeclined(let content): templateString = NSLocalizedString("notification.invite_declined", comment: "") arguments = [content.userName, content.eventName] case .adminMessage(let content): templateString = NSLocalizedString("notification.admin_message", comment: "") arguments = [content.messageTitle] case .invite(let content): templateString = NSLocalizedString("notification.invite", comment: "") arguments = [content.inviterName, content.eventName] } // 替换占位符得到原始字符串 let rawString = String(format: templateString, arguments: arguments) // 解析标记生成富文本 return parseHighlightedString(rawString) } private func parseHighlightedString(_ string: String) -> NSAttributedString { let attributedString = NSMutableAttributedString(string: string, attributes: [ .foregroundColor: defaultTextColor, .font: font ]) var range = string.startIndex..<string.endIndex while let startRange = string.range(of: "*", range: range) { let startPos = string.distance(from: string.startIndex, to: startRange.lowerBound) attributedString.deleteCharacters(in: NSRange(startPos..<startPos+1)) guard let endRange = string.range(of: "*", range: startRange.upperBound..<string.endIndex) else { break } let endPos = string.distance(from: string.startIndex, to: endRange.lowerBound) - 1 // 设置高亮样式 let highlightRange = NSRange(startPos..<endPos) attributedString.addAttributes([ .foregroundColor: highlightTextColor ], range: highlightRange) attributedString.deleteCharacters(in: NSRange(endPos..<endPos+1)) range = endRange.upperBound..<string.endIndex } return attributedString } }
方案优势
- 多语言兼容:本地化模板可自由调整词序,不受固定拆分逻辑限制
- 扩展性强:新增通知类型只需补充结构体、模板和枚举分支
- 样式灵活:通过标记可任意控制高亮范围,支持复杂文本样式组合
内容的提问来源于stack exchange,提问作者Stefan
相关产品推荐
相关产品推荐

