iOS Programming Recipe 35 – Implementing The Common Views: UIPickerView, UITableView, and UICollectionView

Over the past couple years I’ve noticed some patterns when it comes to noobs and the more useful iOS UI elements. Whether your dealing with UICollectionViews, UITableViews, or UIPickerViews, the pattern is essentially the same. And a lot of beginners, don’t quite grasp how similar these views really are. This recipe will focus a little less on the nitty gritty of doing a single one of these view types, and more on how the pattern works for them all. As a bonus, this will be the first tutorial I’ll be doing with Swift. As far as the UI elements go, it’s all the same philosophy, just a slightly different syntax.

Assumptions

A Bit of Protocol Background

All of these UI elements use datasource and delegate protocols. And really, understanding these is the hardest part about using these types of controls. So first, let’s explain this in a little more depth.

Protocols

Say we have two classes. A car class and a person class. In order for the person to operate the car class he will need to carry out specific instructions for operating a car. So the car class might have a method for moving the gear shifter into drive and another method for controlling the gas pedal. A car can’t control these methods itself (normally) so it’s up to the the person to carry out the car driving protocol. Two important types of protocols are Delegates and DataSources.

Delegates

Moving forward with the protocol example, it’s important that you change your car oil every 3000 or so miles (or 4828.03KM for you foreign folks). In the past this was a manual process, the person would occasionally look at the odometer and do some math to figure out if an oil change is needed. New cars however, just let you know when you’ve reached 3000 miles. This is equivelent to a delegate protocol. In this case, the person enters an agreement with the car that says “when the car lets me know that 3000 miles has been reached, I will change the oil.” A person would then probably have a changeTheOil method.

To reiterate, Instead of the person having an instruction that says checkTheCarOdometer and repeating it a bunch, he would now have an instruction that says carOilReadyToChange which would be called by the car and kick of the changeTheOil process in the person class.

DataSources

A Data source protocol is just a delegate, but it’s a little more specific. It’s a delegate protocol that informs whoever subscribes to the delegate that data is needed in order to carry out a process. As a psuedo real world example: It would be like your car alerting you that you first need to put the car into drive before the gas pedal will work.

The Basic Pattern

For UITableViews, UICollectionViews, UIPickerViews, and many others, there is a basic pattern that usually needs to be followed.

  1. First Subscribe to the delegate and dataSource protocols.
  2. Implement the datasource methods.
  3. Implement the delegate methods.
  4. Insure that you properly set the delegate and datasource receiver.

TL;DR

Go get the source code here: NSCookbook Recipe 35

Setting up Your Project

Before we jump into this, we’ll first set this project up with everything we’ll need for creating 3 different types of common UIViews. So start off by creating a single view application and drag a tab bar controller onto the storyboard. The tab bar controller will be connected to two view controllers. First control-click and drag from the tab bar controller to the view controller that was there when you created the project. Select “View Controllers” under “Relationship Segue” to make this connection. You’ll also want to click and drag the starting error on the first view controller and move it to the tab bar controller. If you’ve done everything correctly your storyboard should look like Figure 1.

Figure1
Figure 1: The Complete Storyboard

Next You’ll want to create two more view controllers by pushing the “+” in the bottom left hand side of the Project Navigator and select a “Cocoa Touch Class” and press next. Make both classes a Subclass of “UIViewController”.

You’ll Name one “TableViewController” and one “CollectionViewController”. If you wish you can rename the single view controller that existed when you created the project to “PickerViewController” Be warned that if you do this, you’ll need to also change the name in the .h and .m files. It’s up to you whether you want to use swift of Objective-C for the class types. We’ll be covering both.

Once you have created the new view controllers, you’ll need to connect them properly in the storyboard. Do this by first selecting one of the view controllers and then fill in the custom class from the identity inspector on the right as shown in Figure Two.

Figure2
Figure 1: Assigning a class to a view controller in the storyboard

Note: You may have noticed that there are special controller classes for both a UITableView and a UICollectionView, but these classes do some of the work for you and we want make sure you understand these types of views completely.

Also of Note: You’ll notice these view controllers don’t look like an iPad or an iPhone. that’s because we’re using size classes, A default in XCode 6. These allow for laying out a view on all size screens.

The last thing you’ll want to do is drag a UIPickerView to the first ViewController, a UITableView to the second one, and UICollectionView to the final ViewController. When you’re done your storyboard should resemble Figure 3.

Figure3
Figure3: Adding the appropriate view controllers.

The last step is adding a bit of autolayout constraints to each view. select the view inside each view controller and add TOP,LEFT,RIGHT, and LEFT margin constraints with a 0 value. You can easily do this with the pin menu in the bottom right hand corner of the storyboard as shown in Figure 4. Do this for every view controller and it’s view.

Note: The Picker view won’t let you constrain it full screen, so just center the picker and constrain it using the 4 constraints show. Don’t forget to push the “Add Constraints” button.

Figure4

Figure4: Adding constraints to the individual views.

The last step we’ll need to do is embed each view in a navigation controller. From the storyboard simply select one of the views and choose Editor -> Embed In -> Navigation Controller from the file menu. Do this for each one of your View Controllers. This step lets us take advantage of a Navigation Controllers’ navigation stack. If’ you’ve done this correctly, your storyboard should now look like Figure 5**.

Figure5
Figure 5: The Complete Storybaord

Adding Assets

Alright, Now we’ll need to add some image assets for our Collection View. It doesn’t matter what images you use, just simply find the images you want and drag and drop them into the Project Navigator on the left hand side of the XCode screen. Alternatively you could make an image asset for each image. To do this Simply open Images.xcassets and create new image assets by pushing the “+” in the bottom left hand corner. From there you can drag and drop in the images of the appropriate @1x and @2x sizes. Make sure you give your images a unique name. For this tutorial I went out and collected some free stock photography I found of at Unsplash.com. Personally, I like to drag them to into the supporting files folder.

Important: Make sure you check the “Copy items if needed” checkbox.

Subscribing to the Delegate and Data Source Protocols

To start off, we’re gonna grab the low hanging fruit. First start by opening your PickerViewController.h file and adding the code in Code Chunk 1 at the end of the @interface line. With swift this is a little different, you simply add it to the end of the class method.

Objective-C


1
@interface PickerViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource>

Swift


1
class PickerViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource{

Code Chunk 1: Declaring UIPickerView Delegate and UIPickerView DataSource

Now simply repeat for the other two classes shown in Code Chunk 2 & 3.

Objective-c


1
@interface PickerViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

Swift


1
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{

Code Chunk 2: Declaring UITableView Delegate and UITableView DataSource

Objective-c


1
@interface PickerViewController : UIViewController <UICollectionViewDelegate, UICollectionViewDataSource>

Swift


class TableViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{

Code Chunk 3: Declaring UICollectionView Delegate and UICollectionView DataSource

See what we’ve done here is simply entered a contract with the UIPickerView,UITableView,and UICollectionView classes that basically says. “Hey, If there is a required protocol, then you must implement it or you will crash. If there is an optional protocol method,then it’s up to you to implement and I’ll use it if needed”.

So, if you were to try and run right now, you would most definitely crash.

Implementing the Data Source Methods

You can easily figure out which methods are required and optional inside a protocol by CMD-Clicking that method name

Figure6
Figure 6: Finding the Required And Optional Methods.

Many of these views have optional DataSources, but they are not of much use if you don’t use them. So You’ll just have to take my word for it and implement the following ones for each class. Simply add the following code chunks (Code Chunk 4 -6) inside of the class implementation (betweeen @implementatin and @end for Objective-c and between the class braces for swift).

UIPIckerView

Objective-C


1
2
3
4
5
6
7
8
9
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{

}

// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{

}

Swift


1
2
3
4
5
6
7
8
9
// returns the number of 'columns' to display.
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int{

}

// returns the # of rows in each component..
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{

}

UITableView

Code Chunk 5: UITableView Required DataSource Methods

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
}



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
}
</code  lang="haskell" line_numbers="true"></pre>

<p><em>Swift</em></p>

<pre><code>func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{

}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{

}

UICollectionView

Code Chunk 5: UICollectionView Required DataSource Methods

Objective-C


1
2
3
4
5
6
7
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
}



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
}

Swift


1
2
3
4
5
6
7
8
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{

}


func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -&gt; UICollectionViewCell{

}

At this point your compiler is probably pretty upset with you because all of these methods require return values. Now we’re going to fill these in one by one. We’ll do this by providing arrays of data that we’ll simply hardcode into the viewDidLoad method. If you wish to populate these with data from the web, You’ll need to read one of my past articles and populate the array’s accordingly when they load.

Populating the UIPickerView DataSource Methods

The First thing we’re going to need is some data to populate our datasource method with. Start by populating creating a new pickerViewData array property and populating it in the viewDidLoad method as shown in Code Chunk 7. Note we’re using literals in the Objective-C implementation to populate the array with a bunch of strings.

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import "PickerViewController.h"

@interface PickerViewController ()

@property (strong, nonatomic) NSArray *pickerViewData;

@end

@implementation PickerViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.pickerViewData = @[@"Row 1",@"Row 2",@"Row 3",@"Row 4",@"Row 5",@"Row 6"];
}
//...

Swift

import UIKit


1
2
3
4
5
6
7
8
9
10
11
class PickerViewControllerSwift: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    var pickerViewData = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        pickerViewData = ["Row 1","Row 2","Row 3","Row 4","Row 5","Row 6"]

        // Do any additional setup after loading the view.
    }

Code Chunk 7: Creating Data to populate the Picker View with.

Now We’ll fill in the two required Data Source methods for a picker view. The first method numberOfComponentsInPickerView, will simply return 1 as we only need one column. The next method, pickerView:numberOfRowsInComponent: , will simply be 6 or the number of items in the array we just made. To make this more dynamic we’ll return the count of items in our array. Code Chunk 8 shows this code.

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
14
//...
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{

    return 1;

}

// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{

    return self.pickerViewData.count;
}
//...

Swift


1
2
3
4
5
6
7
8
9
// returns the number of 'columns' to display.
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int{
    return 1
}

// returns the # of rows in each component..
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
    return pickerViewData.count
}

Code Chunk 8: Filling in the DataSource methods

Alright That is all we need to do for the Data Source method implementation of a Picker View. Now lets move on to the Table View.

Populating the UITableView DataSource Methods

Once Again, We’re going to need data. For the Table View we’ll simply use the same exact Array. So add the property and modify the viewDidLoad method as shown in Code Chunk 9.

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface TableViewController ()

@property (strong, nonatomic) NSArray *tableViewData;

@end

@implementation TableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.tableViewData = @[@"Row 1",@"Row 2",@"Row 3",@"Row 4",@"Row 5",@"Row 6"];
}
//...

Swift


1
2
3
4
5
6
7
8
9
10
11
import UIKit

class TableViewViewController: UIViewController, UITableViewDelegate , UITableViewDataSource{

    var tableViewData = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        tableViewData = ["Row 1","Row 2","Row 3","Row 4","Row 5","Row 6"]
    }

Code Chunk 9: Creating Data to populate the Table View with.

The Two Data Source methods are a little different for the table view. With the table view you actual define the actual data that will populate the cell. I personally feel the Picker view should also populate the row as part of a data source method, but we’re stuck with it the way it is.

With the tableView you are required to define the number of rows, which we do by getting the count of our array, and the individual cell data. The method(function) for defining the cell uses some boiler plate code that’s sort of hard to remember, but it’s almost always there. We’ll set the cell text to the value at the current index of the table view. So Basically the tableView: cellForRowAtIndexPath: makes 6 cells and ask us what data is in each cell one at a time. The swift and Objective-c example of this is shown in Code Chunk 10.

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

    return self.tableViewData.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    //start boilerplate
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    //end boilerplate

    cell.textLabel.text = [self.tableViewData objectAtIndex:indexPath.row];

    return cell;
}
//...

Swift


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
    return tableViewData.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
    var cell:UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell

    if cell == nil {
        cell = UITableViewCell(style:.Default, reuseIdentifier: "Cell")
    }

    cell!.textLabel.text = tableViewData[indexPath.row]

    return cell as UITableViewCell!
}

Code Chunk 10: Filling in the DataSource methods

Ok. Now it’s time to move to the Collection View. This one is probably the most complicated, but the concepts are all teh same, there’s just a couple more steps.

Populating the UICollectionView DataSource Methods

I bet you know what were gonna do next? That’s right, more data source! For the Collection View we’ll need to make an array of images rather than just simple text. So add the image array property and modify the viewDidLoad method as shown in Code Chunk 11. I named my images “smallImg_”, but you can name them whatever you want. Also, if you use .png’s you don’t need to add the .png extension. XCode will find them automatically.

Since this is a Collection View and we’re making cells programmatically, we also need to register a class for Reuse Identifier in the viewDidLoad method.

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import "CollectionViewController.h"

@interface CollectionViewController ()

@property (strong, nonatomic) NSArray *collectionViewImagesData;

@end

@implementation CollectionViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

        [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];
    self.collectionViewImagesData = @[@"smallImg1.jpg",@"smallImg2.jpg",@"smallImg3.jpg",@"smallImg4.jpg",@"smallImg5.jpg",@"smallImg6.jpg",@"smallImg7.jpg",@"smallImg8.jpg",@"smallImg9.jpg",@"smallImg10.jpg",@"smallImg11.jpg",@"smallImg12.jpg"];

}
//...

Swift


1
2
3
4
5
6
7
8
9
10
11
12
13
import UIKit

class CollectionViewViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

    var collectionViewImagesData = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionViewImagesData = ["smallImg1.jpg","smallImg2.jpg","smallImg3.jpg","smallImg4.jpg","smallImg5.jpg","smallImg6.jpg","smallImg7.jpg","smallImg8.jpg","smallImg9.jpg","smallImg10.jpg","smallImg11.jpg","smallImg12.jpg"]

        // Do any additional setup after loading the view.
    }

Code Chunk 11: Creating Data to populate the Collection View with.

The Collection view data source is pretty similar to the table view cell. Later the layout will be a bit different, but that doesn’t effect the required data source methods too much. Again we’ll be populating the number of items as well as defining the individual cells. Code Chunk 12 shows this code. Notice, that were now defining “items” instead of “rows”. It’s probably obvious, but that’s because a Collection View has both columns and rows.

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//...
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{

    return self.collectionViewImagesData.count;
}

// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];


    UIImage *tmpImage = [UIImage imageNamed:[self.collectionViewImagesData objectAtIndex:indexPath.item]];
    UIImageView *tmpImageView = [[UIImageView alloc] initWithImage:tmpImage];
    tmpImageView.frame = CGRectMake(0, 0, 100, 100);
    [cell addSubview:tmpImageView];
    cell.clipsToBounds = YES;

    return cell;

}
//...

Swift


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -&gt; Int{
        return collectionViewImagesData.count
    }


    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell{

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as UICollectionViewCell

        let tmpImage = UIImage(named: collectionViewImagesData[indexPath.item])
        let tmpImageView = UIImageView(image:tmpImage)
        tmpImageView.frame = CGRectMake(0,0,100,100)
        cell.addSubview(tmpImageView)
        return cell

    }

Code Chunk 12: Filling in the DataSource methods

Alright! Now that our data sources are complete, Let’s move onto to the delegate methods. One thing before we move on though. So far we’ve been defining our cells programmatically. If you would rather create a cell on the storyboard and link it using an IBOutlet you can do that. if you choose to use this method, then you need to remove the if statements that check if a cell==nil and creates a new cell. I’ve done tutorials in the past that how to create these the Outlet way in Objective-C.

Populating the UIPickerView Delegate Methods

With the picker view there are no required delegates, but if you don’t implement at least one of them, there will be nothing to see in the Picker View. As mentioned before, It’s kind of odd to me that this is the only UIView here that doesn’t populate the data in one of the datasource methods. Either way, We’ll be implementing the optional, but very useful, pickerView: titleForRow: forComponent: method to populate the Picker View. We’ll also be implementing the pickerView: didSelectRow: inComponent: method to define the behavior of a selected item.

In the pickerView: titleForRow: forComponent, we’ll simply just set the title to the value in the array for that row.

The other delegate method, is a little more complicated. We’re simply just creating a new view controller and setting it’s title to the row that is selected and pushing it onto the navigation stack. We won’t go into populating the detail view controller, but you could easily create a custom class for the view controller and set your own properties or pass a model into it.

Code Chunk 13: Shows the full implementation of the Picker View Delegate methods.

Note: You could alternatively use the storyboard to create a segue from the picker view to a new view controller and define the data in the prepareForSegue method. This tutorial will be handling these types of navigation programmatically, As I’ve shown it done the other way in the past.

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//...
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{

    return [self.pickerViewData objectAtIndex:row];

}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{

    UIViewController *tmpViewController = [[UIViewController alloc] init];
    tmpViewController.title = [self.pickerViewData objectAtIndex:row];
    tmpViewController.view.backgroundColor = [UIColor blackColor];

    [self.navigationController pushViewController:tmpViewController animated:YES];

}
//...

Swift


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String!{

    return pickerViewData[row]

}

func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){

    let tmpViewController = UIViewController()
    tmpViewController.title = pickerViewData[row]
    tmpViewController.view.backgroundColor = UIColor.blackColor()

    self.navigationController?.pushViewController(tmpViewController, animated: true)

}

Code Chunk 13: Filling in the Delegate methods

Populating the UITableView Delegate Methods

The Table View delegate is even easier because we’ve already populated the table view with data. Here we will simply define the behavior for selecting a Table View cell. Which, Is almost identical to the way we handle selecting a row in the Picker View. Code Chunk 14 shows the implementation of this single optional Delegate method.

Objective-C


1
2
3
4
5
6
7
8
9
10
11
//...
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{

    UIViewController *tmpViewController = [[UIViewController alloc] init];
    tmpViewController.title = [self.tableViewData objectAtIndex:indexPath.row];
    tmpViewController.view.backgroundColor = [UIColor blackColor];

    [self.navigationController pushViewController:tmpViewController animated:YES];

}
//...

Swift


1
2
3
4
5
6
7
8
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){

    let tmpViewController = UIViewController()
    tmpViewController.title = tableViewData[indexPath.row]
    tmpViewController.view.backgroundColor = UIColor.blackColor()

    self.navigationController?.pushViewController(tmpViewController, animated: true)
}

Code Chunk 14: Filling in the Delegate method

Populating the UICollectionView Delegate Methods

The Collection View has a little more going on than the Table View delegate. He’re we’ll be implementing to two methods. One delegate method will set the size of the Cell, and the other will present a new view controller with an image in the top left corner and the title as the image name. Code Chunk 15 Shows the implementation of these two methods.

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//...
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(100, 100);
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{

    UIViewController *tmpViewController = [[UIViewController alloc] init];
    UIImage *tmpImage = [UIImage imageNamed:[self.collectionViewImagesData objectAtIndex:indexPath.item]];
    UIImageView *tmpImageView = [[UIImageView alloc] initWithImage:tmpImage];
    tmpImageView.frame = CGRectMake(10, 84, 100, 100);
    [tmpViewController.view addSubview:tmpImageView];
    tmpViewController.title = [self.collectionViewImagesData objectAtIndex:indexPath.item];
    tmpViewController.view.backgroundColor = [UIColor blackColor];


    [self.navigationController pushViewController:tmpViewController animated:YES];
}
//...

Swift


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func collectionView(collectionView: UICollectionView,
    layout collectionViewLayout: UICollectionViewLayout,
    sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize{

        return CGSizeMake(100, 100)
}

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath){

    let tmpViewController = UIViewController()
    let tmpImage = UIImage(named: collectionViewImagesData[indexPath.item])
    let tmpImageView = UIImageView(image:tmpImage)
    tmpImageView.frame = CGRectMake(10,84,100,100)
    tmpViewController.view.addSubview(tmpImageView)
    tmpViewController.title = collectionViewImagesData[indexPath.item]
    tmpViewController.view.backgroundColor = UIColor.blackColor()

    self.navigationController?.pushViewController(tmpViewController, animated: true)
}

Code Chunk 15: Filling in the Delegate methods

Setting the Delegate and DataSource Recievers

This last part is the easiest part. There are two ways to do it. You can either create a property for each of the elements, and then set the delegate to “self” in the viewDidLoad. Or you can use interface builder to set them with one step. I’ll show you both methods here.

The Interface Builder Method

Now this method can be repeated on all three of these views, so we won’t show you all three. The process is identical though, so you should catch on. Figure 7 shows the process of connecting the delegate and datasource recievers.

Figure7
Figure 7: Connecting the Delegate and DataSource

The Property – Code Method

Ok the next method is slightly more involved but a bit more explicit. First you need to make a property for each of your views, EG tableView, collectionView, and pickerView. Figure 8 Shows the creation of a property. You can repeat this step for all three view types. If you’re doing it in Objective-C make the connection in the @interface secion of either the .h or .m file.

Figure8
Figure 8: Creating a Property Outlet for the view

Ok, now we simply assign each Delegate and DataSource to self. Code Chunk 16-18 Shows this bit of code for the Picker View.

UIPickerView

Objective-C


1
2
3
4
5
6
7
8
9
//...
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.pickerView.dataSource = self;
    self.pickerView.delegate = self;
    self.pickerViewData = @[@"Row 1",@"Row 2",@"Row 3",@"Row 4",@"Row 5",@"Row 6"];
}
//...

Swift


1
2
3
4
5
6
7
8
9
10
override func viewDidLoad() {
    super.viewDidLoad()

    pickerView.delegate = self
    pickerView.dataSource = self

    pickerViewData = ["Row 1","Row 2","Row 3","Row 4","Row 5","Row 6"]

    // Do any additional setup after loading the view.
}

Code Chunk 16: Assigning Picker View Delegate and DataSource to Self

UITableView

Objective-C


1
2
3
4
5
6
7
8
9
//...
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.tableView.dataSource = self;
    self.tableView.delegate = self;
    self.tableViewData = @[@"Row 1",@"Row 2",@"Row 3",@"Row 4",@"Row 5",@"Row 6"];
}
//...

Swift


1
2
3
4
5
6
7
8
override func viewDidLoad() {
    super.viewDidLoad()

    tableView.dataSource = self
    tableView.delegate = self

    tableViewData = ["Row 1","Row 2","Row 3","Row 4","Row 5","Row 6"]
}

Code Chunk 17: Assigning Table View Delegate and DataSource to Self

UICollectionView

Objective-C


1
2
3
4
5
6
7
8
9
10
11
12
13
//...
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    self.collectionView.dataSource = self;
    self.collectionView.delegate = self;

    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];
    self.collectionViewImagesData = @[@"smallImg1.jpg",@"smallImg2.jpg",@"smallImg3.jpg",@"smallImg4.jpg",@"smallImg5.jpg",@"smallImg6.jpg",@"smallImg7.jpg",@"smallImg8.jpg",@"smallImg9.jpg",@"smallImg10.jpg",@"smallImg11.jpg",@"smallImg12.jpg"];

}
//...

Swift


1
2
3
4
5
6
7
8
9
10
override func viewDidLoad() {
    super.viewDidLoad()

    collectionView.delegate = self
    collectionView.dataSource = self
    collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
    collectionViewImagesData = ["smallImg1.jpg","smallImg2.jpg","smallImg3.jpg","smallImg4.jpg","smallImg5.jpg","smallImg6.jpg","smallImg7.jpg","smallImg8.jpg","smallImg9.jpg","smallImg10.jpg","smallImg11.jpg","smallImg12.jpg"]

    // Do any additional setup after loading the view.
}

Code Chunk 18: Assigning Table View Delegate and DataSource to Self

Wrapping Up

If you’ve done everything right, your app should flow like Figure 9.

finalApp
Figure 9: The Final App

The intention of this tutorial was not to go into each of the view types in a lot of depth, but rather get you accustomed to they way these types of views generally work. All of them have minor differences, but the process is generally the same. Getting familiar with the DataSource – Delegation pattern will help you out with many view types from both Apple and third party views. If you want more in depth tutorials on each of the view types discussed here, I have done these in the past with Objective-C. Between the new code shown here and those tutorials, It shouldn’t be too hard to figure out the swift versions.

This was a long post, so if you’re still here, thank you for reading! I hope it helps.

About Joe Hoffman

I am An Electrical Engineer that spends a lot of my off time doing web development and other programming. Currently I am trying to expand my knowledge with iOS and I find that writing tutorials helps me learn more thoroughly as well as provide some useful info to others.

Speak Your Mind

*

css.php
Privacy Policy