最近公司有一个日程同步日历的功能,以前没有做过,网上查资料,反反复复搞的时间还不短,算踩了不少坑;下面把一些重要的注意点与大家分享,希望有同学做此类型的功能可以少走弯路;
下面正式进入正题!!👇
本次分享主要有如下几点内容:
- 单条日程的写入;
- 重复日程的写入;
- 全天日程的写入;
- 系统日历中自定义的日程读取及删除;
- ⚠️注意的地方⚠️
一、单条日程的写入
说到做日程操作,必须会用到EKEventStore、EKEvent、EKAlarm这几个类;
EKEventStore:相当于事件的储存器,事件和提醒写入到日历,必须要通过它的承载;
EKEvent:日历的一个事件;
EKAlarm:创建一个提醒;
下面直接上代码☞:
EKEventStore *eventStore = [[EKEventStore alloc] init];
EKEvent *myEvent = [EKEvent eventWithEventStore:eventStore];
myEvent.title = title; //标题
myEvent.location = location;
myEvent.notes = notes; //描述
myEvent.startDate = startDate;
myEvent.endDate = endDate;
// 设置提醒
EKAlarm *alarm = [EKAlarm alarmWithRelativeOffset:((60*timeNumber.integerValue) * -1)];
[myEvent addAlarm:alarm]; // 多个提醒设置alarms属性就好
//添加calendar
[myEvent setCalendar:[eventStore defaultCalendarForNewEvents]];
//保存
[eventStore saveEvent:myEvent span:EKSpanThisEvent error:&err];
二、重复日程的写入
重复日程我们要用到EKRecurrenceRule,把重复规则去配置好,系统会自动生成对应的日程;在开发时,iOS实现与Android的不太一样,Android直接支持rfc5545Rrule规则来赋值实现(一句代码就搞定),iOS可就不一样了,必须要自己来算规则,不过不要慌,这些坑已经有前辈们帮我们踩过了;继续往下看👇:
2.1 自己配置规则
// 根据后台参数返回的规则,填充好initRecurrenceWithFrequency:frequency:方法的对应的参数即可,具体参数意思可在xcode中查看API的说明;
EKRecurrenceRule *rule = [[EKRecurrenceRule alloc] initRecurrenceWithFrequency:frequency
interval:interval
daysOfTheWeek:daysOfTheWeek
daysOfTheMonth:daysOfTheMonth
monthsOfTheYear:monthsOfTheYear
weeksOfTheYear:weeksOfTheYear
daysOfTheYear:daysOfTheYear
setPositions:setPositions
end:recurrenceEnd];
2.2 直接用rfc5545Rrule
// 使用方法如下:
EKRecurrenceRule *rule = [[EKRecurrenceRule alloc] initWithString:rfc5545Rrule];
[myEvent addRecurrenceRule:rule];
- (EKRecurrenceRule *)initWithString:(NSString *)rfc2445String {
// If the date formatter isn't already set up, create it and cache it for reuse.
if (dateFormatter == nil)
{
dateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
[dateFormatter setLocale:enUSPOSIXLocale];
[dateFormatter setDateFormat:@"yyyyMMdd'T'HHmmss'Z'"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
}
// Begin parsing
NSArray *components = [rfc2445String.uppercaseString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@";="]];
EKRecurrenceFrequency frequency = EKRecurrenceFrequencyDaily;
NSInteger interval = 1;
NSMutableArray *daysOfTheWeek = nil;
NSMutableArray *daysOfTheMonth = nil;
NSMutableArray *monthsOfTheYear = nil;
NSMutableArray *daysOfTheYear = nil;
NSMutableArray *weeksOfTheYear = nil;
NSMutableArray *setPositions = nil;
EKRecurrenceEnd *recurrenceEnd = nil;
for (int i = 0; i < components.count; i++)
{
NSString *component = [components objectAtIndex:i];
// Frequency
if ([component isEqualToString:@"FREQ"])
{
NSString *frequencyString = [components objectAtIndex:++i];
if ([frequencyString isEqualToString:@"DAILY"]) frequency = EKRecurrenceFrequencyDaily;
else if ([frequencyString isEqualToString:@"WEEKLY"]) frequency = EKRecurrenceFrequencyWeekly;
else if ([frequencyString isEqualToString:@"MONTHLY"]) frequency = EKRecurrenceFrequencyMonthly;
else if ([frequencyString isEqualToString:@"YEARLY"]) frequency = EKRecurrenceFrequencyYearly;
}
// Interval
if ([component isEqualToString:@"INTERVAL"])
{
interval = [[components objectAtIndex:++i] intValue];
}
// Days of the week
if ([component isEqualToString:@"BYDAY"])
{
daysOfTheWeek = [NSMutableArray array];
NSArray *dayStrings = [[components objectAtIndex:++i] componentsSeparatedByString:@","];
for (NSString *dayString in dayStrings)
{
if(dayString && ![dayString isEqualToString:@""] && dayString.length > 0){
int dayOfWeek = 0;
int weekNumber = 0;
// Parse the day of the week
if ([dayString rangeOfString:@"SU"].location != NSNotFound) dayOfWeek = 1;
else if ([dayString rangeOfString:@"MO"].location != NSNotFound) dayOfWeek = 2;
else if ([dayString rangeOfString:@"TU"].location != NSNotFound) dayOfWeek = 3;
else if ([dayString rangeOfString:@"WE"].location != NSNotFound) dayOfWeek = 4;
else if ([dayString rangeOfString:@"TH"].location != NSNotFound) dayOfWeek = 5;
else if ([dayString rangeOfString:@"FR"].location != NSNotFound) dayOfWeek = 6;
else if ([dayString rangeOfString:@"SA"].location != NSNotFound) dayOfWeek = 7;
// Parse the week number
weekNumber = [[dayString substringToIndex:dayString.length-2] intValue];
[daysOfTheWeek addObject:[EKRecurrenceDayOfWeek dayOfWeek:dayOfWeek weekNumber:weekNumber]];
}
}
}
// Days of the month
if ([component isEqualToString:@"BYMONTHDAY"])
{
daysOfTheMonth = [NSMutableArray array];
NSArray *dayStrings = [[components objectAtIndex:++i] componentsSeparatedByString:@","];
for (NSString *dayString in dayStrings)
{
[daysOfTheMonth addObject:[NSNumber numberWithInt:dayString.intValue]];
}
}
// Months of the year
if ([component isEqualToString:@"BYMONTH"])
{
monthsOfTheYear = [NSMutableArray array];
NSArray *monthStrings = [[components objectAtIndex:++i] componentsSeparatedByString:@","];
for (NSString *monthString in monthStrings)
{
[monthsOfTheYear addObject:[NSNumber numberWithInt:monthString.intValue]];
}
}
// Weeks of the year
if ([component isEqualToString:@"BYWEEKNO"])
{
weeksOfTheYear = [NSMutableArray array];
NSArray *weekStrings = [[components objectAtIndex:++i] componentsSeparatedByString:@","];
for (NSString *weekString in weekStrings)
{
[weeksOfTheYear addObject:[NSNumber numberWithInt:weekString.intValue]];
}
}
// Days of the year
if ([component isEqualToString:@"BYYEARDAY"])
{
daysOfTheYear = [NSMutableArray array];
NSArray *dayStrings = [[components objectAtIndex:++i] componentsSeparatedByString:@","];
for (NSString *dayString in dayStrings)
{
[daysOfTheYear addObject:[NSNumber numberWithInt:dayString.intValue]];
}
}
// Set positions
if ([component isEqualToString:@"BYSETPOS"])
{
setPositions = [NSMutableArray array];
NSArray *positionStrings = [[components objectAtIndex:++i] componentsSeparatedByString:@","];
for (NSString *potitionString in positionStrings)
{
[setPositions addObject:[NSNumber numberWithInt:potitionString.intValue]];
}
}
// RecurrenceEnd
if ([component isEqualToString:@"COUNT"])
{
NSUInteger occurenceCount = [[components objectAtIndex:++i] intValue];
recurrenceEnd = [EKRecurrenceEnd recurrenceEndWithOccurrenceCount:occurenceCount];
} else if ([component isEqualToString:@"UNTIL"])
{
NSDate *endDate = [dateFormatter dateFromString:[components objectAtIndex:++i]];
recurrenceEnd = [EKRecurrenceEnd recurrenceEndWithEndDate:endDate];
}
}
EKRecurrenceRule *rule = [[EKRecurrenceRule alloc] initRecurrenceWithFrequency:frequency
interval:interval
daysOfTheWeek:daysOfTheWeek
daysOfTheMonth:daysOfTheMonth
monthsOfTheYear:monthsOfTheYear
weeksOfTheYear:weeksOfTheYear
daysOfTheYear:daysOfTheYear
setPositions:setPositions
end:recurrenceEnd];
return rule;
}
三、全天日程的写入
写入全天日程用一个属性叫:allDay;
myEvent.allDay = YES;
四、系统日历中自定义的日程读取及删除
删除日程,只会删除你自己创建的日程,系统默认已写好的日程是无权删除的;
EKEventStore *eventStore = [[EKEventStore alloc] init];
// 获取到此事件
NSArray * eventArray = [eventStore calendarsForEntityType:EKEntityTypeEvent];
NSMutableArray * onlyArray = [NSMutableArray array];
for (int i=0; i<eventArray.count; i++) {
EKCalendar * tempCalendar = eventArray[i];
EKCalendarType type = tempCalendar.type;
if (type == EKCalendarTypeCalDAV) {
[onlyArray addObject:tempCalendar];
}
}
NSPredicate * predicate = [eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:onlyArray];
NSArray * events = [eventStore eventsMatchingPredicate:predicate];
for (int i = 0; i < events.count; i++) {
// 删除这一条事件
EKEvent *event = events[i];
// commit:NO:最后再一次性提交
[eventStore removeEvent:event span:EKSpanThisEvent commit:NO error:&error];
}
//一次提交所有操作到事件库
NSError *errored = nil;
BOOL commitSuccess = [eventStore commit:&errored];
到这里,我们日程的一些基本的操作都已经介绍完了,你以为这就完了,那就错了,如果你按照这样写进去,测试只要稍微测一下,就会给你提一堆问题,所以自测一定很重要,场景什么都自己过一遍,然后再提测,这样后面自己改的时候也能有底。继续往下看👇:
五、⚠️注意的地方⚠️
5.1 iPhone 时间设置12小时制咋搞❓❓
把我们拿到的时间戳转时间的时候,把时间格式写成@"YYYY-MM-dd HH:mm:ss"样式就可以了,这里要注意点就是小时那一部分,一定要写成大写的HH,这样系统就能自动识别出是12小时制,还是24小时制了;
+ (NSDate *)dateWithTimestamp:(NSTimeInterval)timestamp format:(NSString *)format {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(timestamp)];
NSDateFormatter *dateFmt = [[NSDateFormatter alloc] init];
if (format.length) {
dateFmt.dateFormat = format;
} else {
dateFmt.dateFormat = @"YYYY-MM-dd HH:mm:ss";
}
return date;
}
5.2 全天的开始时间/结束时间设了咋没用❓❓
在开始的时候,把时间算成从0点开始到23:59结束,系统直接给过滤掉了,后来发现,直接开始时间和结束时间都给年月日零点的格式就ok了;
+ (NSDate *)dateWithTimestamp:(NSTimeInterval)timestamp format:(NSString *)format onlyYearMonthDay:(BOOL)only {
NSDate *date = [self dateWithTimestamp:timestamp format:format];
if (only) {
NSTimeInterval interval = [date timeIntervalSince1970];
int daySeconds = 24 * 60 * 60;
NSInteger allDays = interval / daySeconds;
return [NSDate dateWithTimeIntervalSince1970:allDays * daySeconds];
}
return date;
}
5.3 全天提醒设置后怎么就不是我想要的呢❓❓
系统默认的开始时间和结束时间与中国的北京时间是有时差的,所以我们一定要加上对应的时差(具体看后台数据);
// 例如:
myEvent.startDate = [NSDate dateWithTimestamp:(startInterval+8*60*60) format:@"YYYY-MM-dd HH:mm:ss" onlyYearMonthDay:YES];
myEvent.endDate = [NSDate dateWithTimestamp:(endInterval+8*60*60) format:@"YYYY-MM-dd HH:mm:ss" onlyYearMonthDay:YES];
5.4 为啥后台给我了好几个提醒,最后就显示2个提醒❓❓
iPhone上默认只显示2条提醒,自己直接日历创建日程也只会显示2条提醒;
资料有说Mac会显示多条,具体没有验证,小伙伴可以自己验证一下,欢迎小伙伴告诉到评论区😁;
5.5 为啥就设置了标题和定位,就默认给我来个提醒:"出发时间"❓❓
这个也是系统默认的,只要你设置了定位,没有设置提醒项,系统就回默认给你填充一个"出发时间"的提醒;(当初在查问题的时候卡了很久😭😭)
关于日历的操作就分享到这里了,如果有什么可以补充的或疑问欢迎评论区留言,希望这篇文章可以帮助到有需要的你们。分享只是一种美德,我们应该多分享只是给身边的朋友,大家一起成长。