问题
I have an ics (Calendar) file that I'm opening with a UIDocumentInteractionController
, using presentOptionsMenuFromRect:
. When this runs, the "Open In" menu looks like this.
As you can see, no "Add to Calendar" option. Here's what gets me: I'm using the same exact code for a .vcf (contact card) file, and it works as expected with the "Open In Contacts" option available.
Am I missing some sort of permission in my Info.plist
for calendar access? Why can't UIDocumentInteractionController
handle the .ics
file type correctly, but .vcf
works just fine? Those two file types are very similar. From the options menu, if I mail the ics file to myself and open it from the mail app, it reads it just fine, and I can add the events to my calendar, so I know the data is valid. I've searched high and low for a solution, and nobody seems to know why Calendar access isn't working. Some questions I've come across that remain unanswered:
Unable to Add ics file to Calendar
How can I have UIDocumentInteractionController show Calendar as an option for opening a .ics file?
If Apple is doing this deliberately, the only reason I can think of is because they'd rather developers use EventKit to add events to Calendar. If true, that solution is rather frustrating. Any insight on this problem would be much appreciated.
回答1:
I ended up downloading the .ics file via (https://github.com/KiranPanesar/MXLCalendarManager). I was then able to use EventKit to parse the downloaded .ics file into an EKEvent and open it via EKEventEditViewController (https://developer.apple.com/library/prerelease/ios/samplecode/SimpleEKDemo/Listings/Classes_RootViewController_m.html). A little round about but seemed to work. Here's how I setup my webview controller class that implements this:
@interface WebViewController : UIViewController <UIWebViewDelegate, EKEventEditViewDelegate> {
// EKEventStore instance associated with the current Calendar application
@property (nonatomic, strong) EKEventStore *eventStore;
// Default calendar associated with the above event store
@property (nonatomic, strong) EKCalendar *defaultCalendar;
@end
@implementation WebViewController
- (void)viewDidLoad {
[super viewDidLoad];
...
// Initialize the event store
self.eventStore = [[EKEventStore alloc] init];
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = [request URL];
NSString *path = [url absoluteString];
NSRange range = [path rangeOfString:@".ics" options:NSCaseInsensitiveSearch];
if (range.length > 0) {
[self checkCalendarAndAddEvent:url];
return NO;
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
return YES;
}
-(void)checkCalendarAndAddEvent:(NSURL*)url
{
EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
if(status == EKAuthorizationStatusAuthorized)
{
[self addEventToCalendar:url];
} else
{
[self.eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
{
if (granted)
{
// Let's ensure that our code will be executed from the main queue
dispatch_async(dispatch_get_main_queue(), ^{
// The user has granted access to their Calendar; add to calendar
[self addEventToCalendar:url];
});
}else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Privacy Warning" message:@"Permission was not granted for Calendar"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
}];
}
}
-(void) addEventToCalendar: (NSURL *)url
{
MXLCalendarManager* calendarManager = [[MXLCalendarManager alloc] init];
self.defaultCalendar = self.eventStore.defaultCalendarForNewEvents;
[calendarManager scanICSFileAtRemoteURL:url withCompletionHandler:^(MXLCalendar *calendar, NSError *error) {
MXLCalendarEvent *mxlEvent = calendar.events.firstObject;
EKEventEditViewController *addController = [[EKEventEditViewController alloc] init];
EKEvent * event = [EKEvent eventWithEventStore:self.eventStore];
event.location = mxlEvent.eventLocation;
event.startDate = mxlEvent.eventStartDate;
event.endDate = mxlEvent.eventEndDate;
event.title = mxlEvent.eventSummary;
event.notes = mxlEvent.eventDescription;
addController.event = event;
// Set addController's event store to the current event store
addController.eventStore = self.eventStore;
addController.editViewDelegate = self;
[self presentViewController:addController animated:YES completion:nil];
}];
}
@end
I also had to slightly modify parts of MXLCalendarManager.m to be ready for my specific types of .ics formatting. For example, my summary section of my .ics file looks like:
DESCRIPTION;LANGUAGE=en-us:The following details your appointment:\n\n\n
where as MXLCalendarManager is only looking for:
DESCRIPTION: (Something).
I modified the code as such to account for the ;ln. This also removed all the artificial line breaks but allowed me add my own in the summary description:
// Extract event description
[eventScanner scanUpToString:@"DESCRIPTION" intoString:nil];
[eventScanner scanUpToString:@":" intoString:nil];
[eventScanner scanUpToString:@"\nSEQUENCE" intoString:&descriptionString];
if(descriptionString.length > 1)
{
descriptionString = [descriptionString substringFromIndex:1];
descriptionString = [[[descriptionString stringByReplacingOccurrencesOfString:@"\nSEQUENCE" withString:@""] stringByReplacingOccurrencesOfString:@"\r\n " withString:@""] stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];
}
来源:https://stackoverflow.com/questions/27927665/uidocumentinteractioncontroller-calendar-access