iOS徽章数字实时更新

15

我创建了一个iOS自定义日历,尝试使用badge number来显示日期数量,但是在一天过去之后数字没有改变,我的意思是它们应该实时更新。这是我的代码:我需要像天气实况应用程序一样的东西,这个应用程序不发送任何推送通知!

    int a = [DateComponents showDay];
    [UIApplication sharedApplication].applicationIconBadgeNumber = a ;

2
你知道吗,人们可以通过设置禁用通知。 - Cole Tobin
3个回答

33

目前在iOS上做到这一点并不容易。虽然用户偶尔打开应用程序,但您可以接近,但无法每天可靠地更新徽章 除非用户偶尔打开应用

您需要使用UILocalNotification。您可以创建和安排“静默”通知,以此方式更新应用程序徽章,而不会提醒用户或播放警报声音:

UILocalNotification* notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:seconds];
notification.timeZone = [NSTimeZone systemTimeZone];
notification.alertBody = nil;
notification.soundName = nil;
notification.applicationIconBadgeNumber = dayTomorrow;

[[UIApplication sharedApplication] scheduleLocalNotification:notification];
[notification release];

dayTomorrow是明天的日期,以整数形式表示。而seconds是某个时间间隔,例如距离午夜的时间间隔。当此通知被发送时,用户将不会收到任何警报或声音,但应用程序图标上的徽章应更新为新值。您可以使用repeatInterval属性使此通知每天重复,但问题在于,第二天您需要将dayTomorrow更改为另一个值,之后的日子也是如此。但是,重复的通知始终具有相同的applicationIconBadgeNumber值。

因此,我认为实现与您想要的内容接近的唯一方法是安排多个本地通知 - 您可以提前安排最多64个通知 - 每个通知相隔一天,并将那一天的值设置为applicationIconBadgeNumber。这些通知必须是非重复的,因此请确保将repeatInterval设置为nil(或者不设置它,因为nil是默认值)。假设所有这些通知都可靠地被iOS传递,您将能够在不打扰用户的情况下静默更新徽章号码长达64天。在那之后,徽章将停止更新...除非您在此期间成功安排更多通知 - 即当用户打开应用程序时。您可以尝试在应用程序启动时取消所有现有的预定通知:

[[UIApplication sharedApplication] cancelAllLocalNotifications];

然后循环提前64天,每天午夜定时一个通知,并使用正确的月份为 applicationIconBadgeNumber 属性更新徽章图标。只要用户每64天至少打开一次您的应用程序,您就可以保持徽章图标最新状态。如果您预计您的应用程序用户经常打开应用程序(例如,每天多次),那么优化可能是检查已安排的现有通知数量,然后取消它们并创建64个新通知,如下所示:

if ([[[UIApplication sharedApplication] scheduledLocalNotifications] count] < 10)
{
    //there are 10 or fewer days' worth of notifications scheduled, so create and
    //schedule more here, up to 64 in total.
}

需要注意以下几点:

  • 本地通知的可靠性我不太确定。推送通知并不是百分之百保证会被接收到,但我希望并期望本地通知是能够保证被传递的。例如,如果手机在午夜关闭,直到早上7点才打开,我希望设定在午夜的本地通知会立即传递。但我从未测试过这一点。
  • 如果你的应用程序在计划的通知发送时处于打开状态,则会调用application:didReceiveLocalNotification:方法,但徽章图标不会更新。因此,为了考虑到这种情况,我建议每次应用程序启动时(或从后台返回前台)将徽章图标更新为当天的日期。
  • 不确定苹果公司对徽章图标的这种使用会做出什么反应。在审查您的应用程序时,他们可能会觉得它会让用户感到困惑,因为徽章通常用于指示应用程序中新/更改数据的数量。因此,您的应用程序可能会因此被拒绝。
  • 理想情况下,苹果公司将为开发人员提供API,以向用户提供这种“一目了然”的信息,无论是通过以某种方式更改应用程序图标/徽章,还是通过小部件等方式实现。我上面描述的方法应该可以工作,但显然是为了绕过iOS当前的限制而进行的一种折衷办法。内置的日历应用程序每天都会更改其图标以显示月份的日期,我可以看到许多应用程序可以从这种行为中受益 - 例如,天气应用程序可以显示当前位置的天气条件图标。

编辑:

以下是一个代码片段,用于取消任何现有的本地通知并在将来的64个午夜计划通知,并将正确的日期指定为徽章号码:

[[UIApplication sharedApplication] cancelAllLocalNotifications];

NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents* components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:[NSDate date]];

[components setHour:0];
[components setMinute:0];
[components setSecond:0];

NSDate* midnightToday = [calendar dateFromComponents:components];

const int SECONDS_PER_DAY = 60 * 60 * 24;

for (int i = 1; i <= 64; i++)
{
    NSDate* futureDate = [midnightToday dateByAddingTimeInterval:i * SECONDS_PER_DAY];
    NSDateComponents* components = [calendar components:NSDayCalendarUnit fromDate:futureDate];

    UILocalNotification* notification = [[UILocalNotification alloc] init];
    notification.fireDate = futureDate;
    notification.timeZone = [NSTimeZone systemTimeZone];
    notification.alertBody = nil;
    notification.soundName = nil;
    notification.applicationIconBadgeNumber = components.day;

    //NSLog(@"futureDate: %@", [NSDateFormatter localizedStringFromDate:futureDate dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterMediumStyle]);
    //NSLog(@"notification: %@", notification);        

    [[UIApplication sharedApplication] scheduleLocalNotification:notification];
    [notification release];
}

[calendar release];

这里发生的情况是:

  • 我们使用[NSDate date]获取今天的日期,然后将其拆分成我们想要保留的部分(年、月、日),并将时间组件(小时、分钟、秒钟)设置为午夜。这给了我们midnightToday
  • 我们循环64次,每次创建一个新日期,该日期从1到64天的未来,通过取midnightToday并加上每天的秒数乘以我们希望日期在未来多少天内的天数来实现。我们使用此日期安排本地通知。
  • 我们还需要计算出徽章编号的月份日期。因此,我们再次使用NSDateComponents将未来日期分解为我们感兴趣的组件 - 在这种情况下,只需指定NSDayCalendarUnit即可得到日期。然后,我们可以将applicationIconBadgeNumber设置为components.day
  • 每个64个通知都由UIApplication调度,以在未来1到64天内发送。

请注意,这可能仅适用于使用格雷戈里历(西式)的用户 - 根据您的市场,您可能需要考虑世界各地使用的其他日历类型,并支持这些类型。


非常感谢Rob的帮助,但是SECONDS_PER_DAY显示的是明天,而i * SECONDS_PER_DAY则是后天,所以我将其更改为:60 * 60;并删除了i *,因为我的日历是波斯日历,所以我正在等待明天看到更新的徽章数量。 - iOS.Lover
1
谢谢,很抱歉这是一大段文字 - 我对这个问题非常感兴趣,必须找到解决方案!在调试器中查看futureDate时要小心,因为我认为它总是显示为GMT而不是您自己的时区。但是,如果您在设置了fireDate之后检查通知,它将显示为您所在时区的正确时间。所以请确保您检查了这一点。我还在我的设备上进行了设置,并耐心地等待明天看是否所有更新都正确 :) - Rob B
很遗憾,徽章编号没有更新!实际上什么也没发生。 - iOS.Lover
我认为这是由于您使用波斯日历造成的。我已经在代码中添加了一些NSLog语句 - 您能否将它们添加到您的代码中并取消注释,然后查看记录日期的样子。如果您的iOS设备设置为使用波斯日历,则无法像我在代码中那样使用NSGregorianCalendar - 将其更改为NSPersianCalendar(仅用于测试目的 - 您可能需要进行其他更改以使其适用于所有可能的日历类型)。此外,请将SECONDS_PER_DAY计算更改回我使用的方式 - 您的更改似乎有误。 - Rob B
哦,你是在实际设备上测试而不是模拟器上测试吗?我不确定本地通知是否能在模拟器上正常工作。我已经在我的iPad上测试了上述代码几天了,徽章图标每天都能正确更新。不过我的国家/时间设置使用的是公历。 - Rob B
我在设备上进行了测试,我将再次测试并报告结果,谢谢。 - iOS.Lover

3

2

您是如何调用此方法的?如果您设置一个定时器每x秒运行一次此代码,它将在下一个定时器触发时更新。


如果您退出应用程序,计时器将不会运行。 - oskob
1
你可以发送一个UILocalNotification,在午夜时触发,重复间隔为NSDayCalendarUnit。在AppDelegate中更新徽章:- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification。 - Hubert Kunnemeyer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接