DataSource: Binding with UI Controls

TKDataSource works well with data enabled controls and provides an easy way to shape and present your data. The currently supported UI controls are:

This article describes how to bind TKDataSource and customize those controls.

UITableView

Setting the dataSource property is enough in order to present data in UITableView. TKDataSource will take care of the implementation of all methods in UITableViewDataSource protocol:

self.dataSource = [[TKDataSource alloc] initWithArray:@[ @10, @5, @12, @7, @44 ]];

UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
tableView.dataSource = self.dataSource;
[self.view addSubview:tableView];
let dataSource = TKDataSource(array: [ 10, 5, 12, 13, 7, 44 ])

let tableView = UITableView(frame: self.view.bounds.insetBy(dx: 0, dy: 30))
tableView.autoresizingMask = UIViewAutoresizing(rawValue: UIViewAutoresizing.flexibleWidth.rawValue | UIViewAutoresizing.flexibleHeight.rawValue)
tableView.dataSource = dataSource
self.view.addSubview(tableView)
NSObject[] array = new NSObject[] {
    NSObject.FromObject (10),
    NSObject.FromObject (5),
    NSObject.FromObject (12),
    NSObject.FromObject (13),
    NSObject.FromObject (7),
    NSObject.FromObject (44)
};

this.dataSource = new TKDataSource (array);

CGRect rect = this.View.Bounds;
rect.Inflate (0, -30);
UITableView table = new UITableView (rect);
table.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
table.DataSource = this.dataSource;
this.View.AddSubview (table);

You can specify displayKey and valueKey properties to specify what to display in table view cells:

NSMutableArray *items = [NSMutableArray new];
[items addObject:[[DSItem alloc] initWithName:@"John" value:50 group:@"A"]];
[items addObject:[[DSItem alloc] initWithName:@"Abby" value:33 group:@"A"]];
[items addObject:[[DSItem alloc] initWithName:@"Smith" value:42 group:@"B"]];
[items addObject:[[DSItem alloc] initWithName:@"Peter" value:28 group:@"B"]];
[items addObject:[[DSItem alloc] initWithName:@"Paula" value:25 group:@"B"]];

dataSource = [TKDataSource new];
dataSource.itemSource = items;
dataSource.displayKey = @"name";
var items = [DataSourceItem]()
items.append(DataSourceItem(name: "John", value: 50, group: "A"))
items.append(DataSourceItem(name: "Abby", value: 33, group: "A"))
items.append(DataSourceItem(name: "Smith", value: 42, group: "B"))
items.append(DataSourceItem(name: "Peter", value: 28, group: "B"))
items.append(DataSourceItem(name: "Paula", value: 25, group: "B"))

dataSource.itemSource = items
dataSource.displayKey = "name"
NSMutableArray items = new NSMutableArray();
items.Add (new DSItem () { Name = "John", Value = 50, Group = "A" });
items.Add (new DSItem () { Name = "Abby", Value = 33, Group = "A" });
items.Add (new DSItem () { Name = "Smith", Value = 42, Group = "B" });
items.Add (new DSItem () { Name = "Peter", Value = 28, Group = "B" });
items.Add (new DSItem () { Name = "Paula", Value = 25, Group = "B" });

this.dataSource = new TKDataSource ();
this.dataSource.ItemSource = items;
this.dataSource.DisplayKey = "Name";

In the majority of the scenarios you will also need to customize the cells. In this case you can implement the initCell block from TKDataSourceTableViewSettings class:

[self.dataSource.settings.tableView initCell:^(UITableView *tableView, NSIndexPath *indexPath, UITableViewCell *cell, id item) {
    cell.textLabel.text = @"Item:";
    cell.detailTextLabel.text = [self.dataSource textFromItem:item inGroup:nil];
}];
dataSource.settings.tableView .initCell({ (tableView, indexPath, cell, item) -> Void in
    cell.textLabel?.text = "Item:"
    cell.detailTextLabel?.text = dataSource.text(fromItem: item, in: nil)
});
this.dataSource.Settings.TableView.InitCell ((UITableView tableView, NSIndexPath indexPath, UITableViewCell cell, NSObject item) => {
    cell.TextLabel.Text = "Item:";
    cell.DetailTextLabel.Text = this.dataSource.TextFromItem(item, null);
});

If this is not enough to achieve to look you want, you can create your custom cells by using the createCell block function:

[self.dataSource.settings.tableView createCell:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath, id item) {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"cell"];
    }
    return cell;
}];
dataSource.settings.tableView.createCell { (tableView, indexPath, item) -> UITableViewCell in
    var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
    if cell == nil {
        cell = UITableViewCell(style:UITableViewCellStyle.value1, reuseIdentifier:"cell")
    }
    return cell!
}
this.dataSource.Settings.TableView.CreateCell ((UITableView tableView, NSIndexPath indexPath, NSObject item) => {
    UITableViewCell cell = tableView.DequeueReusableCell("cell");
    if (cell == null) {
        cell = new UITableViewCell(UITableViewCellStyle.Value1, "cell");
    }
    return cell;
});

TKDataSource will take care of everything and no code is necessary even when your data is grouped:

[self.dataSource group:^id(id item) {
    return @([item intValue] % 2 == 0);
}];
dataSource.group({ (item: Any) -> Any in
    return ((item as! NSNumber).int32Value % 2 == 0);
})
this.dataSource.Group ((NSObject item) => {
    return NSObject.FromObject(((NSNumber)item).Int32Value % 2 == 0);
});

UICollectionView

TKDataSource integrates well with UICollectionView. Just set the dataSource property and prepare the collection view:

UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
layout.itemSize = CGSizeMake(140, 140);

UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectInset(self.view.bounds, 0, 30) collectionViewLayout:layout];
collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
collectionView.dataSource = self.dataSource;
collectionView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:collectionView];
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 140, height: 140)

let collectionView = UICollectionView(frame:self.view.bounds.insetBy(dx: 0, dy: 30), collectionViewLayout:layout)
collectionView.autoresizingMask = UIViewAutoresizing(rawValue: UIViewAutoresizing.flexibleWidth.rawValue | UIViewAutoresizing.flexibleHeight.rawValue)
collectionView.dataSource = dataSource
collectionView.backgroundColor = UIColor.white
self.view.addSubview(collectionView)
var layout = new UICollectionViewFlowLayout();
layout.ItemSize = new CGSize (140, 140);

CGRect rect = this.View.Bounds;
rect.Inflate (0, -30);
var collectionView = new UICollectionView (rect, layout);
collectionView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
collectionView.DataSource = this.dataSource;
collectionView.BackgroundColor = UIColor.White;
this.View.AddSubview(collectionView);

Use the collection view settings class and its initCell in case you want to customize the cell appearance:

[self.dataSource.settings.collectionView initCell:^(UICollectionView *collectionView, NSIndexPath *indexPath, UICollectionViewCell *cell, id item) {
    TKCollectionViewCell *tkcell = (TKCollectionViewCell*)cell;
    tkcell.label.text = [self.dataSource textFromItem:item inGroup:nil];
    tkcell.backgroundColor = [UIColor yellowColor];
}];
dataSource.settings.collectionView.initCell({ (collectionView, indexPath,
    cell, item) -> Void in
    let tkCell = cell as! TKCollectionViewCell
    tkCell.label.text = dataSource.text(fromItem: item, in: nil)
});
this.dataSource.Settings.CollectionView.InitCell ((UICollectionView collection, NSIndexPath indexPath, UICollectionViewCell cell, NSObject item) => {
    var tkCell = cell as TKCollectionViewCell;
    tkCell.Label.Text = this.dataSource.TextFromItem(item, null);
    tkCell.BackgroundColor = UIColor.Yellow;
});

TKListView

You can also easily use TKListView with TKDataSource:

TKListView *listView = [[TKListView alloc] initWithFrame:CGRectInset(self.view.bounds, 0, 30)];
listView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
listView.dataSource = self.dataSource;
[self.view addSubview:listView];
let listView = TKListView(frame:self.view.bounds.insetBy(dx: 0, dy: 30))
listView.autoresizingMask = UIViewAutoresizing(rawValue: UIViewAutoresizing.flexibleWidth.rawValue | UIViewAutoresizing.flexibleHeight.rawValue)
listView.dataSource = dataSource
self.view.addSubview(listView)
CGRect rect = this.View.Bounds;
rect.Inflate (0, -30);
var listView = new TKListView (rect);
listView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
this.dataSource.SetDataSourceFor (listView);
this.View.AddSubview (listView);

The initCell and createCell methods can be used to customize the cell appearance:

[self.dataSource.settings.listView createCell:^TKListViewCell *(TKListView *listView, NSIndexPath *indexPath, id item) {
    return [listView dequeueReusableCellWithReuseIdentifier:@"myCustomCell" forIndexPath:indexPath];
}];
[self.dataSource.settings.listView initCell:^(TKListView *listView, NSIndexPath *indexPath, TKListViewCell *cell, id item) {
    cell.textLabel.text = [self.dataSource textFromItem:item inGroup:nil];
    ((TKView*)cell.backgroundView).fill = [TKSolidFill solidFillWithColor:[UIColor colorWithWhite:0.1 alpha:0.1]];
}];
dataSource.settings.listView.createCell({ (listView, indexPath, item) -> TKListViewCell! in
    return listView.dequeueReusableCell(withReuseIdentifier: "myCustomCell", for:indexPath) as! TKListViewCell
})

dataSource.settings.listView.initCell({ (listView, indexPath, cell, item) -> Void in
    cell.textLabel.text = dataSource.text(fromItem: item, in:nil)
    (cell.backgroundView as! TKView).fill = TKSolidFill(color: UIColor(white: 0.1, alpha: 0.1))
})
this.dataSource.Settings.ListView.CreateCell ((TKListView list1, NSIndexPath indexPath, NSObject item) => {
    return list1.DequeueReusableCell("myCustomCell", indexPath) as TKListViewCell;
});

this.dataSource.Settings.ListView.InitCell ((TKListView list2, NSIndexPath indexPath, TKListViewCell cell, NSObject item) => {
    cell.TextLabel.Text = this.dataSource.TextFromItem(item, null);
    (cell.BackgroundView as TKView).Fill = new TKSolidFill(new UIColor(0.1f, 0.1f, 0.1f, 0.1f));
});

TKChart

In order to present data in TKChart, you need to set the displayKey and valueKey properties. The displayKey defines the x-axis values, and the valueKey defines the y-axis values:

NSMutableArray *items = [NSMutableArray new];

[items addObject:[[DSItem alloc] initWithName:@"John" value:50 group:@"A"]];
[items addObject:[[DSItem alloc] initWithName:@"Abby" value:33 group:@"A"]];
[items addObject:[[DSItem alloc] initWithName:@"Paula" value:33 group:@"A"]];

[items addObject:[[DSItem alloc] initWithName:@"John" value:42 group:@"B"]];
[items addObject:[[DSItem alloc] initWithName:@"Abby" value:28 group:@"B"]];
[items addObject:[[DSItem alloc] initWithName:@"Paula" value:25 group:@"B"]];

self.dataSource.displayKey = @"name";
self.dataSource.valueKey = @"value";
self.dataSource.itemSource = items;

TKChart *chart = [[TKChart alloc] initWithFrame:self.view.bounds];
chart.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
chart.dataSource = self.dataSource;
[self.view addSubview:chart];
var items:Array<DataSourceItem> = []
items.append(DataSourceItem(name: "John", value: 50, group:"A"))
items.append(DataSourceItem(name: "Abby", value: 33, group:"A"))
items.append(DataSourceItem(name: "Paula", value: 33, group:"A"))

items.append(DataSourceItem(name: "John", value: 42, group:"B"))
items.append(DataSourceItem(name: "Abby", value: 28, group:"B"))
items.append(DataSourceItem(name: "Paula", value: 25, group:"B"))

dataSource.displayKey = "name"
dataSource.valueKey = "value"
dataSource.itemSource = items

let chart = TKChart(frame:self.view.bounds)
chart.autoresizingMask = UIViewAutoresizing(rawValue: UIViewAutoresizing.flexibleWidth.rawValue | UIViewAutoresizing.flexibleHeight.rawValue)
chart.dataSource = dataSource
self.view.addSubview(chart)
NSMutableArray items = new NSMutableArray ();
items.Add (new DSItem () { Name = "John", Value = 50, Group = "A" });
items.Add (new DSItem () { Name = "Abby", Value = 33, Group = "A" });
items.Add (new DSItem () { Name = "Paula", Value = 33, Group = "A" });

items.Add (new DSItem () { Name = "John", Value = 42, Group = "B" });
items.Add (new DSItem () { Name = "Abby", Value = 28, Group = "B" });
items.Add (new DSItem () { Name = "Paula", Value = 25, Group = "B" });

this.dataSource.DisplayKey = "Name";
this.dataSource.ValueKey = "Value";
this.dataSource.ItemSource = items;

var chart = new TKChart(this.View.Bounds);
chart.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
chart.DataSource = this.dataSource;
this.View.AddSubview(chart);

In order to present different series the data should be grouped. When this is done the createSeries method can be used to customize the series that should be created:

[self.dataSource groupWithKey:@"group"];

[self.dataSource.settings.chart createSeries:^TKChartSeries *(TKDataSourceGroup *group) {
    TKChartColumnSeries *series = [TKChartColumnSeries new];
    return series;
}];
dataSource.group(withKey: "group")

dataSource.settings.chart.createSeries({ (group) -> TKChartSeries! in
    let series = TKChartColumnSeries()
    return series
})
this.dataSource.GroupWithKey ("Group");

this.dataSource.Settings.Chart.CreateSeries ((TKDataSourceGroup group) => {
    var series = new TKChartColumnSeries();
    return series;
});

TKCalendar

TKDataSource is able to represent your data as calendar events. In this scenario you should set the startDateKey and endDateKey properties:

self.dataSource.displayKey = @"name";
self.dataSource.settings.calendar.startDateKey = @"startDate";
self.dataSource.settings.calendar.endDateKey = @"endDate";
self.dataSource.settings.calendar.defaultEventColor = [UIColor redColor];
self.dataSource.itemSource = items;

TKCalendar *calendar = [[TKCalendar alloc] initWithFrame:CGRectInset(self.view.bounds, 0, 30)];
calendar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
calendar.dataSource = self.dataSource;
[self.view addSubview:calendar];
dataSource.displayKey = "name"
dataSource.settings.calendar.startDateKey = "startDate"
dataSource.settings.calendar.endDateKey = "endDate"
dataSource.settings.calendar.defaultEventColor = UIColor.red
dataSource.itemSource = items

let calendar = TKCalendar(frame:self.view.bounds.insetBy(dx: 0, dy: 30))
calendar.autoresizingMask = UIViewAutoresizing(rawValue: UIViewAutoresizing.flexibleWidth.rawValue | UIViewAutoresizing.flexibleHeight.rawValue)
calendar.dataSource = dataSource
self.view.addSubview(calendar)
this.dataSource.DisplayKey = "Name";
this.dataSource.Settings.Calendar.StartDateKey = "StartDate";
this.dataSource.Settings.Calendar.EndDateKey = "EndDate";
this.dataSource.Settings.Calendar.DefaultEventColor = UIColor.Red;
this.dataSource.ItemSource = items;

var calendar = new TKCalendar (this.View.Bounds);
calendar.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
this.dataSource.SetDataSourceFor (calendar);
this.View.AddSubview (calendar);