Xamarin.Forms iOS如何在本地通知显示前修改标题与正文
核心原因
你之前的两种尝试不生效,本质是没摸透iOS本地通知的运行机制:
- 修改
iOSNotificationReceiver里传入ReceiveNotification()的标题、消息参数,只会改动C#业务层接收到事件时拿到的数据,系统弹出通知时读取的是你当初调度通知时写入UNNotification对象的原始内容,改动事件参数完全不会影响实际展示效果。 - 直接在
NotificationReceived?.Invoke(null, args);前return拦不住原始通知,是因为你没有正确调用系统回调。iOS前台通知代理方法要求必须执行传入的completionHandler,明确告知系统要如何处理当前通知,你不传递正确的拦截标识,系统默认就会按原始内容弹出。
实现步骤
分场景处理,覆盖前台、后台、App杀进程所有触发情况:
1. 前台通知动态修改
在iOS项目的UNUserNotificationCenter代理的WillPresentNotification方法中按如下逻辑实现:
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")] public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler) { var request = notification.Request; // 替换为你自己的每日通知标识前缀 if (request.Identifier.StartsWith("DailyNotification_")) { // 拉取最新API数据,组装新的通知内容 var newContent = new UNMutableNotificationContent { Title = "从API获取的最新标题", Body = "从API获取的最新正文", Sound = UNNotificationSound.Default, UserInfo = request.Content.UserInfo // 保留原始通知携带的自定义参数 }; // 关键:传入None告诉系统不要展示原始通知 completionHandler(UNNotificationPresentationOptions.None); // 插入0.1秒后触发的新通知,用户无感知,不会出现叠弹 var trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(0.1, false); var newRequest = UNNotificationRequest.FromIdentifier(request.Identifier, newContent, trigger); center.AddNotificationRequest(newRequest, null); } else { // 其他通知按原有逻辑正常展示 completionHandler(UNNotificationPresentationOptions.Alert | UNNotificationPresentationOptions.Sound); } }
2. 后台/杀进程状态下动态修改
前台代理方法在App不在前台运行时不会生效,这种场景必须通过iOS官方提供的Notification Service Extension实现通知内容修改,这是唯一合规的方案:
- 右键iOS解决方案添加新项目,选择
Notification Service Extension模板,注意扩展的最低部署版本不能高于主App的最低支持版本 - 在扩展的
DidReceiveNotificationRequest方法中实现内容修改逻辑:
UNMutableNotificationContent BestAttemptContent; Action<UNNotificationContent> ContentHandler; public override void DidReceiveNotificationRequest(UNNotificationRequest request, Action<UNNotificationContent> contentHandler) { ContentHandler = contentHandler; BestAttemptContent = (UNMutableNotificationContent)request.Content.MutableCopy(); if (request.Identifier.StartsWith("DailyNotification_")) { // 两种方式拿最新内容: // 1. 开启App Group能力,从主App和扩展共享的存储空间中,读取主App提前拉取好的最新内容 // 2. 直接在扩展内发起API请求拉取最新内容,系统给扩展预留的处理时间约30秒,足够普通接口响应 BestAttemptContent.Title = "最新通知标题"; BestAttemptContent.Body = "最新通知正文"; } // 将修改后的内容回传给系统展示 ContentHandler(BestAttemptContent); } public override void TimeWillExpire() { // 系统分配的处理时间即将耗尽时,兜底返回现有内容,避免通知弹不出来 ContentHandler(BestAttemptContent); }
注意:通知服务扩展和主App是独立进程,不能直接读取主App内存中的数据,必须通过App Group共享容器或者独立网络请求获取数据。
3. 稳定的每日通知调度方案
不要使用系统UNCalendarNotificationTrigger自带的重复属性,这类重复通知的内容在创建时就被固化,无法动态更新。也不要只调度单条通知,避免某一次触发失败导致后续所有通知断链:
- 采用滚动预调度方案,一次性预调度未来7-14天的每日通知,每个通知ID带上对应的日期后缀,比如
DailyNotification_20240620 - 每次App启动、回到前台、或者收到通知触发事件时,检查未来7-14天的通知调度队列,补齐缺失的通知即可
- 预调度时通知内容可以先填占位符,触发时通过前面提到的两种替换逻辑换成最新内容即可
这种方案下,就算用户连续一周没打开App,提前调度好的通知也能正常触发,完全不会出现断链问题。
内容的提问来源于stack exchange,提问作者juicebyjustin
相关产品推荐
相关产品推荐

