以编程方式在iPhone日历中添加自定义事件

185
有没有办法从自定义应用程序将iCal事件添加到iPhone日历中?
11个回答

170
根据 苹果文档,从iOS 6.0开始有所改变。
1) 你应该通过 "requestAccessToEntityType:completion:" 请求访问用户的日历,并在一个块内执行事件处理。
2) 现在需要立即提交您的事件,或将 "commit" 参数传递给您的保存 / 删除调用。
其他一切保持不变...
在代码中添加EventKit框架和#import <EventKit/EventKit.h> 在我的示例中,我有一个NSString *savedEventId实例属性。
添加一个事件:
    EKEventStore *store = [EKEventStore new];
    [store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
        if (!granted) { return; }
        EKEvent *event = [EKEvent eventWithEventStore:store];
        event.title = @"Event Title";
        event.startDate = [NSDate date]; //today
        event.endDate = [event.startDate dateByAddingTimeInterval:60*60];  //set 1 hour meeting
        event.calendar = [store defaultCalendarForNewEvents];
        NSError *err = nil;
        [store saveEvent:event span:EKSpanThisEvent commit:YES error:&err];
        self.savedEventId = event.eventIdentifier;  //save the event id if you want to access this later
    }];

移除事件:

    EKEventStore* store = [EKEventStore new];
    [store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
        if (!granted) { return; }
        EKEvent* eventToRemove = [store eventWithIdentifier:self.savedEventId];
        if (eventToRemove) {
            NSError* error = nil;
            [store removeEvent:eventToRemove span:EKSpanThisEvent commit:YES error:&error];
        }
    }];

如果您有多个日历,那么此操作将向您的默认日历添加事件,您需要找出哪个是默认日历。

Swift 版本

您需要导入 EventKit 框架。

import EventKit

添加事件

let store = EKEventStore()
store.requestAccessToEntityType(.Event) {(granted, error) in
    if !granted { return }
    var event = EKEvent(eventStore: store)
    event.title = "Event Title"
    event.startDate = NSDate() //today
    event.endDate = event.startDate.dateByAddingTimeInterval(60*60) //1 hour long meeting
    event.calendar = store.defaultCalendarForNewEvents
    do {
        try store.saveEvent(event, span: .ThisEvent, commit: true)
        self.savedEventId = event.eventIdentifier //save event id to access this particular event later
    } catch {
        // Display error to user
    }
}

删除事件

let store = EKEventStore()
store.requestAccessToEntityType(EKEntityTypeEvent) {(granted, error) in
    if !granted { return }
    let eventToRemove = store.eventWithIdentifier(self.savedEventId)
    if eventToRemove != nil {
        do {
            try store.removeEvent(eventToRemove, span: .ThisEvent, commit: true)
        } catch {
            // Display error to user
        }
    }
}

6
对我来说无效,一切都没有错误,但是日历中没有事件。 - Boris Gafurov
所有的内容都存储在ekevent对象中,但不会存储在日历中,请帮我。 - user2580666
1
@William T:我能否使用URL Scheme展示日历应用程序的“添加事件”屏幕并传递事件信息,以便当“添加事件”屏幕出现时,它将具有预填充的数据。用户只需按下添加事件按钮即可。在您的示例中,事件被添加而没有向用户指示。 - Ans
1
如果一切似乎正常但没有日历出现,请检查云端与本地日历是否有问题。如果您同时使用云端和本地日历,则云端日历可能会强制隐藏本地日历。 - zonabi
@William,这对我有效。在这里,我遇到了一个问题,如果我向日历添加多个事件并尝试从日历中删除事件,则仅会删除最后一个事件。我们如何在从日历中删除事件时获取事件的标识符? - Logger
2
@ReddyBasha,当您添加事件时,需要将eventIdentifier保存下来并存储以备将来使用。在您需要移除该事件时,应使用该事件ID。 - William T.

153
您可以使用OS 4.0中的Event Kit框架来实现此功能。
在窗口左侧的“组和文件导航器”中,右键单击“FrameWorks”组。选择“添加”,然后选择“现有框架”,再选择“EventKit.Framework”。
然后,您就可以像这样使用代码添加事件:
#import "EventTestViewController.h"
#import <EventKit/EventKit.h>

@implementation EventTestViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    EKEventStore *eventStore = [[EKEventStore alloc] init];

    EKEvent *event  = [EKEvent eventWithEventStore:eventStore];
    event.title     = @"EVENT TITLE";

    event.startDate = [[NSDate alloc] init];
    event.endDate   = [[NSDate alloc] initWithTimeInterval:600 sinceDate:event.startDate];

    [event setCalendar:[eventStore defaultCalendarForNewEvents]];
    NSError *err;
    [eventStore saveEvent:event span:EKSpanThisEvent error:&err];       
}

@end

18
感谢您发布这个。提醒所有读者注意内存泄漏。在这段代码示例中有几个。此外,最佳实践应该是在saveEvent:span:error后检查'err'的值,并相应地处理它。 - David Carney
你知道如何添加重复事件吗?比如每个星期一的事件? - Jay Vachhani
5
程序化添加重复事件:请查看http://developer.apple.com/library/ios/#documentation/EventKit/Reference/EKRecurrenceRuleClassRef/Reference/Reference.html#//apple_ref/doc/c_ref/EKRecurrenceRule。另一种选择是使用默认的框架提供的视图控制器来添加/编辑事件(例如Calendar At-A-Glance应用程序 http://bit.ly/cJq4Bh)。有关此选项,请参见http://developer.apple.com/library/ios/#documentation/EventKitUI/Reference/EKEventEditViewControllerClassRef/Reference/Reference.html。 - DenTheMan
要在XCode 4中添加框架,请参考这个SO问题:https://dev59.com/3XA75IYBdhLWcg3wVXav - Nate
1
4.0?在6中不会起作用,请参见上面的答案。 - Boris Gafurov

13

目前(2.1版本)仍然没有相应的API。不过在WWDC(苹果开发者大会)上,很多人已经对此功能感兴趣(包括我自己),建议大家访问以下网站并创建一个功能请求。如果有足够的兴趣和需求,他们可能会将ICal.framework移至公共SDK。

https://developer.apple.com/bugreporter/


5
回答已过时,请考虑将其删除。 - Jasper

12

iPhone OS 4.0新增了日历访问功能:

日历访问功能
应用现在可以使用事件套件 (Event Kit) 直接在日历应用程序中创建和编辑事件。
创建循环事件,设置开始和结束时间,并将它们分配给设备上的任何日历。


6

Swift 4.0实现:

在页面顶部使用import EventKit导入。

然后:

@IBAction func addtoCalendarClicked(sender: AnyObject) {

    let eventStore = EKEventStore()

    eventStore.requestAccess( to: EKEntityType.event, completion:{(granted, error) in

        if (granted) && (error == nil) {
            print("granted \(granted)")
            print("error \(error)")

            let event = EKEvent(eventStore: eventStore)

            event.title = "Event Title"
            event.startDate = Date()
            event.endDate = Date()
            event.notes = "Event Details Here"
            event.calendar = eventStore.defaultCalendarForNewEvents

            var event_id = ""
            do {
                try eventStore.save(event, span: .thisEvent)
                event_id = event.eventIdentifier
            }
            catch let error as NSError {
                print("json error: \(error.localizedDescription)")
            }

            if(event_id != ""){
                print("event added !")
            }
        }
    })
}

你能帮我解决有关谷歌日历的问题吗?关于同样的答案@Dashrath - Dilip Tiwari

5
你可以像Tristan所述那样使用Event API添加事件,还可以添加Google日历事件,该事件将显示在iOS日历中。 使用Google的API Objective-C客户端
  - (void)addAnEvent {
  // Make a new event, and show it to the user to edit
  GTLCalendarEvent *newEvent = [GTLCalendarEvent object];
  newEvent.summary = @"Sample Added Event";
  newEvent.descriptionProperty = @"Description of sample added event";

  // We'll set the start time to now, and the end time to an hour from now,
  // with a reminder 10 minutes before
  NSDate *anHourFromNow = [NSDate dateWithTimeIntervalSinceNow:60*60];
  GTLDateTime *startDateTime = [GTLDateTime dateTimeWithDate:[NSDate date]
                                                    timeZone:[NSTimeZone systemTimeZone]];
  GTLDateTime *endDateTime = [GTLDateTime dateTimeWithDate:anHourFromNow
                                                  timeZone:[NSTimeZone systemTimeZone]];

  newEvent.start = [GTLCalendarEventDateTime object];
  newEvent.start.dateTime = startDateTime;

  newEvent.end = [GTLCalendarEventDateTime object];
  newEvent.end.dateTime = endDateTime;

  GTLCalendarEventReminder *reminder = [GTLCalendarEventReminder object];
  reminder.minutes = [NSNumber numberWithInteger:10];
  reminder.method = @"email";

  newEvent.reminders = [GTLCalendarEventReminders object];
  newEvent.reminders.overrides = [NSArray arrayWithObject:reminder];
  newEvent.reminders.useDefault = [NSNumber numberWithBool:NO];

  // Display the event edit dialog
  EditEventWindowController *controller = [[[EditEventWindowController alloc] init] autorelease];
  [controller runModalForWindow:[self window]
                          event:newEvent
              completionHandler:^(NSInteger returnCode, GTLCalendarEvent *event) {
                // Callback
                if (returnCode == NSOKButton) {
                  [self addEvent:event];
                }
              }];
}

4

更新Dashrath答案的Swift 4版

import UIKit
import EventKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let eventStore = EKEventStore()

        eventStore.requestAccess( to: EKEntityType.event, completion:{(granted, error) in

            if (granted) && (error == nil) {


                let event = EKEvent(eventStore: eventStore)

                event.title = "My Event"
                event.startDate = Date(timeIntervalSinceNow: TimeInterval())
                event.endDate = Date(timeIntervalSinceNow: TimeInterval())
                event.notes = "Yeah!!!"
                event.calendar = eventStore.defaultCalendarForNewEvents

                var event_id = ""
                do{
                    try eventStore.save(event, span: .thisEvent)
                    event_id = event.eventIdentifier
                }
                catch let error as NSError {
                    print("json error: \(error.localizedDescription)")
                }

                if(event_id != ""){
                    print("event added !")
                }
            }
        })
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

同时别忘了添加日历使用权限 image for privary setting


3

使用Swift-4.2编写的可运行代码

import UIKit
import EventKit
import EventKitUI

class yourViewController: UIViewController{

    let eventStore = EKEventStore()

    func addEventToCalendar() {

    eventStore.requestAccess( to: EKEntityType.event, completion:{(granted, error) in
        DispatchQueue.main.async {
            if (granted) && (error == nil) {
                let event = EKEvent(eventStore: self.eventStore)
                event.title = self.headerDescription
                event.startDate = self.parse(self.requestDetails.value(forKey: "session_time") as? String ?? "")
                event.endDate = self.parse(self.requestDetails.value(forKey: "session_end_time") as? String ?? "")
                let eventController = EKEventEditViewController()
                eventController.event = event
                eventController.eventStore = self.eventStore
                eventController.editViewDelegate = self
                self.present(eventController, animated: true, completion: nil)

            }
        }


       })
    }

}

现在我们将获得事件屏幕,在这里您还可以修改设置:

enter image description here

现在添加委托方法来处理取消和添加事件按钮操作的事件屏幕:
    extension viewController: EKEventEditViewDelegate {

    func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
        controller.dismiss(animated: true, completion: nil)

    }
}

注意: 不要忘记在信息属性列表中添加NSCalendarsUsageDescription键。


1
请记得将endDate设置为已创建的事件,这是必须的。
否则它会以几乎无声的方式失败并出现以下错误:
"Error Domain=EKErrorDomain Code=3 "No end date has been set." UserInfo={NSLocalizedDescription=No end date has been set.}"

对我来说完整的可工作代码是:

EKEventStore *store = [EKEventStore new];
[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
    if (!granted) { return; }
    EKEvent *calendarEvent = [EKEvent eventWithEventStore:store];
    calendarEvent.title = [NSString stringWithFormat:@"CEmprendedor: %@", _event.name];
    calendarEvent.startDate = _event.date;
    // 5 hours of duration, we must add the duration of the event to the API
    NSDate *endDate = [_event.date dateByAddingTimeInterval:60*60*5];
    calendarEvent.endDate = endDate;
    calendarEvent.calendar = [store defaultCalendarForNewEvents];
    NSError *err = nil;
    [store saveEvent:calendarEvent span:EKSpanThisEvent commit:YES error:&err];
    self.savedEventId = calendarEvent.eventIdentifier;  //saving the calendar event id to possibly deleted them
}];

1
还要记得结束日期必须等于或晚于开始日期。否则,你会得到另一个错误。 - moody

0

谷歌的想法很好,但存在问题。

我可以成功地打开谷歌日历事件屏幕 - 但只能在主桌面版本上,而且在iPhone Safari上显示不正确。谷歌移动日历可以在Safari上正确显示,但似乎无法使用API添加事件。

目前,我看不到一个好的解决办法。


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