iOS日历日程大全

最近公司有一个日程同步日历的功能,以前没有做过,网上查资料,反反复复搞的时间还不短,算踩了不少坑;下面把一些重要的注意点与大家分享,希望有同学做此类型的功能可以少走弯路;

下面正式进入正题!!👇

本次分享主要有如下几点内容:

  1. 单条日程的写入;
  2. 重复日程的写入;
  3. 全天日程的写入;
  4. 系统日历中自定义的日程读取及删除;
  5. ⚠️注意的地方⚠️

一、单条日程的写入

说到做日程操作,必须会用到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

// 使用方法如下:
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 为啥就设置了标题和定位,就默认给我来个提醒:"出发时间"❓❓

这个也是系统默认的,只要你设置了定位,没有设置提醒项,系统就回默认给你填充一个"出发时间"的提醒;(当初在查问题的时候卡了很久😭😭)

关于日历的操作就分享到这里了,如果有什么可以补充的或疑问欢迎评论区留言,希望这篇文章可以帮助到有需要的你们。分享只是一种美德,我们应该多分享只是给身边的朋友,大家一起成长。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容