The first thing that came to mind when I saw Swift was TableViews. This is a step by step tutorial.
tl;dr
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.
@IBOutlet
var tableView: UITableView!
The @IBOutlet attribute makes the tableView
property visible in Interface Builder.
Conform to the TableView Delegate and DataSource protocols
To conform to the UITableViewDelegate
and UITableViewDataSource
protocol just add them seperated by colons afterUIViewController
in the class declaration. This was a bit confusing at first but the new protocol syntax is cleaner.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
...
}
After that we need to implement the tableView(_:numberOfRowsInSection:)
, tableView(_:cellForRowAtIndexPath:)
and
tableView(_:didSelectRowAtIndexPath:)
methods in the ViewController
class and leave them empty for now
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
...
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return UITableViewCell()
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
Add a table view in your view controller
Search in the componet library(It should be in the lower right corener) for Table View and then drag and drop it on the view.
Connect the Interface Builder Outlets
Connect the referencing, dataSource and delegate outlets.
We can connect the respective outlets using interface builder by right clicking on the table view and dragging each outlet on the view controller.
Register the cell class
In the viewDidLoad method call the registerClass(_:forCellReuseIdentifier:)
. Tip: to get the class use the class name followed by .self
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
...
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
...
}
Add some data to display
Add a property called items as an Array of Strings and set some values
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var items: [String] = ["We", "Heart", "Swift"]
...
}
Set the number of rows
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
...
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
...
}
Create the cell
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
...
}
Handle cell selection
Another new thing in Swift is String Interpolation that let’s you add arbitrary code inside a string.
Some examples:
var one = "\(1)" // "1"
var two = "\(1 + 1)" // "2"
var name = "Andrei Puni"
println("my name is \(name) and I heart Swift") // will print "my name is Andrei Puni and I heart Swift"
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
...
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
...
}
By now your ViewController class should look like this:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet
var tableView: UITableView
var items: [String] = ["We", "Heart", "Swift"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
}
If you haven’t skipped any steps you should be able to run the app and see something similar to:
In case you missed anything you could try our source code.
Feel free to ask questions or point out any mistakes.
For a more complete guide to UITableView checkout this tutorial.
Here you can find a tutorial about creating custom table view cells and more Swift awesomeness. And this is a complete guide to Object Oriented Programming in Swift.
If you found this useful please take a moment to share this with your friends.
What to learn the basics of computer programming? Read our book
So it is UITableViewCell().self! I have been struggling with it yesterday. I thought it would be UITableViewCell().class().
Are you sure? Doesn’t UITableViewCell() create a new table view cell and then calls self on it ?
UITableViewCell().self! doesn’t even compile: Operand of postfix ‘!’ should have optional type; type is ‘UITableViewCell’
This is the very first tutorial ever that I’m following, so thank you for that
I just want to point out that the part about the Outlets was giving me problems. I got it working when I linked three outlets: not only the Referencing Outlet, but also the two “regular” outlets (datasource and delegate).
Thanks for this!
I’m really happy this helped you
Plus 1 for this comment.
I’m having this error adding: UITableViewDelegate, UITableViewDataSource to class Viewcontroller:
Type ‘ViewController’ does not conform to protocol ‘UITableViewDataSource’
Did you implement both:
tableView(_:numberOfRowsInSection:) and
tableView(_:cellForRowAtIndexPath:) ?
Thanks!!
No need to declare Delegates if you set there property datasource and delegate in XIB . if You re confirm you are selected or not whether you selected you won’t write these delegates in code
Very nice tutorial. I was able to take it and learn a lot from it, now I’ve got a pretty nice example of my own that I’m building on
I like what you guys are up too. This kind of
clever work and reporting! Keep up the excellent works guys I’ve incorporated you guys to our blogroll.
I’m completely new to iOS and Swift so thanks for this tutorial, was very helpful. I am trying to implement this simple table view into a settings page that I created. I copied your view controller code entirely.
I was able to connect the table view Data Source and Delegate outlets to the view controller but *not* the referencing outlet. Any idea what I did wrong?
Screenshot: https://www.dropbox.com/s/t2rxanyzuu3n6j9/Screenshot%202014-06-07%2011.05.35.png
Did you add a table view IBOutlet ?
Yep, I have that in there. I’m not using a single page app like you did, I’m using a tabbed application. Not sure if that has anything to do with it…
Make sure your class definition and table view property are setup correctly
class SettingsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet var tableView: UITableView
When connecting the referencing outlet you will get a second popup -> http://imgur.com/a/wucLK
This will only work in iOS8!
How do you set the dataSource and delegate to you IBOutlet in iOS7?
Just wondering why you used “self.items” instead of just “items”?
I noticed that you could implicitly use self in swift – there are some case when you need to use self like in initializers.
Yes, generally In Initializers where you need to distinguish between the Instances’ property and the initializers argument. I was just wondering if there was any reason why you used it in this situation.
I didn’t read the question right the first time.
I used self.items because I didn’t know at the time that self is implicit.
Ah ok – Thanks. Great work with the Blog Anderi. Keep it up man!
Ah ok – Thanks. Great work with the Blog Anderi. Keep it up man!
Thanks a lot for this tutorial. Thanks to following this I was able to figure out why my existing table view wasn’t working. Now it’s behaving as expected. Cheers!
I tried to follow your tutorial exactly but I get this message on the “class ViewController” line:
Type ‘ViewController’ does not conform to protocol ‘UITableViewDataSource’
The line in my code reads:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { …}
What am I doing wrong? Thanks!
That code looks good
Maybe you have a typo somewhere before the class declarations
Nice tutorial, thanks. I tried to use the assistant editor to generate the outlet; it generates a ‘strong’ tableView (@IBOutlet strong var tableView: UITableView). With this code version the compiler is complaining: “Swift Compiler error: Property self.tableView not initialised at super.init call. Is this a Xcode beta3 bug?
By the way, if I silence in func “viewDidLoad” the code line: self.tableView.registerClass(…) the app runs also well. What is this code line for ?
The source code contains bugs
I am using Xcode version 6.0.1 please fix it
Overall nice tut
The code should work now.
I just downloaded your sample and opened it in Xcode 6.0 and it has loads of compilation errors.
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:14:22: error: array types are now written with the brackets around the element type
var items: String[] = [“We”, “Heart”, “Swift”]
^
[
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:12:5: error: ‘IBOutlet’ property has non-optional type ‘UITableView’
@IBOutlet
^
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:13:31: note: add ‘?’ to form the optional type ‘UITableView?’
var tableView: UITableView
^
?
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:13:31: note: add ‘!’ to form the implicitly unwrapped optional type ‘UITableView!’
var tableView: UITableView
^
!
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:11:7: error: class ‘ViewController’ has no initializers
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
^
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:13:9: note: stored property ‘tableView’ without initial value prevents synthesized initializers
var tableView: UITableView
^
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:12:1: error: ‘required’ initializer ‘init(coder:)’ must be provided by subclass of ‘UIViewController’
@IBOutlet
^
UIKit.UIViewController:100:34: note: ‘required’ initializer is declared in superclass here
@objc(initWithCoder:) required init(coder aDecoder: NSCoder)
^
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:11:1: error: type ‘ViewController’ does not conform to protocol ‘UITableViewDataSource’
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
^
UIKit.UITableViewDataSource:2:48: note: protocol requires function ‘tableView(_:numberOfRowsInSection:)’ with type ‘(UITableView, numberOfRowsInSection: Int) -> Int’
@objc(tableView:numberOfRowsInSection:) func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
^
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:23:10: note: candidate has non-matching type ‘(UITableView!, numberOfRowsInSection: Int) -> Int’
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
^
UIKit.UITableViewDataSource:3:48: note: protocol requires function ‘tableView(_:cellForRowAtIndexPath:)’ with type ‘(UITableView, cellForRowAtIndexPath: NSIndexPath) -> UITableViewCell’
@objc(tableView:cellForRowAtIndexPath:) func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
^
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:27:10: note: candidate has non-matching type ‘(UITableView!, cellForRowAtIndexPath: NSIndexPath!) -> UITableViewCell!’
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
^
/Users/matt/Desktop/ios8tableview/ios8test/ViewController.swift:30:14: error: ‘UILabel?’ does not have a member named ‘text’
cell.textLabel.text = self.items[indexPath.row]
^ ~~~~
I forgot to update the second link from the article. Here is the updated version of thew code.
Thank you!
I wonder about the difference between your code and my code.
You are implementing the method “func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!”, but this only gives me an error that my class is not implementing the protocol, because it defines “func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell” (no !explicit unwrapping).
What’s up with that? Would be great if you could give me a reply on that.
well func foo(bar: Type) and func foo(bar: Type!) are different because Type and Type! are different types
it would be the same issue if you had foo(bar: Int) and foo(bar: String)
error opening source code, good game /trolled
what XCode version do you have?
Hey loved the tutorial was wondering if you had considered creating one around creating a tableview that goes to another table view and then gives more detail on the subject?
e.g.
tableview 1
DOG
CAT
SNAKE
select DOG and you get a tableview showing
LABRADOR
HUSKEY
POODLE
select LABRADOR
and you get a table showing information on labradors
and equally you could select CAT to get a list of different breeds then more information etc. etc.
would love an example on the above
Thanks for asking! We will make a tutorial soon
Hi I have done everything as you mentioned but in table view the items are not displaying. its just blank. Am not getting the reason. Can you please help me with this.
please check my code below.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tblView: UITableView!
var animals = [“Horse”,”Monkey”,”Lion”,”Tiger”]
override func viewDidLoad() {
super.viewDidLoad()
self.tblView.registerClass(UITableViewCell.self, forCellReuseIdentifier: “cell”)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return self.animals.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
println(“the cell”)
let cell : UITableViewCell = self.tblView.dequeueReusableCellWithIdentifier(“cell”) as UITableViewCell
cell.textLabel.text = self.animals[indexPath.row]
return cell
}
}
1. don’t be lazy when declaring a variable name tableView takes as long as tblView to type.
2. the code looks ok. the problem might be that you did not connect the tblView outlet in interface builder. there should be a circle on the left of the code next to @IBOutlet weak var tblView: UITableView! it should be full not empty.
You should link the tableView with the View Controller to handle “delegate” and “datasource” ..
For that you can simple cntrl click the tableVIew and drag it to the View Controller, then you can select delegate and datasource. You have to do this to connect the table view and controller. In case of TableViewController it is done automatically. Here we have to do it manually.
UITableViewController is easier to setup in this example app. But it’s important to know about the data source and delegate protocols. Also I tend no to use UITableViewController because the view is a UITableView.
Great tutorial, thanks!
I have a little problem you might be able to help me with. I’m running xCode 6.1 and your code works perfectly when running on the iOs 8.1 simulator (probably on 8.1 devices as well). However, if I run it on the iOs 7.1 simulator (or on my iPhone 4s running iOs 7.1), the table view is never displayed. If I debug the app, I see that the tableView/numberOfRowsInSection method is called, but the tableView/cellForRowAtIndexPath is never called.
Have you ever run your example on iOs 7.1? If so, can you tell me what I have to change to make it work? I’m having the same problem with Collection Views.
Thanks!
A colleague of mine helped me out. The problem was that the “Use Size Classes” checkbox in Main.storyboard was checked. This isn’t compatible with iOs 7 (http://stackoverflow.com/questions/24172860/how-can-xcode-6-adaptive-uis-be-backwards-compatible-with-ios-7-and-ios-6/25795533#25795533), but it’s checked by default when you create a new app in xCode 6.1
I hope posting this will help someone else that’s trying to run this example on iOs 7.
Thanks again for the great tutorial!
Thanks for example. It is nice explained!
I only have problem to get it work. I get runtime exception
“fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)”
in viewDidLoad() on line tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: “cell”). The reason is tableView is nil. Here is the screenshot http://pbrd.co/1FYXspY and you can see the outlet is created and connected to UITableView (I had mouse cursor on circle when taking screenshot to see blue highlighted table). Any Idea?
1. Clean Shift+Cmd+K then run again.
2. The solution that always works for me if things look like they should work but don;t. Remove the tableview and add it again
Thanks for hint but still get the error.
One thing is different on my Xcode – when try to connect IBOutlet I see this small window http://pbrd.co/1wEKnkb
It seems problem is in my storyboard file. I copied the file from another project to my project and it maybe wasn’t good idea
One thing is different on my Xcode – when try to connect IBOutlet I see this small window http://pbrd.co/1wEKnkb
I ran into the same issue on Xcode 6.4 (6E35b).
My fix is as follows – go to the storyboard file -> right-click ViewController on the left pane under View Controller Scene -> in the Outlets section, your tableView is most likely not connected so crtl-click and connect the outlet to the Table View
Good luck.
its not working
thanks for nothing
The framework changed a bit in Xcode 6.1.1. I’ve updated the article
hey Andrei
great tutorial.
i am having an issue with the
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
absolutely nothing runs inside that function….what ever code i use does not run at all
do you have any suggestions?
i have worked with tableViews before but only as UItableViewcontroller ….but i used your tutorial because i am using a tableView inside a ViewController.
I’m trying to use this code:
if let cell = tableView.cellForRowAtIndexPath(indexPath) { if cell.accessoryType == .None {
cell.accessoryType = .Checkmark } else {
cell.accessoryType = .None }
}
tableView.deselectRowAtIndexPath(indexPath, animated: true) }
but even when i use a simple println() nothing runs on click….it seems to me that the entire click on the table view is not being recognized.
thank you
did you connect the delegate outlet?
Thank you for the tutorial, it works; however, if I have enough things to display, the table does not scroll. Do I have to implement a new method to make it scrollable?
the tableview should be scrollable by default. You might have the scrollable checkbox unchecked in interface builder.
Hi, I modified slightly the code for cell, so the Xcode will not call any issues, it’s the unwrapped optional case:
var cell:UITableViewCell? = self.tableView.dequeueReusableCellWithIdentifier(“cell”) as UITableViewCell?
cell?.textLabel?.text = self.items[indexPath.row]
return cell!
Does anybody else needed that modification to get the app working?
Hey David,
You should be able to cast the cell to a UITableViewCell directly.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
This modification was the solution to a “‘UITableViewCell?’ is not convertible to ‘UITableViewCell’ error Xcode generated. Thanks David.
Running:
Xcode 7.0 beta 5
El Cap 10.11 Beta
linker command failed with exit code 1 (use -v to see invocation)
Why is that coming up ?
What version of Xcode are you using?
Same for me when I try to build your source code.
I use Xcode 6.1.1
That’s strange, I also have 6.1 and it works for me. Try cleaning Cmd + Shift + K and the building.
Same here, same error. Also Xcode 6.1.1
I changed the project. It should work now.
Hi i do get the error of fatal error: unexpectedly found nil while unwrapping an Optional value. I have found out that it is because the uilabel in the custom cell is not available although i have connected it to the storyboard? Any idea ?
a bit of context would help
James,
I was having the same problem as you, with the unwrapping of an optional value error. Make sure that you’re connecting the Referencing Outlet, datasource AND delegate to the ViewController. That’s what fixed it for me.
Hi! I LOVE this tutorial. It was very helpful. I’m having an issue where the text isn’t showing up on the app, however the table and the lines are showing up. I was having a hard time following the tutorial for the part with the dragging of the outlets. I was able to drag the referencing outlet into the code, but am having a hard time dragging the other 2 outlets anywhere except the storyboard itself. When I do that, I get an error. How do I fix this problem? Thank you so much for this amazing tutorial and for your help!
Never mind, figured it out! Must have been an issue from my typing, because when I started from scratch it worked out well. Thanks for this tutorial.
Edit to my last post: I figured out how to connect them, however the table cell text still isn’t appearing.
Thanks in advance for your help!
You need to connect the dataSource and delegate outlets to the ViewController in the storyboard
Thanks for the code.
Although I enabled in the Storyboard “Accessory – DisclosureIndicator” is not visible when I code this way. Also the related segue that shows detail page. Can you help?
Aysin
I can’t understand the problem. Can you rephrase your question?
On the storyboard, for the TableViewCell, I have set Accessory as Disclosure Indicator. And I have added a push segue to show other ViewController. Normally this works fine with TableViewController. But now the indicator does not show up in the TableCell and does not show the other controller when tapped.
Thanks so much! I got hung up for just a second on the Referencing outlet part. I hooked the delegate and data source to the View Controller, but didn’t hook the IBOutlet variable in View Controller to the table view.
Thank you for the great tutorial! I hope this tidbit will help someone else.
Adding the line
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: “cell”)
to the viewDidLoad method produced the following runtime error:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
I was able to fix it like this:
self.tableView?.registerClass(UITableViewCell.self, forCellReuseIdentifier: “cell”)
This is my first tutorial, I am a complete NOOB so forgive me if this type of thing is obvious.
Running Xcode 6.1 on MacOs 10.10.1
i follow your tutorials but it seem not to display cell value the only thing missing is `tableView.delegate = self` and `tableView.dataSource = self`. give it a try it may help. fyi im using xcode 6.3
you can do that in interface builder. it’s in the step in witch you connect the tableView outlet.
Nice Article, 10x.
You tutorial has been helpful.
In my XCode the line
“var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier(“cell”) as UITableViewCell”
XCode wants to put a ‘!’ in after the ‘as’. It compiles successfully this way.
Great article, best tableview article I have found (and I’ve been searching for a while now!). Only article that I’ve found that actually creates a table at the end. I also did get thrown off at the outlet portion, but a comment below fixed my issue quickly. Thank you so much!!!!
Xcode und Swift sind der letze scheißdreck! Da programmier ich lieber Maschienensprache
Warum Maschienensprache? Du kannst doch die bits mit einem magnetisierter Nadel stossen.
Would be nice if you showed how to make the tableview go away and display some content on the main screen… This tutorial ended one step too soon.
no – this tutorial was how to make a simple table view – it’s really good at explaining just that
We have other table view related tutorials – you might enjoy https://www.weheartswift.com/birds-cats-dogs/
Have fun!
I got error that “Type viewcontroller doesnt conform to protocol UITableViewDataSource” please reply soon
The protocol changed – cmd+ click on UITableViewDataSource to see the correct method signatures – most probably there is a difference in the type of a parameter X -> X! or X! -> X
hello…wondering if you have experience on import QuickLook with tableview before?
Hello. In the “add some data to display” section, If you have hundreds of records to display, what’s the best place to enter this data? How would you implement this? Thanks.
try doing the same approach as you would with tens of items… if that fails then optimise.
cell.textLabel?.text = self.items(indexPath.row)
getting error on that statement — cannot call value of non-function type ‘[string]’
self.items[indexpath.row]
use square brackets [ ] for arrays
( ) are for functions that’s why you get that error
Love the post! Worked like a charm! Cheers!
AS OF: 7 / 3 / 2016 , this is the BEST UITableView tutorial for a NESTED UITableView.
One tip for the author:
I got an error with this:
let cell = tableView.dequeueReusableCellWithIdentifier(“SomeTableViewCell”) as! SomeTableViewCell
but this worked:
let cell = tableView.dequeueReusableCellWithIdentifier(“SomeTableViewCell”, forIndexPath: indexPath) as! SomeTableViewCell
Otherwise, great step-by-step tutorial, thanks man!