【iOS】日曆行程的增刪改查(完整)
前言
我們可以使用系統提供的EventKit框架來訪問和操作使用者的日曆日程和提醒(雖然日曆和提醒是兩個獨立的app,但是是用同一個框架來處理資料)。同樣地,日曆和提醒的資料的資料,都是儲存在同一個叫做Calendar Database的資料庫中:
EventKit不僅能獲取已存在的日程和提醒,還能在自己的app中建立、編輯、刪除使用者的日程和提醒,還能新增提醒、監聽變化等。
在iOS10+中,若要訪問使用者日程或提醒,需要在info.plist中分別新增NSRemindersUsageDescription和NSCalendarsUsageDescription
一. 讀取日程
我們可以通過EKEventStore類來對使用者的Calendar database進行查詢、建立、編輯、刪除等操作。我們可以使用條件來獲取符合條件的一組日程,也可以用唯一標識來獲取指定的一條日程。獲取到的每一條日程都是一個EKEvent的例項物件,因此我們修改EKEvent物件的屬性即可實現修改日程資訊。
1.建立連線
#import <EventKit/EventKit.h>
...
EKEventStore *store = [[EKEventStore alloc] init];
EKEventStore物件的建立和釋放會比較耗時,因此我們一般會在app載入後只建立一個event store物件。
2.通過條件獲取日程
如果要獲取一個時間段內的日程,可以使用EKEventStore物件的eventsMatchingPredicate: 方法。下面程式碼演示如何獲取昨天至一年後的所有日程:
// 獲取日曆物件
NSCalendar *calendar = [NSCalendar currentCalendar];
// 建立開始時間
NSDateComponents *oneDayAgoComponents = [[NSDateComponents alloc] init];
oneDayAgoComponents.day = -1;
NSDate *oneDayAgo = [calendar dateByAddingComponents:oneDayAgoComponents
toDate:[NSDate date]
options:0];
// 建立結束時間
NSDateComponents *oneYearFromNowComponents = [[NSDateComponents alloc] init];
oneYearFromNowComponents.year = 1;
NSDate *oneYearFromNow = [calendar dateByAddingComponents:oneYearFromNowComponents
toDate:[NSDate date]
options:0];
// 建立條件
NSPredicate *predicate = [store predicateForEventsWithStartDate:oneDayAgo endDate:oneYearFromNow calendars:nil];
// 獲得符合條件的所有日程
NSArray *events = [store eventsMatchingPredicate:predicate];
3.批量處理日程
如果需要批量處理得到的日程,可以用EKEventStore
例項的enumerateEventsMatchingPredicate:usingBlock:方法(同步方法,為了不阻塞主執行緒建議在其它執行緒中執行),例如打印出所有符合條件的日程標題:
[store enumerateEventsMatchingPredicate:predicate usingBlock:^(EKEvent * _Nonnull event, BOOL * _Nonnull stop) {
NSLog(@"event:%@",event.title);
}];
4.通過唯一標識獲取日程
每一個日程都有隻讀的唯一標識屬性eventIdentifier,我們可以通過EKEventStore物件的eventWithIdentifier:方法,傳入唯一標識獲取指定的一個日程(這個標識是隻讀屬性,由系統指定,可以通過前面的條件查詢獲取,也可以在建立新的日程時儲存這個唯一標識),例如我們已經知道一個日程的eventIdentifier值為”D8574A98-A929-4A92-8E9F-048F46FB5DE7:717c8b40-44e3-31ab-8243-2d5918e266ef”:
EKEvent *event = [store eventWithIdentifier:@"D8574A98-A929-4A92-8E9F-048F46FB5DE7:717c8b40-44e3-31ab-8243-2d5918e266ef"];
NSLog(@"event:%@",event);
二.建立日程
1.通過程式碼建立
通過EKEvent物件的eventWithEventStore:來建立一個日程,並通過對應的屬性編輯日程詳細資訊,部分屬性如:
title - 日程的標題
startDate - 日程的開始日期
endDate - 日程的結束日期
calendar - 日程對應的日曆
alarms - 日程的提醒時間
recurrenceRules - 重複規則
例項程式碼:
EKEvent *event = [EKEvent eventWithEventStore:store];
event.title = @"程式碼建立的日程";
event.calendar = [store defaultCalendarForNewEvents];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
components.hour = 1;
NSDate *endTime = [calendar dateByAddingComponents:components toDate:[NSDate date] options:0];
event.startDate = [NSDate date];
event.endDate = endTime;
event.notes = @"檔期詳情:hyaction://hunyu-music";
[event addAlarm:[EKAlarm alarmWithRelativeOffset:-10*60]];
NSError *error;
[store saveEvent:event span:EKSpanFutureEvents commit:YES error:&error];
if (!error) {
NSLog(@"新增成功!");
}else{
NSLog(@"新增失敗:%@",error);
}
2.通過系統日曆ui新增日程
#import <EventKitUI/EventKitUI.h>
...
EKEventEditViewController *vc = [[EKEventEditViewController alloc] init];
vc.eventStore = store;
vc.editViewDelegate = self;
[self presentViewController:vc animated:YES completion:nil];
通過實現EKEventEditViewDelegate代理事件獲得結果:
- (void)eventEditViewController:(EKEventEditViewController *)controller didCompleteWithAction:(EKEventEditViewAction)action{
NSLog(@"新增日程結果:%zd",action);
[self dismissViewControllerAnimated:YES completion:nil];
}
三.編輯和刪除日程
我們可以通過修改event的屬性值來對日程進行編輯,最後需要呼叫EKEventStore的例項方法saveEvent:span:commit:error:進行持久化儲存:
event.title = @"修改後的標題";
NSError *error;
[store saveEvent:event span:EKSpanFutureEvents commit:YES error:&error];
通過EKEventStore的例項方法removeEvent:span:commit:error:.來刪除日程:
NSError *error;
[store removeEvent:event span:EKSpanFutureEvents error:&error];
四.新增提醒
我們可以給日程新增本地推送提醒,在指定的時間或地點給使用者進行提醒。
1.基於時間的提醒
我們可以通過event的 addAlarm:方法為一個日程新增提醒。我們可以指定一個確切時間或一個相對時間(只能是日程開始時間之前)。通過removeAlarm: 方法可將提醒移除。
如在開始時間前10分鐘提醒:
[event addAlarm:[EKAlarm alarmWithRelativeOffset:-10*60]];
2.基於地理位置的提醒
我們可以設定當使用者進入或離開指定的地理位置區域時,觸發日程提醒。例如當用戶離開公司,提醒使用者需要到超市購買日用品,作為開發者,需要確定一個經緯度以及一個半徑範圍。
EKAlarm *alarm = [[EKAlarm alloc] init];
EKStructuredLocation *location = [EKStructuredLocation
locationWithTitle:@"Current Location"];
location.geoLocation = [[CLLocation alloc] initWithLatitude:23.1754700000 longitude:113.4147400000];
alarm.structuredLocation = location;
alarm.proximity = EKAlarmProximityEnter;
[event addAlarm:alarm];