如何设置28个每日9点触发、每28天重复的本地通知
解决方案:实现每28天循环的每日9点本地通知
核心问题分析
UNCalendarNotificationTrigger的重复逻辑基于日历单位(天/月/年),无法直接支持28天固定周期,会因月份天数差异导致断更。UNTimeIntervalNotificationTrigger的重复机制要求第一次触发间隔与后续重复间隔完全一致,若初始触发时间为未来固定点(如9点),直接设置重复会导致后续间隔混乱——因为第一次间隔是当前时间到目标时间的差值,和28天固定间隔不相等。
可行实现方案
采用「单次触发+触发后重新调度」的模式:每个通知首次用日历触发器在指定9点触发,触发后立即调度下一个28天后的同一时间通知,以此实现无限循环直到手动取消。
步骤1:配置通知权限与代理
在App启动时完成权限申请和代理设置,用于监听通知触发事件:
// App启动入口配置 UNUserNotificationCenter.Current.Delegate = new NotificationDelegate(); var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.Badge; UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) => { if (granted) { Console.WriteLine("通知权限已授权"); } });
步骤2:实现通知代理处理触发回调
创建代理类,在通知触发时自动调度下一个周期的对应通知:
public class NotificationDelegate : UNUserNotificationCenterDelegate { public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler) { var request = response.Notification.Request; // 解析通知ID,获取对应序号(ID格式:daily_notice_0 到 daily_notice_27) if (request.Identifier.StartsWith("daily_notice_")) { int index = int.Parse(request.Identifier.Split('_')[2]); // 计算下一次触发时间:当前触发日期+28天,保持上午9点 var nextTriggerDate = DateTime.Today.AddDays(index + 28).AddHours(9); if (nextTriggerDate < DateTime.Now) { nextTriggerDate = nextTriggerDate.AddDays(28); } ScheduleDailyNotification(index, nextTriggerDate); } completionHandler(); } // 前台触发时同样处理重调度 public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler) { var request = notification.Request; if (request.Identifier.StartsWith("daily_notice_")) { int index = int.Parse(request.Identifier.Split('_')[2]); var nextTriggerDate = DateTime.Today.AddDays(index + 28).AddHours(9); if (nextTriggerDate < DateTime.Now) { nextTriggerDate = nextTriggerDate.AddDays(28); } ScheduleDailyNotification(index, nextTriggerDate); } completionHandler(UNNotificationPresentationOptions.Alert | UNNotificationPresentationOptions.Sound); } }
步骤3:初始化28个首次通知
循环创建28个通知,每个对应从当前日期开始第i天的9点(若当天9点已过,则自动延后到下一个周期的同一天):
public void Initialize28DailyNotifications() { for (int i = 0; i < 28; i++) { DateTime firstTriggerDate = DateTime.Today.AddDays(i).AddHours(9); // 若当天9点已过,直接调度下一个周期的同一天 if (firstTriggerDate < DateTime.Now) { firstTriggerDate = firstTriggerDate.AddDays(28); } ScheduleDailyNotification(i, firstTriggerDate); } } private void ScheduleDailyNotification(int index, DateTime triggerDate) { // 构建通知内容 var content = new UNMutableNotificationContent { Title = $"每日通知 {index+1}", Body = $"这是第{index+1}个通知,每28天重复一次", Sound = UNNotificationSound.Default, CategoryIdentifier = "daily_notice_category" }; // 创建日历触发器,精准指定触发日期和时间 var dateComponents = new NSDateComponents(); dateComponents.Year = triggerDate.Year; dateComponents.Month = triggerDate.Month; dateComponents.Day = triggerDate.Day; dateComponents.Hour = 9; dateComponents.Minute = 0; dateComponents.Second = 0; var trigger = UNCalendarNotificationTrigger.CreateTrigger(dateComponents, false); // 单次触发,不启用系统重复 var request = UNNotificationRequest.FromIdentifier($"daily_notice_{index}", content, trigger); // 添加通知请求到系统 UNUserNotificationCenter.Current.AddNotificationRequest(request, (error) => { if (error != null) { Console.WriteLine($"调度通知失败:{error.LocalizedDescription}"); } else { Console.WriteLine($"已调度通知:{request.Identifier},触发时间:{triggerDate}"); } }); }
步骤4:手动取消通知的逻辑(可选)
若需要停止所有循环通知,可批量移除待触发的请求:
// 生成所有通知ID并移除 var identifiers = Enumerable.Range(0,28).Select(i => $"daily_notice_{i}").ToArray(); UNUserNotificationCenter.Current.RemovePendingNotificationRequests(identifiers); // 同时移除已触发的通知记录 UNUserNotificationCenter.Current.RemoveDeliveredNotifications(identifiers);
测试代码问题修正
你之前的测试代码错误地将「当前时间到首次触发时间的间隔」与「重复间隔」相加,导致重复时使用了错误的时间差。正确的测试方式应沿用「单次触发+回调重调度」逻辑:
// 测试用:每分钟1条,共5条,每5分钟重复一次 public void TestRecurringNotifications() { for (int i = 0; i < 5; i++) { DateTime firstTriggerDate = DateTime.Now.AddMinutes(i); firstTriggerDate = firstTriggerDate.AddSeconds(-firstTriggerDate.Second); // 取整分触发 if (firstTriggerDate < DateTime.Now) { firstTriggerDate = firstTriggerDate.AddMinutes(5); } TestScheduleNotification(i, firstTriggerDate); } } private void TestScheduleNotification(int index, DateTime triggerDate) { var content = new UNMutableNotificationContent { Title = $"测试通知 {index+1}", Body = $"每5分钟重复一次", Sound = UNNotificationSound.Default }; var dateComponents = new NSDateComponents(); dateComponents.Hour = triggerDate.Hour; dateComponents.Minute = triggerDate.Minute; dateComponents.Second = 0; var trigger = UNCalendarNotificationTrigger.CreateTrigger(dateComponents, false); var request = UNNotificationRequest.FromIdentifier($"test_notice_{index}", content, trigger); UNUserNotificationCenter.Current.AddNotificationRequest(request, (error) => { if (error != null) { Console.WriteLine($"测试通知调度失败:{error}"); } }); } // 在代理的回调中添加测试通知的重调度 public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler) { var request = response.Notification.Request; if (request.Identifier.StartsWith("test_notice_")) { int index = int.Parse(request.Identifier.Split('_')[2]); var nextTriggerDate = DateTime.Now.AddMinutes(5); nextTriggerDate = nextTriggerDate.AddSeconds(-nextTriggerDate.Second); TestScheduleNotification(index, nextTriggerDate); } completionHandler(); }
内容的提问来源于stack exchange,提问作者user20292388
相关产品推荐
相关产品推荐

