You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何设置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 11:45:36