tl;dr

I’m going to make a simple tableview with a custom cell type, use the new UIVisualEffectView from iOS 8 and then show off a bit of the power of Swift. It should take you about ~30 minutes to go through all the steps. Enjoy!

full code

if you are looking for a simpler tutorial for tableviews check out this one

Create a new project

Open Xcode 6, create a new “Single Page Application” and select Swift as the programming language.

Add a table view property

Open the ViewController.swift class and add a new tableview instance variable below the class declaration. Add the @IBOutlet Interface Builder declaration attribute to expose the tableView property.

“Interface Builder attributes are declaration attributes used by Interface Builder to synchronize with Xcode. Swift provides the following Interface Builder attributes: IBAction, IBDesignable, IBInspectable, and IBOutlet. These attributes are conceptually the same as their Objective-C counterparts.” Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/ro/jEUH0.l

class ViewController : UIViewController { @ IBOutlet var tableView : UITableView! ... }

Conform to the UITableViewDelegate and UITableViewDataSource protocols

To conform to the UITableViewDelegate and UITableViewDataSource protocol, just add them separated by commas after UIViewController in the class declaration. (more about protocols in Apple’s Docs)

class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... }

Add a table view in your view controller interface

Open Main.storyboard and drag a UITableView from the library (in the lower right corner) into the ViewController view.

Connect the Interface Builder outlets

Connect the dataSource , delegate and tableView outlets in interface builder. Just right click on the table view and then connect them.

Create the custom cell class

Create a new class above your ViewController code. Your custom cell class should inherit from UITableViewCell ! Add outlets for the backgroundImage and titleLabel .

class CustomTableViewCell : UITableViewCell { @ IBOutlet var backgroundImage : UIImageView @ IBOutlet var titleLabel : UILabel } class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... }

Create the custom cell interface

Right click on your applications directory and select new file.

Select User Interface and then the Empty template. Select iPhone and name it CustomTableViewCell .

Open CustomTableViewCell.xib and add a UITableViewCell in it from the component library. Select the Table View Cell and change it’s class to CustomTableViewCell

After that the table view cell should change its name to Custom Table View Cell , the backgroundImage and titleLabel outlets should be visible now.

Add an image view and a label in the cell.

Resize the cell to 320 x 320 using the size inspector. And set the row height to 320

Connect the cell outlets to the CustomTableCellViewCell . Notice that custom view bind outlets to the view object and custom view controllers bind them to the File's Owner

Add the loadItem method

In a real life application you usualy have more than one type of cell in a table view (or collection view). By keeping the initialization logic in the cell we can avoid code duplication or spaghetti code.

This cell displays an image stored on the device and has a string title. All we need to do during the cell initialization is to set the image and the tile.

class CustomTableViewCell : UITableViewCell { @ IBOutlet var backgroundImage : UIImageView! @ IBOutlet var titleLabel : UILabel! func loadItem ( # title : String , image : String ) { backgroundImage . image = UIImage ( named : image ) titleLabel . text = title } }

Obs: For you Objective-C folks in Swift you do not need to call properties using the self keyword! Use the self keyword only when you have a parameter named like your property, so that the compiler can understand your code.

Note: This function uses a shorthand external parameter name. If the method declaration was func loadItem(title: String, image: String) (without # symbol) to call it we would have to write cell.loadItem("We❤Swift", image: "someimage.jpeg") . Instead, with the # symbol, to call loadItem we would write cell.loadItem(title: "We❤Swift", image: "someimage.jpeg") . I think the second method is clearer.

From Apple’s Docs:

Shorthand External Parameter Names If you want to provide an external parameter name for a function parameter, and the local parameter name is already an appropriate name to use, you do not need to write the same name twice for that parameter. Instead, write the name once, and prefix the name with a hash symbol (#). This tells Swift to use that name as both the local parameter name and the external parameter name.

Add some data to display

Download the swifts and add them in your project. Unarchive the zip file and drag the files in you Xcode Navigator. Make sure to check Copy items if needed .

For each custom cell we need a title and a image name, we are going to store them in an Array of Tuples.

From Apple’s Docs:

A tuple pattern is a comma-separated list of zero or more patterns, enclosed in parentheses. Tuple patterns match values of corresponding tuple types. … A pattern represents the structure of a single value or a composite value. For example, the structure of a tuple (1, 2) is a comma-separated list of two elements. Because patterns represent the structure of a value rather than any one particular value, you can match them with a variety of values. For instance, the pattern (x, y) matches the tuple (1, 2) and any other two-element tuple. In addition matching a pattern with a value, you can extract part or all of a composite value and bind each part to a constant or variable name. In Swift, patterns occur in variable and constant declarations (on their left-hand side), in for-in statements, and in switch statements (in their case labels). Although any pattern can occur in the case labels of a switch statement, in the other contexts, only wildcard patterns, identifier patterns, and patterns containing those two patterns can occur. …

The first value will represent the title and the second one the imageName .

class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... var items : [ ( String , String )] = [ ( "❤" , "swift 1.jpeg" ), ( "We" , "swift 2.jpeg" ), ( "❤" , "swift 3.jpeg" ), ( "Swift" , "swift 4.jpeg" ), ( "❤" , "swift 5.jpeg" ) ] }

Set the number of rows

Implement tableView(_:numberOfRowsInSection:) and return the number of items .

class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... func tableView ( tableView : UITableView , numberOfRowsInSection section : Int ) -> Int { return items . count ; } }

Note: In iOS the number of sections is 1 by default, so we do not need to set a value for that. In case you want to create a table with multiple sections just implement the numberOfSectionsInTableView(_:) method.

Register the Nib

Load the CustomTableViewCell interface file into a UINib object and then tell the table view to use it for the customCell reuse identifier.

class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... override func viewDidLoad () { ... var nib = UINib ( nibName : "CustomTableViewCell" , bundle : nil ) tableView . registerNib ( nib , forCellReuseIdentifier : "customCell" ) } ... }

Create the cell

Now when you will ask for a cell from the table view with the reuse identifier customCell , the tableView will look for any unused cells with that reuse identifier or just create one using the CustomTableViewCell nib.

The tableView will call the tableView(_:cellForRowAtIndexPath:) method on the dataSource whenever it need a specific cell. The location of the cell is stored in an NSIndexPath that has a row and section property.

To create a cell all we need to do is ask for one using the dequeueReusableCellWithIdentifier(_:) method. After we have a cell we need to load the title and image and then return it.

class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... func tableView ( tableView : UITableView , cellForRowAtIndexPath indexPath : NSIndexPath ) -> UITableViewCell { var cell : CustomTableViewCell = self . tableView . dequeueReusableCellWithIdentifier ( "customCell" ) as CustomTableViewCell // this is how you extract values from a tuple var ( title , image ) = items [ indexPath . row ] cell . loadItem ( title : title , image : image ) return cell } }

Handle Table Selection

When a cell is selected the table view will call the tableView(_:didSelectRowAtIndexPath:) method on the delegate . To handle table view selection all you need to do is implement that method.

class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... func tableView ( tableView : UITableView , didSelectRowAtIndexPath indexPath : NSIndexPath ) { tableView . deselectRowAtIndexPath ( indexPath , animated : true ) println ( "You selected cell #\(indexPath.row)!" ) } }

If you run the project it should look like this:

Add a blur view

In iOS 8 we now have a easy way of recreating the blur effect used throughout the system. UIVisualEffectView is a subclass of UIView that provides a simple abstraction over complex visual effects. UIKit has two implemented effects UIBlurEffect and UIVibrancyEffect .

Let’s create a UIVisualEffectView and add it to the main view.

class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... override func viewDidLoad () { ... addEffect () ... } func addEffect () { var effect = UIBlurEffect ( style : UIBlurEffectStyle . Light ) var effectView = UIVisualEffectView ( effect : effect ) effectView . frame = CGRectMake ( 0 , 0 , 320 , 100 ) view . addSubview ( effectView ) } ... }

There are 3 type of blur effects. If we send the effect and offset as a parameters to the addEffect method we can reuse the code and see all three blur effects at once.

class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... override func viewDidLoad () { super . viewDidLoad () addEffect ( UIBlurEffect ( style : UIBlurEffectStyle . Light ), offset : 0 ) addEffect ( UIBlurEffect ( style : UIBlurEffectStyle . Dark ), offset : 50 ) addEffect ( UIBlurEffect ( style : UIBlurEffectStyle . ExtraLight ), offset : 100 ) var nib = UINib ( nibName : "CustomTableViewCell" , bundle : nil ) tableView . registerNib ( nib , forCellReuseIdentifier : "customCell" ) } func addEffect ( effect : UIVisualEffect , offset : CGFloat ) { var effectView = UIVisualEffectView ( effect : effect ) effectView . frame = CGRectMake ( 0 , offset , 320 , 50 ) view . addSubview ( effectView ) } ... }

Extend the Array class

In ruby the Array class two nifty methods each and eachWithIndex . The each method takes a function as a parameter and calls it with each element of the array in order, eachWithIndex takes a function as a parameter and calls it with the tuple (element, index) for each element.

We can extend a class using the extension keyword. The implementation of each and eachWithIndex in Swift would look like this:

extension Array { func each ( callback : T -> ()) { for item in self { callback ( item ) } } func eachWithIndex ( callback : ( T , Int ) -> ()) { var index = 0 for item in self { callback ( item , index ) index += 1 } } }

Putting it all together

Now we have 3 method calls that look pretty similar. Two things change: the style and offset.

override func viewDidLoad () { ... addEffect ( UIBlurEffect ( style : UIBlurEffectStyle . Light ), offset : 0 ) addEffect ( UIBlurEffect ( style : UIBlurEffectStyle . Dark ), offset : 50 ) addEffect ( UIBlurEffect ( style : UIBlurEffectStyle . ExtraLight ), offset : 100 ) ... }

We can rewrite this code using eachWithIndex :

class ViewController : UIViewController , UITableViewDataSource , UITableViewDelegate { ... override func viewDidLoad () { ... addEffects () ... } func addEffects () { [ UIBlurEffect ( style : UIBlurEffectStyle . Light ), UIBlurEffect ( style : UIBlurEffectStyle . Dark ), UIBlurEffect ( style : UIBlurEffectStyle . ExtraLight ) ]. eachWithIndex { ( effect , index ) in var effectView = UIVisualEffectView ( effect : effect ) effectView . frame = CGRectMake ( 0 , CGFloat ( 50 * index ), 320 , 50 ) self . view . addSubview ( effectView ) } } ... }

We can do one thing here and use the map function:

func addEffects () { [ UIBlurEffectStyle . Light , UIBlurEffectStyle . Dark , UIBlurEffectStyle . ExtraLight ]. map { UIBlurEffect ( style : $0 ) }. eachWithIndex { ( effect , index ) in var effectView = UIVisualEffectView ( effect : effect ) effectView . frame = CGRectMake ( 0 , CGFloat ( 50 * index ), 320 , 50 ) self . view . addSubview ( effectView ) } }

This is called method chaining. You can read more about closures and higher order functions in Silviu’s post here.

You can get the code here.

Challenges:

use more than one type of cell in the same table view

implement more of the dataSource and delegate methods and see what you can do with them. You could start with numberOfSectionsInTableView(_:) , tableView(_:titleForHeaderInSection:) , sectionIndexTitlesForTableView(_:) , tableView(_:heightForRowAtIndexPath:)

, , , make a Contact Book App (hint: finish the second challenge first)

I’ve been using similar patterns in Objective-C but they were always a pain because of the syntax and limitations of blocks. I Hope you got a bit excited about functional programming and Swift.

If you found this useful, please take a moment and share it with your friends