Calendar: Getting Started

This quick start tutorial demonstrates how to create a simple iOS application with TKCalendar.

Prerequisites

This article assumes that you have followed the Downloading UI for iOS, Installing UI for iOS and Setting Up the project steps from the common Getting Started article.

Setting up TKCalendar

Since iOS 10, Apple requires explicit description when NSCalendar is being used, more precisely when one is trying to access the phone's calendar events. This description is prompted to the user, only when there is an attempt for accessing events, and requires user's confirmation.

If you are using TelerikUI but never accessing device's calendar your users won't get prompted with this message.

In order to submit your application to the AppStore make sure to include a NSCalendarsUsageDescription variable to your info.plist and provide some description, for example "This application need to acces your calendar events."

Now that our project is created and the TelerikUI.framework is added, we can start referencing and using the TelerikUI types:

Open your ViewController.m file and add a reference to Telerik UI header file:

#import <TelerikUI/TelerikUI.h>

Note that starting with Xcode 6 Apple doesn't generate the precompiled headers file automatically. That is why you should add import the UIKit framework before importing TelerikUI:

#import <UIKit/UIKit.h>

If you are writing Swift, add the same line in your bridging header.

Type the following code in viewDidLoad method:

TKCalendar *calendarView = [[TKCalendar alloc] initWithFrame:self.view.bounds];
calendarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:calendarView];
let calendarView = TKCalendar(frame: self.view.bounds)
calendarView.autoresizingMask = UIViewAutoresizing(rawValue:UIViewAutoresizing.flexibleWidth.rawValue | UIViewAutoresizing.flexibleHeight.rawValue)
self.view.addSubview(calendarView)
TKCalendar calendarView = new TKCalendar (this.View.Bounds);
calendarView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
this.View.AddSubview (calendarView);

This code creates a new instance of TKCalendar and adds it as a subview of the ViewController's main view. The autoresizingMask property is set in order to allow correct resizing of the calendar when the device is rotated in landscape mode.

The next step is to create some random data that will be consumed by the calendar. You can use the following code:

TKCalendarEvent *event = [TKCalendarEvent new];
NSMutableArray *array = [NSMutableArray new];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *date = [NSDate date];
for (int i = 0; i<3; i++) {
    event.title = @"Sample event";
    event.allDay = YES;
    NSDateComponents *components = [calendar components:NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear fromDate:date];
    components.day = arc4random()%50;
    event.startDate = [calendar dateFromComponents:components];
    event.endDate = [calendar dateFromComponents:components];
    event.eventColor = [UIColor redColor];

    [array addObject:event];
}

self.events = array;
let array = NSMutableArray();
let calendar = Calendar.current
let date = Date()
for _ in 0..<3 {
    let event = TKCalendarEvent()
    event.title = "Sample event"
    var components = (self.calendarView.calendar as NSCalendar).components(NSCalendar.Unit(rawValue:NSCalendar.Unit.year.rawValue | NSCalendar.Unit.month.rawValue | NSCalendar.Unit.day.rawValue), from: date)
    components.day = Int(arc4random() % 50)
    event.startDate = calendar.date(from: components)!
    event.endDate = calendar.date(from: components)!
    event.eventColor = UIColor.red
    self.events.add(event)
}

self.events = array;
events = new List<TKCalendarEvent> ();
NSCalendar calendar = new NSCalendar (NSCalendarType.Gregorian);
NSDate date = NSDate.Now;

Random r = new Random ();
for (int i = 0; i < 3; i++) {
    TKCalendarEvent ev = new TKCalendarEvent ();
    ev.Title = "Sample event";
    NSDateComponents components = calendar.Components (NSCalendarUnit.Day | NSCalendarUnit.Month | NSCalendarUnit.Year, date);
    components.Day = r.Next () % 20;
    ev.StartDate = calendar.DateFromComponents (components);
    ev.EndDate = calendar.DateFromComponents (components);
    ev.EventColor = UIColor.Red;
    events.Add (ev);
}

This code will add 10 events with random dates to an array named events. The arc4random method is being used to create the random dates. The code also assigns a title and a color to the events.

Now let's add this random data to the calendar and present it. In order to do this, we should first adopt the TKCalendarDataSource protocol:

@interface CalendarDocsWithSimpleEvent() <TKCalendarDataSource, TKCalendarDelegate>
class CalendarDocsWithSimpleEvent: TKExamplesExampleViewController, TKCalendarDataSource, TKCalendarDelegate {
class CalendarDataSource : TKCalendarDataSource

And we should implement its calendar:eventsForDate: method:

- (NSArray *)calendar:(TKCalendar *)calendar eventsForDate:(NSDate *)date
{
    NSDateComponents *components = [self.calendarView.calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:date];
    components.hour = 23;
    components.minute = 59;
    components.second = 59;
    NSDate *endDate = [self.calendarView.calendar dateFromComponents:components];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(startDate <= %@) AND (endDate >= %@)", endDate, date];
    return [self.events filteredArrayUsingPredicate:predicate];
}
func calendar(_ calendar: TKCalendar, eventsFor date: Date) -> [Any]? {
    var components = (self.calendarView.calendar as NSCalendar).components(NSCalendar.Unit(rawValue:NSCalendar.Unit.year.rawValue | NSCalendar.Unit.month.rawValue | NSCalendar.Unit.day.rawValue), from: date)
    components.hour = 23
    components.minute = 59
    components.second = 59
    let endDate = self.calendarView.calendar.date(from: components)
    let predicate = NSPredicate(format: "(startDate <= %@) AND (endDate >= %@)", endDate! as CVarArg, date as CVarArg)
    return self.events.filtered(using: predicate) as [AnyObject]?
}
public TKCalendarEventProtocol[] EventsForDate (TKCalendar calendar, NSDate date)
{
    NSDateComponents components = calendar.Calendar.Components (NSCalendarUnit.Day | NSCalendarUnit.Month | NSCalendarUnit.Year, date);
    components.Hour = 23;
    components.Minute = 59;
    components.Second = 59;
    NSDate endDate = calendar.Calendar.DateFromComponents (components);
    List<TKCalendarEventProtocol> filteredEvents = new List<TKCalendarEventProtocol> ();
        for (int i = 0; i < this.events.Count; i++) {
            TKCalendarEventProtocol ev = this.events[i];
            if (ev.StartDate.SecondsSinceReferenceDate <= endDate.SecondsSinceReferenceDate && 
                ev.EndDate.SecondsSinceReferenceDate >= date.SecondsSinceReferenceDate) {
                filteredEvents.Add (ev);
            }
    }
    return filteredEvents.ToArray ();
}

Here, the predicate is used to filter the events array by date. Do not forget to assign the dataSource property of TKCalendar:

calendarView.dataSource = self;
calendarView.dataSource = self
calendarView.DataSource = new CalendarDataSource (this);

For information about populating TKCalendar with EventKit events, please refer to the following article: Populating with data

As a next step you may want to tune up the calendar more precisely by specifying minimum and maximum allowed dates. This can be done by setting the minDate and maxDate properties:

calendarView.minDate = [TKCalendar dateWithYear:2010 month:1 day:1 withCalendar:nil];
calendarView.maxDate = [TKCalendar dateWithYear:2016 month:12 day:31 withCalendar:nil];
calendarView.minDate = TKCalendar.date(withYear: 2010, month: 1, day: 1, with: nil)
calendarView.maxDate = TKCalendar.date(withYear: 2016, month: 12, day: 31, with: nil)
calendarView.MinDate = TKCalendar.DateWithYear (2010, 1, 1, calendar);
calendarView.MaxDate = TKCalendar.DateWithYear (2016, 12, 31, calendar);

By default, TKCalendar displays the current date, use the navigateToDate:animated method to display a different date:

NSDateComponents *components = [NSDateComponents new];
components.year = 2016;
components.month = 6;
components.day = 1;
NSDate *newDate = [self.calendarView.calendar dateFromComponents:components];
[self.calendarView navigateToDate:newDate animated:NO];
var components = DateComponents()
components.year = 2016
components.month = 6
components.day = 1
let newDate = self.calendarView.calendar.date(from: components)
calendarView.navigate(to: newDate!, animated: false)
NSDateComponents newComponents = new NSDateComponents();
newComponents.Year = 2015;
newComponents.Month = 5;
newComponents.Day = 1;
NSDate newDate = calendarView.Calendar.DateFromComponents (newComponents);
calendarView.NavigateToDate (newDate, true);

TKCalendar sends different notifications. For example, in order to be notified when a date was selected, override the calendar:didSelectDate: method of TKCalendarDelegate protocol:

- (void)calendar:(TKCalendar *)calendar didSelectDate:(NSDate *)date
{
    NSLog(@"%@", date);
}
func calendar(_ calendar: TKCalendar, didSelect date: Date) {
    print("%@", date)
}
class CalendarDelegate : TKCalendarDelegate
{
    public override void DidSelectDate (TKCalendar calendar, NSDate date)
    {
        Console.WriteLine ("{0}", date);
    }
}

Note that TKCalendar supports single, multiple and range date selection. Selection modes are described in detail in the article about Selection.

Along with selection notifications TKCalendar supports navigation and customization notifications by adopting the TKCalendarDelegate protocol. These notifications are described in the articles about: Navigation and Customizations.

Here is the full code of this example:

#import "CalendarDocsWithSimpleEvent.h"
#import <TelerikUI/TelerikUI.h>

@interface CalendarDocsWithSimpleEvent() <TKCalendarDataSource, TKCalendarDelegate>

@property (nonatomic, strong) NSArray *events;
@property (nonatomic, strong) TKCalendar *calendarView;

@end

@implementation CalendarDocsWithSimpleEvent

- (void)viewDidLoad
{
    [super viewDidLoad];

    TKCalendar *calendarView = [[TKCalendar alloc] initWithFrame:self.view.bounds];
    calendarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self.view addSubview:calendarView];

    calendarView.delegate = self;

    calendarView.dataSource = self;

    calendarView.minDate = [TKCalendar dateWithYear:2010 month:1 day:1 withCalendar:nil];
    calendarView.maxDate = [TKCalendar dateWithYear:2016 month:12 day:31 withCalendar:nil];

    self.calendarView = calendarView;

    TKCalendarEvent *event = [TKCalendarEvent new];
    NSMutableArray *array = [NSMutableArray new];
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDate *date = [NSDate date];
    for (int i = 0; i<3; i++) {
        event.title = @"Sample event";
        event.allDay = YES;
        NSDateComponents *components = [calendar components:NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear fromDate:date];
        components.day = arc4random()%50;
        event.startDate = [calendar dateFromComponents:components];
        event.endDate = [calendar dateFromComponents:components];
        event.eventColor = [UIColor redColor];

        [array addObject:event];
    }

    self.events = array;

    NSDateComponents *components = [NSDateComponents new];
    components.year = 2016;
    components.month = 6;
    components.day = 1;
    NSDate *newDate = [self.calendarView.calendar dateFromComponents:components];
    [self.calendarView navigateToDate:newDate animated:NO];

    [self.calendarView reloadData];
}

#pragma mark TKCalendarDataSource

- (NSArray *)calendar:(TKCalendar *)calendar eventsForDate:(NSDate *)date
{
    NSDateComponents *components = [self.calendarView.calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:date];
    components.hour = 23;
    components.minute = 59;
    components.second = 59;
    NSDate *endDate = [self.calendarView.calendar dateFromComponents:components];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(startDate <= %@) AND (endDate >= %@)", endDate, date];
    return [self.events filteredArrayUsingPredicate:predicate];
}

#pragma mark TKCalendarDelegate
- (void)calendar:(TKCalendar *)calendar didSelectDate:(NSDate *)date
{
    NSLog(@"%@", date);
}

@end
import Foundation

class CalendarDocsWithSimpleEvent: TKExamplesExampleViewController, TKCalendarDataSource, TKCalendarDelegate {

    let calendarView = TKCalendar();
    var events = NSMutableArray();

    override func viewDidLoad() {
        super.viewDidLoad()

        let calendarView = TKCalendar(frame: self.view.bounds)
        calendarView.autoresizingMask = UIViewAutoresizing(rawValue:UIViewAutoresizing.flexibleWidth.rawValue | UIViewAutoresizing.flexibleHeight.rawValue)
        self.view.addSubview(calendarView)

        calendarView.delegate = self
        calendarView.dataSource = self

        calendarView.minDate = TKCalendar.date(withYear: 2010, month: 1, day: 1, with: nil)
        calendarView.maxDate = TKCalendar.date(withYear: 2016, month: 12, day: 31, with: nil)

        let array = NSMutableArray();
        let calendar = Calendar.current
        let date = Date()
        for _ in 0..<3 {
            let event = TKCalendarEvent()
            event.title = "Sample event"
            var components = (self.calendarView.calendar as NSCalendar).components(NSCalendar.Unit(rawValue:NSCalendar.Unit.year.rawValue | NSCalendar.Unit.month.rawValue | NSCalendar.Unit.day.rawValue), from: date)
            components.day = Int(arc4random() % 50)
            event.startDate = calendar.date(from: components)!
            event.endDate = calendar.date(from: components)!
            event.eventColor = UIColor.red
            self.events.add(event)
        }

        self.events = array;

        var components = DateComponents()
        components.year = 2016
        components.month = 6
        components.day = 1
        let newDate = self.calendarView.calendar.date(from: components)
        calendarView.navigate(to: newDate!, animated: false)


        self.calendarView.reloadData();
    }

    func calendar(_ calendar: TKCalendar, eventsFor date: Date) -> [Any]? {
        var components = (self.calendarView.calendar as NSCalendar).components(NSCalendar.Unit(rawValue:NSCalendar.Unit.year.rawValue | NSCalendar.Unit.month.rawValue | NSCalendar.Unit.day.rawValue), from: date)
        components.hour = 23
        components.minute = 59
        components.second = 59
        let endDate = self.calendarView.calendar.date(from: components)
        let predicate = NSPredicate(format: "(startDate <= %@) AND (endDate >= %@)", endDate! as CVarArg, date as CVarArg)
        return self.events.filtered(using: predicate) as [AnyObject]?
    }

    func calendar(_ calendar: TKCalendar, didSelect date: Date) {
        print("%@", date)
    }


}
    public class CalendarDocsWithSimpleEvent : XamarinExampleViewController
    {
        CalendarDelegate calendarDelegate;
        List<TKCalendarEvent> events;

        public TKCalendar CalendarView {
            get;
            set;
        }

        public TKCalendarEventProtocol[] EventsForDate {
            get;
            set;
        }

        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();
            TKCalendar calendarView = new TKCalendar (this.View.Bounds);
            calendarView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
            this.View.AddSubview (calendarView);

            calendarDelegate = new CalendarDelegate ();

            calendarView.DataSource = new CalendarDataSource (this);

            events = new List<TKCalendarEvent> ();
            NSCalendar calendar = new NSCalendar (NSCalendarType.Gregorian);
            NSDate date = NSDate.Now;

            Random r = new Random ();
            for (int i = 0; i < 3; i++) {
                TKCalendarEvent ev = new TKCalendarEvent ();
                ev.Title = "Sample event";
                NSDateComponents components = calendar.Components (NSCalendarUnit.Day | NSCalendarUnit.Month | NSCalendarUnit.Year, date);
                components.Day = r.Next () % 20;
                ev.StartDate = calendar.DateFromComponents (components);
                ev.EndDate = calendar.DateFromComponents (components);
                ev.EventColor = UIColor.Red;
                events.Add (ev);
            }

            calendarView.MinDate = TKCalendar.DateWithYear (2010, 1, 1, calendar);
            calendarView.MaxDate = TKCalendar.DateWithYear (2016, 12, 31, calendar);

//            calendarDelegate.events = this.events;
            calendarView.Delegate = calendarDelegate;

            NSDateComponents newComponents = new NSDateComponents();
            newComponents.Year = 2015;
            newComponents.Month = 5;
            newComponents.Day = 1;
            NSDate newDate = calendarView.Calendar.DateFromComponents (newComponents);
            calendarView.NavigateToDate (newDate, true);

            calendarView.ReloadData();
        }

        class CalendarDataSource : TKCalendarDataSource
        {
            CalendarDocsWithSimpleEvent main;
            public List<TKCalendarEvent> events;
            public CalendarDataSource(CalendarDocsWithSimpleEvent main)
            {
                this.main = main;
            }

            public TKCalendarEventProtocol[] EventsForDate (TKCalendar calendar, NSDate date)
            {
                NSDateComponents components = calendar.Calendar.Components (NSCalendarUnit.Day | NSCalendarUnit.Month | NSCalendarUnit.Year, date);
                components.Hour = 23;
                components.Minute = 59;
                components.Second = 59;
                NSDate endDate = calendar.Calendar.DateFromComponents (components);
                List<TKCalendarEventProtocol> filteredEvents = new List<TKCalendarEventProtocol> ();
                    for (int i = 0; i < this.events.Count; i++) {
                        TKCalendarEventProtocol ev = this.events[i];
                        if (ev.StartDate.SecondsSinceReferenceDate <= endDate.SecondsSinceReferenceDate && 
                            ev.EndDate.SecondsSinceReferenceDate >= date.SecondsSinceReferenceDate) {
                            filteredEvents.Add (ev);
                        }
                }
                return filteredEvents.ToArray ();
            }
        }

        class CalendarDelegate : TKCalendarDelegate
        {
            public override void DidSelectDate (TKCalendar calendar, NSDate date)
            {
                Console.WriteLine ("{0}", date);
            }
        }


}

You can easily change the way data is presented in chart by changing the view mode property:

self.calendarView.viewMode = TKCalendarViewModeYear;
self.calendarView.viewMode = TKCalendarViewMode.year
this.CalendarView.ViewMode = TKCalendarViewMode.Year;

All view modes are desctibed in the following article:
View modes