iOS EKEvent Store 在循环中重新创建 iCloud 日历,不会保存本地。
Posted
技术标签:
【中文标题】iOS EKEvent Store 在循环中重新创建 iCloud 日历,不会保存本地。【英文标题】:iOS EKEvent Store recreating iCloud calendars in a loop, won't save local. 【发布时间】:2012-12-13 21:52:59 【问题描述】:我遇到了一个奇怪的问题 EKEventStore、iCloud 和本地日历。如果启用了 iCloud,则会创建日历并将事件保存到您所期望的日历中。如果 iCloud 已关闭并且您尝试保存事件,则没有任何反应,但是设备会继续每 3-5 秒循环创建 iCloud 日历,直到重新打开 iCloud,然后所有这些日历作为副本涌入 iCloud。我使用的代码几乎与此处多次引用的确切代码以及 Apples Docs 中的代码完全相同。我完全不知道为什么它不起作用,而且通常关于 EKEventStore 的文档似乎很少。
//•••••••••••••••••••••••••••••••••••••••••••• •• #pragma mark – 保存事件 //••••••••••••••••••••••••••••••••••••••••••••••
-(void)saveEventWithDate:(NSDate *)startDate endDate:(NSDate *)endDate
AppData *theData = [self theAppData];
if([self checkIsDeviceVersionHigherThanRequiredVersion:@"6.0"])
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) // ios 6 Support
if (granted)
NSLog(@"Access Granted");
else
NSLog(@"Access Not Granted");
];
EKEvent *event = [EKEvent eventWithEventStore:eventStore];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([eventStore calendarWithIdentifier:[defaults objectForKey:@"My Calendar"]] != nil) // Calendar Existed
event.calendar = [eventStore calendarWithIdentifier:[defaults objectForKey:@"My Calendar"]];
NSLog(@"Calendar Existed");
else // Create Calendar
EKSource *theSource = nil;
for (EKSource* src in eventStore.sources)
if ([src.title isEqualToString:@"iCloud"])
theSource = src;
break;
if (src.sourceType == EKSourceTypeLocal && theSource==nil)
theSource = src;
break;
[self setupCalendarWithSource:theSource withEvent:event];
NSLog(@"Type of Event:%@",typeOfEvent);
if ([typeOfEvent isEqualToString:@"Hello"])
event.title = [NSString stringWithFormat:@"%@ Hello",[theData.hello_info objectForKey:@"customer_name"]];
event.location = [NSString stringWithFormat:@"Phone #%@",[theData.hello_info objectForKey:@"customer_phone_number"]];
event.notes = [NSString stringWithFormat:@"Hello Issue: %@",[theData.hello_info objectForKey:@"hello_issue"]];
NSLog(@"Hello");
event.startDate = startDate;
event.endDate = endDate;
event.allDay = NO;
EKAlarm *alarm = [EKAlarm alarmWithRelativeOffset:-1800]; // Half Hour Before
event.alarms = [NSArray arrayWithObject:alarm];
[eventStore saveEvent:event span:EKSpanThisEvent error:nil];
SAFE_PERFORM_WITH_ARG(_delegate, @selector(wasScheduled), nil);
-(void)setupCalendarWithSource:(EKSource *)theSource withEvent:(EKEvent *)event
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
EKCalendar *cal;
cal = [EKCalendar calendarWithEventStore:eventStore];
cal.title = @"My Appointments";
cal.source = theSource;
[eventStore saveCalendar:cal commit:YES error:nil];
NSLog(@"cal id = %@", cal.calendarIdentifier);
NSString *calendar_id = cal.calendarIdentifier;
[defaults setObject:calendar_id forKey:@"My Calendar"];
event.calendar = cal;
【问题讨论】:
【参考方案1】:我不确定你为什么会出现这种行为,但我认为由于禁用了 iCloud,系统无法对其进行查询,然后排队创建请求,一旦你唤醒 iCloud 就会解决(但我假设)。
无论如何,我想到的第一个解决方案就是以这种方式检查 iCloud 是否处于活动状态
EKSource *defaultSource = [eventStore defaultCalendarForNewEvents].source;
if (defaultSource.sourceType == EKSourceTypeCalDAV)
NSLog(@"iCloud Enable");
else
NSLog(@"iCloud Disable");
完成后,您可以将您的活动正确保存到默认来源,然后保持 2 个日历(本地日历和云端日历)彼此同步...
仍然会提示重新激活 iCloud 添加所有本地日历。
另请参阅此处的第二个答案Accessing programmatically created calendar on iOS device(这是我的想法;))
希望对你有所帮助。
编辑:也许没有必要创建第二个日历...尝试将日历的来源从 EKSourceTypeCalDAV 更改为 EKSourceTypeLocal ...不要忘记使用提交“YES”保存日历
EDIT2:好的,刚刚测试...
替换为:
else // Create Calendar
EKSource *theSource = nil;
for (EKSource* src in eventStore.sources)
if ([src.title isEqualToString:@"iCloud"])
theSource = src;
break;
if (src.sourceType == EKSourceTypeLocal && theSource==nil)
theSource = src;
break;
[self setupCalendarWithSource:theSource withEvent:event];
有了这个...
else // Create Calendar
EKSource *theSource = [eventStore defaultCalendarForNewEvents].source;
[self setupCalendarWithSource:theSource withEvent:event];
这样您将在正确的来源中创建日历(如果用户停用 iCloud 和 CalDAV,则为本地)
然后:
1) 当用户选择停用 iCloud 时,应将日历保留在 iphone 上(而不是删除),以便您在本地源中拥有云日历
2) 当用户选择激活 iCloud 时,会将他的本地日历与云合并,然后就可以了!
希望对你有帮助
【讨论】:
谢谢,不知道为什么这么多开发人员都在使用其他代码(我发现的所有教程都有相同的代码),但改变它使其按预期运行。 很高兴知道你解决了:)。不幸的是,根据我的经验,这些教程总是基本的,很少更新......也许它们是在 iCloud 之前编写的......无论如何我们不能说这不起作用,只是......不完整......就像任何自尊的教程:p 您不能简单地使用 [eventStore defaultCalendarForNewEvents].source 来创建日历。 Gmail 帐户也使用 EKSourceTypeCalDAV,它们不允许您创建任何日历。这就是为什么每个人都使用搜索方法而不是使用默认来源的原因。如果您有 Gmail 帐户,那么即使您禁用了 iCloud,发布的代码也会返回“启用 iCloud”。 答案比我可以在评论区放的长,并且原始问题与此代码无关。您可以发布一个新问题并让我知道,以便我发布我的答案。 @Bms270 好的,解决这个问题的代码在我帖子的编辑 2 部分,解释包含最终用户应该有的行为(通常是这样)。我在问你是否有更好的代码来解决这个问题,因为在 KDM 发布它之前也是我的问题;)如果是这样,你可以在我认为的新答案中发布它。【参考方案2】:如果您想要一种防弹方法来查找 iCloud 日历并在 iCloud 被禁用时恢复到本地日历,请使用以下代码。我已经包含了一些可能有帮助的 cmets:
for (EKSource *source in eventStore.sources) //Check for iCloud
if (source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:@"iCloud"])
NSLog(@"Found iCloud Service."); //Found iCloud
if([source calendarsForEntityType:EKEntityTypeEvent].count>0) //Check to see if Calendar is enabled on iCloud
NSLog(@"iCloud Calendar is Enabled."); //Calendar is Enabled
if([self saveEventCalendarWithSource:source])
return YES;
else
NSLog(@"iCloud Calendar is Disabled."); //Calendar is Disabled
//If we are here it means that we did not find iCloud Source with iCloud Name. Now trying any CalDAV type to see if we can find it
for (EKSource *source in self.reminderStore.sources) //Check for iCloud
if (source.sourceType == EKSourceTypeCalDAV)
[self logData:@"Trying to save calendar in EKSourceTypeCalDAV Service."];
if([self saveEventCalendarWithSource:source])
return YES;
//If we are here it means that we did not find iCloud and that means iCloud is not turned on. Use Local service now.
for (EKSource *source in self.reminderStore.sources) //Look for Local Source
if (source.sourceType == EKSourceTypeLocal) //Found Local Source
NSLog(@"Found Local Source.");
if([self saveEventCalendarWithSource:source])
return YES;
这是保存日历的代码:
- (Boolean) saveEventCalendarWithSource:(EKSource *)source
EKCalendar *Calendar = nil;
MyCalendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:eventStore];
MyCalendar.title = @"XXX";
MyCalendar.CGColor = [UIColor blueColor].CGColor;
MyCalendar.source = source;
NSError *err;
if([eventStore saveCalendar:MyCalendar commit:YES error:&err])
if(MyCalendar.calendarIdentifier == nil)
NSLog(@"Could not save Calendar: %@",err);
return FALSE;
NSLog(@"Calendar Created. Here's the identifier %@",[MyCalendar calendarIdentifier]);
return TRUE;
NSLog(@"Could not create calendar! Reason:%@",err.description);
return FALSE;
【讨论】:
如果启用了 iCloud,则此代码基本上会找到 iCloud,如果未启用 iCloud,则会找到本地。即使用户将 iCloud“描述”字段更改为其他内容,这也将起作用。【参考方案3】:您的帖子很有帮助,我正在努力解决完全相同的错误。谢谢 !
我只是做了一些小的修改,因为使用 defaultCalendarForNewEvents 源的解决方案并非在所有情况下都有效:某些源不允许您在其中创建新日历。
我只是检查了 icloud 源中的日历数量。如果计数为零,则日历同步关闭,我采用本地来源:
EKSource* localSource = nil;
EKSource* iCloudSource = nil;
for (EKSource* source in _eventStore.sources)
if (source.sourceType == EKSourceTypeLocal)
localSource = source;
else if(source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:@"iCloud"])
iCloudSource = source;
if (iCloudSource && [iCloudSource.calendars count] != 0)
calendar.source = iCloudSource;
else
calendar.source = localSource;
【讨论】:
以上是关于iOS EKEvent Store 在循环中重新创建 iCloud 日历,不会保存本地。的主要内容,如果未能解决你的问题,请参考以下文章