Events & Reminders on iOS

UIEvent & UICalendar Reminders iOS Objective C by Marcio Valenzuela Santiapps.com
UIEvent & UICalendar Reminders iOS Objective C by Marcio Valenzuela Santiapps.com

 

Many apps take advantage of the Events or Reminders on iOS. This is very useful when wanting to give the user the ability to add an event to the calendar or a reminder.

This is quite simple to do, so let’s take a look.  Create an NSObject Class to manage all our EventKit stuff and add the following:

#import <EventKit/EventKit.h>

@property (strong, readonly) EKEventStore *eventStore;

@property (assign, readonly) BOOL eventAccess;

@property (assign, readonly) BOOL reminderAccess;

So far we are simply importing the EventKit, which by the way you must add as Link Library in Target Settings, Summary.  Then we add a property to access the EventStore and some booleans.

– (id) init {
self = [super init]; if (self) {

_eventStore = [[EKEventStore alloc] init];

[_eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {

if (granted) { _eventAccess = YES;

} else {

NSLog(@”Event access not granted: %@”, error);

} }];

[_eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {

if (granted) { _reminderAccess = YES;

} else {
NSLog(@”Reminder access not granted: %@”,

error); }

}]; }

return self; }

Now we can determine whether we have access to that store or not.  Now create a ViewController which will actually use that class:

#import “EventKitController.h”

EventKitController *_eventKitController;

– (id) initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) {

_eventKitController = [[EventKitController alloc] init]; }

return self; }

If you run this, your device will ask you if you want this app to access your EventStore.  Now let’s add some events.  To do so you must select a calendar and then create the event in order to save it.  This is how you access calendars:

NSArray * allCalendars = [_eventStore calendarsForEntityType:EKEntityMaskEvent |

EKEntityMaskReminder];
NSMutableArray * writableCalendars = [NSMutableArray array]; for (EKCalendar * calendar in allCalendars) {

if (calendar.allowsContentModifications) { [writableCalendars addObject:calendar];

} }

Ok so let’s create an event:

//First create the event and then save it

EKEvent *event = [EKEvent eventWithEventStore:eventStore]; event.title = @”My Coding Event”;
event.startDate = startDate;
event.endDate = endDate;

event.calendar = eventStore.defaultCalendarForNewEvents;

NSError *err;
BOOL saved = [eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&err];

if (!saved) {
NSLog(@”Error creating the event”);

}

This is pretty simple.  Let’s modify the EventKit NSObject class we created to handle this kind of code:

– (void) addEventWithName:(NSString*) eventName startTime:(NSDate*) startDate endTime:(NSDate*) endDate {

if (!_eventAccess) {
NSLog(@”No event acccess!”); return;

}

//1. Create an Event EKEvent *event = [EKEvent

eventWithEventStore:self.eventStore]; event.title = eventName;

//3. Set the start and end date

event.startDate = startDate; event.endDate = endDate;

//4. Set an alarm (This is optional)

EKAlarm *alarm = [EKAlarm alarmWithRelativeOffset:-1800]; [event addAlarm:alarm];

//5. Add a note (This is optional)

event.notes = @”This will be exciting”;
//6. Specify the calendar to store the event

event.calendar=self.eventStore.defaultCalendarForNewEvents;

NSError *err;
BOOL success = [self.eventStore

saveEvent:event span:EKSpanThisEvent commit:YES error:&err];

if (!success) {
NSLog(@”There was an error saving event: %@”, err);

} }

Now declare the class in the EventKit NSObject Class by adding this to its interface:

-(void)addEventWithName:(NSString*)eventName startTime:(NSDate*)startDate endTime:(NSDate*)endDate;

and finally add an event from your viewcontroller class by calling this:

[_eventKitController addEventWithName:self.conference.name startTime:self.conference.startDate endTime:self.conference.endDate];

That’s it for events!  There are other options you can look at such as:

//1

EKRecurrenceRule *recurrenceRule =
[[EKRecurrenceRule alloc] initRecurrenceWithFrequency:EKRecurrenceFrequencyMonthly interval:1 end:nil];

//2

event.recurrenceRules = @[recurrenceRule];

EKRecurrenceEnd *end = [EKRecurrenceEnd recurrenceEndWithEndDate:endDate]; //or

EKRecurrenceEnd *end = [EKRecurrenceEnd recurrenceEndWithOccurrenceCount:5];

In which case you could modify the EventKit NSObject class to have this method:

– (void) addRecurringEventWithName:(NSString*) eventName startTime:(NSDate*) startDate endTime:(NSDate*) endDate {

if (!_eventAccess) {
NSLog(@”No event acccess!”); return;

}

//1. Create an Event

EKEvent *event = [EKEvent eventWithEventStore:self.eventStore];

//2. Set the title

event.title = eventName;

//3. Set the start and end date

event.startDate = startDate; event.endDate = endDate;

//4. Set an alarm (This is optional)

EKAlarm *alarm = [EKAlarm alarmWithRelativeOffset:-1800];

[event addAlarm:alarm];
//5. Add a note (This is optional)

event.notes = @”This will be exciting”;

//6. Add the recurrrence rule.

EKRecurrenceRule *rule = [[EKRecurrenceRule alloc]

initRecurrenceWithFrequency: EKRecurrenceFrequencyYearly interval:1 end:nil];

event.recurrenceRules = @[rule];

//7. Specify the calendar to store the event to

event.calendar = self.eventStore.defaultCalendarForNewEvents;

NSError *err;
BOOL success = [self.eventStore

saveEvent:event span:EKSpanThisEvent commit:YES error:&err];

if (!success) {
NSLog(@”There was an error saving event: %@”, err);

} }

So you would have to declare:

-(void)addRecurringEventWithName:(NSString*)eventName startTime:(NSDate*)startDate endTime:(NSDate*)endDate;

and call it like so:

[_eventKitController addRecurringEventWithName:self.conference.name startTime:self.conference.startDate endTime:self.conference.endDate];

Finally you could delete events by adding a queue to your NSObject class:

dispatch_queue_t _fetchQueue;

and define it in viewDidLoad:

_fetchQueue = dispatch_queue_create(“com.conferencePlannerForGeeks.fetchQueue”, DISPATCH_QUEUE_SERIAL);

and add the delete method like so:

– (void) deleteEventWithName:(NSString*) eventName startTime:(NSDate*) startDate endTime:(NSDate*) endDate {

if (!_eventAccess) {
NSLog(@”No event acccess!”); return;

}
dispatch_async(_fetchQueue, ^{

//1. Create a prediate to find the EKEvent to delete

NSPredicate *predicate = [self.eventStore

predicateForEventsWithStartDate:startDate endDate:endDate
calendars: @[self.eventStore.defaultCalendarForNewEvents]];

NSArray *events =
[self.eventStore eventsMatchingPredicate:predicate];

//2. Post filter the events array

NSPredicate *titlePredicate = [NSPredicate

predicateWithFormat:@”title matches %@”,

eventName]; events = [events

filteredArrayUsingPredicate:titlePredicate];

//3. Delete each of these events

NSError *err;
for (EKEvent *event in events) {

[self.eventStore
removeEvent:event span:event.hasRecurrenceRules ? EKSpanFutureEvents:EKSpanThisEvent commit:NO error:&err];

}

BOOL success = [self.eventStore commit:&err]; if (!success) {

NSLog(@”There was an error deleting event”); }

});
if (!_eventAccess) {

NSLog(@”No event acccess!”);

return; }

}

In the end you would declare it:

-(void)deleteEventWithName:(NSString*)eventName startTime:(NSDate*)startDate endTime:(NSDate*)endDate;

and call it like so:

[_eventKitController deleteEventWithName:self.conference.name startTime:self.conference.startDate endTime:self.conference.endDate];

Reminders are a bit different but still quite simple.  To create a basic reminder you do:

//1

EKReminder *reminder =

[EKReminder reminderWithEventStore:eventStore]; reminder.title = @”Creating my first reminder”; reminder.calendar =

[eventStore defaultCalendarForNewReminders];

//2

NSError *err; BOOL success =

[eventStore saveReminder:reminder commit:YES error:&err];

if (!success) {
NSLog(@”Error creating reminder”);

}

Notice you are still using the EventStore.  So we can still use our EventKit NSObject Class and say:

– (void) addReminderWithTitle:(NSString*) title dueTime:(NSDate*) dueDate {

if (!_reminderAccess) {
NSLog(@”No reminder acccess!”); return;

}

//1.Create a reminder

EKReminder *reminder = [EKReminder

reminderWithEventStore: self.eventStore];

//2. Set the title

reminder.title = title;

//3. Set the calendar

reminder.calendar =
[self calendarForReminders];

//4. Extract the NSDateComponents from the dueDate

NSCalendar *calendar =
[NSCalendar currentCalendar];

NSUInteger unitFlags = NSEraCalendarUnit |

NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;

NSDateComponents *dueDateComponents = [calendar components:unitFlags fromDate:dueDate];

//5. Set the due date

reminder.dueDateComponents = dueDateComponents;

//6. Save the reminder

NSError *err;
BOOL success = [self.eventStore

saveReminder:reminder commit:YES error:&err];

if (!success) {
NSLog(@”There was an error saving the reminder %@”,

} }

If you want to use a Calendar Title you can define it like so:

#define kRemindersCalendarTitle @”My Coding reminders”

Now you code for that calendar like so:

– (EKCalendar*) calendarForReminders {

//1

for (EKCalendar *calendar in [self.eventStore

calendarsForEntityType:EKEntityTypeReminder]) {

if ([calendar.title isEqualToString:kRemindersCalendarTitle]) {

return calendar; }

}

//2

EKCalendar *remindersCalendar = [EKCalendar

calendarForEntityType:EKEntityTypeReminder eventStore:self.eventStore];

remindersCalendar.title = kRemindersCalendarTitle; remindersCalendar.source =

self.eventStore.defaultCalendarForNewReminders.source;

NSError *err;
BOOL success = [self.eventStore

saveCalendar:remindersCalendar commit:YES error:&err];

if (!success) {
NSLog(@”There was an error creating the reminders

calendar”);

return nil; }

return remindersCalendar; }

Declare the method in the interface:

-(void)addReminderWithTitle:(NSString*)title dueTime:(NSDate*)dueDate;

Now call the method:

[_eventKitController addReminderWithTitle:

[NSString stringWithFormat:@”Buy your %@ ticket”, self.conference.name]

dueTime:[NSDate date]];

Voila, you’ve got calendar interaction for your app!

Cocos2d Tips: Sounds & Text

Professionalize your game by adding sounds and text for menus and labels throughout your game.

Sound Creates Impacts!
Sound Creates Impacts!

SOUND

To play sounds you need the Sound Engine, which you import into your layer like so:

#import “SimpleAudioEngine.h”

Then you can just code a method to handle some background music playing:

-(void)loadAudio {

// Loading Sounds Synchronously

[CDSoundEngine setMixerSampleRate:CD_SAMPLE_RATE_MID];

[[CDAudioManager sharedManager] setResignBehavior:kAMRBStopPlay

autoHandle:YES];

soundEngine = [SimpleAudioEngine sharedEngine];

[soundEngine preloadBackgroundMusic:BACKGROUND_MUSIC];

[soundEngine playBackgroundMusic:BACKGROUND_MUSIC];

}

You can see immediately that you can preload the audio in one place and then play it in another. This is great for game performance. This means you can preload audio in the main menu, for example, and then play it when you actually need it.

Another way to put this into perspective is to preload an audio when an object such as an enemy ship or main player is created and then play the specific sound when his state is changed. For example, if you create a main player class which manage the player’s state between walking, jumping, running, flying…you can make the calls to play different sound effects when the player’s state changes.

For example;

-(void)changeState:(CharacterStates)newState {

// stop all, nil action, nil move, newPosition ivar, set new state passed into it…

[self stopAllActions];

id action = nil;

[self setCharacterState:newState];

switch (newState) {

case kStateFlying:

action = [CCAnimate actionWithAnimation:flyingAnim];

break;

default:

break;

} // ends the switch that checks for state

if (action != nil) {

[self runAction:[CCRepeatForever actionWithAction:action]];

}

}

You can also get more sophisticated and preload audio into your layer using plists for lists of files which are loaded as soon as the scene is loaded. We will look at this in the Advanced flavor of this post!

Text is Terrific!
Text is Terrific!

TEXT

Now let’s move on to some text. One of my favorite places to add text is at the beginning of a layer when the game is about to start. For this we create a label like so:

CGSize winSize = [CCDirector sharedDirector].winSize;

label = [CCLabelTTF labelWithString:@”hello!” dimensions:CGSizeMake(300, 250) hAlignment:UITextAlignmentCenter fontName:@”Stencil” fontSize:40];

label.color = ccc3(000,000,000);

label.position = ccp(winSize.width/2, winSize.height/2);

This creates a simple label which you can place anywhere on the screen, change its color etc. Let’s say you also wanted to animate this label, you could string this along at the bottom:

CCScaleTo *scaleUp = [CCScaleTo actionWithDuration:2.5 scale:1.8];

CCScaleTo *scaleBack = [CCScaleTo actionWithDuration:0.5 scale:1.5];

CCDelayTime *delay = [CCDelayTime actionWithDuration:3.0];

CCFadeOut *fade = [CCFadeOut actionWithDuration:1.5];

CCHide *hide = [CCHide action];

CCSequence *sequence = [CCSequence actions:scaleUp, scaleBack, delay, fade, hide, nil];

[label runAction:sequence];

Cool! Now you’ve got the workings of some awesome eye candy for your games.