A Table View is a list of rows that can be selected. Each row is populated from a data source. This example creates a simple table view in which each row is a single line of text.
Although there are a number of ways to create a UITableView
, one of the easiest is to add one to a Storyboard. Open your Storyboard and drag a UITableView
onto your UIViewController
. Make sure to use Auto Layout to correctly align the table (pin all four sides).
In order to display content dynamically (i.e. load it from a data source like an array, a Core Data model, a networked server, etc.) in your table view you need to setup the data source.
A data source could, as stated above, be anything with data. Its entirely up to you how to format it and whats in it. The only requirement is that you must be able to read it later so that you can populate each row of your table with data when needed.
In this example, we'll just set an array with some strings (text) as our data source:
Swift
let myDataArray: [String] = ["Row one", "Row two", "Row three", "Row four", "Row five"]
Objective-C
// You'll need to define this variable as a global variable (like an @property) so that you can access it later when needed.
NSArray *myDataArray = @[@"Row one", @"Row two", @"Row three", @"Row four", @"Row five"];
Make sure your view controller conforms to the UITableViewDataSource
protocol.
Swift
class ViewController: UIViewController, UITableViewDataSource {
Objective-C
@interface ViewController : UIViewController <UITableViewDataSource>
As soon as your view controller has declared it will conform to the UITableViewDataSource
(that's what we've just done above), you are required to implement at least the following methods in your view controller class:
tableView:numberOfRowsInSection
, this asks you how many rows your table view should have.
// Swift
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.myDataArray.count
}
tableView:cellForRowAtIndexPath
, requests that you create and return a cell for each row you specified in tableView:numberOfRowsInSection
. So, if you said you needed 10 rows, this method will be called ten times for each row, and you need to create a cell for each of those rows.
// Swift
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Create a new cell here. The cellReuseIdentifier needs to match the reuse identifier from the cell in your Storyboard
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as UITableViewCell!
// Set the label on your cell to the text from your data array
cell.textLabel?.text = self.myDataArray[indexPath.row]
return cell
}
WARNING: You may NOT return nil for any cells in
cellForRowAtIndexPath:
. This will cause your app to crash, and you will see the following error in the console:Uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
You can either do this via code by setting your table's dataSource
property to self
on your view controller. Or you may select your table view in your storyboard, open the Attributes Inspector, select the "Outlets" panel, and drag from dataSource
to your view controller (NOTE: make sure you connect to the UIViewCONTROLLER, not a UIView or another object in your UIViewController).
When a user taps on a row in your table view, generally, you'll want to do something - to respond. In many apps, when you tap on a row, more information about that item you tapped upon is displayed. Think of the Messages app: when you tap on the row showing one of your contacts, the conversation with that person is then displayed on screen.
In orer to do that, you must conform to the UITableViewDelegate
protocol. Doing so is similar to conforming to the data source protocol. This time however, you'll just add it next to UITableViewDataSource
and separate it with a comma. So it should look like this:
Swift
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
Objective-C
@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
There are no required methods to implement for the table view's delegate. However, to handle row selections you'll need to use the following method:
tableView:didSelectRowAtIndexPath
, this is called whenever a row is tapped, which allows you to do something in response. For our example, we'll just print a confirmation statement to the Xcode log.
// Swift
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped cell number \(indexPath.row).")
}
// Objective-C
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"You tapped cell number %ld.", (long)indexPath.row);
}
See below for the full setup with just code, no explanation.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Data model: These strings will be the data for the table view cells
let myDataArray: [String] = ["Row one", "Row two", "Row three", "Row four", "Row five"]
// cell reuse id (cells that scroll out of view can be reused)
let cellReuseIdentifier = "cell"
// don't forget to hook this up from the storyboard
@IBOutlet var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Register the table view cell class and its reuse id
myTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
// This view controller itself will provide the delegate methods and row data for the table view.
myTableView.delegate = self
myTableView.dataSource = self
}
// number of rows in table view
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.myDataArray.count
}
// create a cell for each table view row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as UITableViewCell!
// set the text from the data model
cell.textLabel?.text = self.myDataArray[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController: UIViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView *myTableView;
NSArray *myDataArray;
}
@end
ViewController.m
#import "ViewController.h"
// cell reuse id (cells that scroll out of view can be reused)
NSString * _Nonnull cellReuseIdentifier = @"cell";
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Data model: These strings will be the data for the table view cells
myDataArray = @[@"Row one", @"Row two", @"Row three", @"Row four", @"Row five"];
// Register the table view cell class and its reuse id
[myTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:cellReuseIdentifier];
// This view controller itself will provide the delegate methods and row data for the table view.
myTableView.delegate = self;
myTableView.dataSource = self;
}
// number of rows in table view
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return myDataArray.count;
}
// create a cell for each table view row
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// create a new cell if needed or reuse an old one
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellReuseIdentifier];
// set the text from the data model
cell.textLabel.text = myDataArray[indexPath.row];
return cell;
}
// method to run when table view cell is tapped
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(@"You tapped cell number %ld.", (long)indexPath.row);
}
@end