You are on page 1of 35

Table view controller

Table view controllers

■ Similar to picker view controllers


Table view delegate

•When the app needs information about the table view


(such as what to display), it asks the table view’s
delegate
•Usually the table view’s delegate is the view controller
that contains it
•When we make a table view, we have to specify the
delegate
Three required functions for the table view
delegate
• func numberOfSectionsInTableView(tableView: UITableView) -> Int

• func tableView(tableView: UITableView,


• numberOfRowsInSection section: Int) -> Int { }

• func tableView(tableView: UITableView,


• cellForRowAtIndexPath indexPath: NSIndexPath) ->
UITableViewCell { }

Object A may specify a delegate (another


object) B
numberOfSectionsInTableView

• tells the table view how many sections to display.


• Sections are visual groupings of cells within table views, which is especially
useful in table views with a lot of data.

• override func numberOfSectionsInTableView(tableView: UITableView) -> Int


{
• return 1
•}
numberOfRowsInSection

Suppose the data source for the table view is the array dwarves

func tableView(tableView: UITableView,


numberOfRowsInSection section: Int) -> Int
{
return dwarves.count
}
cellForRowAtIndexPath
• func tableView(tableView: UITableView,
• cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
• var cell =
tableView.dequeueReusableCellWithIdentifier(simpleTableIdentifier)
• if (cell == nil) {
• cell = UITableViewCell(style: UITableViewCellStyle.Default,
reuseIdentifier: simpleTableIdentifier)
• }
• // do other things
• cell!.textLabel?.text = dwarves[indexPath.row]
• cell!.textLabel?.font = UIFont .boldSystemFontOfSize(50)
• return cell!
• }
Why dequeue?

• var cell =
tableView.dequeueReusableCellWithIdentifier(simpleTableIdentifier)
• if (cell == nil) {
• cell = UITableViewCell(style: UITableViewCellStyle.Default,
reuseIdentifier: simpleTableIdentifier)
• }
iOS makes a new table view cell only when it cannot re-use a cell, for
efficiency
Object A may specify a delegate (another
object) B
connect the table view cell with the data source


• cell!.textLabel?.text = dwarves[indexPath.row]
• cell!.textLabel?.font = UIFont .boldSystemFontOfSize(50)
• return cell!

Object A may specify a delegate (another


object) B
The image property of a table view cell

• Table view cells have a built in image property

• let image = UIImage(named: "star")


• cell!.imageView?.image = image
• let highlightedImage = UIImage(named: "star2")
• cell!.imageView?.highlightedImage = highlightedImage

Object A may specify a delegate (another


object) B
Segues

• Consider a typical situation


• Table View
• Tapping on a cell will present a Detail View that display (more)
information on the object of the cell selected in the Table View
• What is needed to be done:
• In the storyboard, embed the Table View Controller in a Navigation
Controller
• make a segue from the cell of the Table View to the Detail View
Controller
• In code (of the Table View), pass the object selected to the Detail
View

Object A may specify a delegate (another


Making a segue in the story board

• In the storyboard, make sure the cell is visible


• Attribute Inspector -> Table View -> set Prototype Cells to 1

• control-drag the cell to the Detail View Controller


• A pop up window appears, choose Selection Segue -> Show
• Select the segue in the storyboard, give it an unique identifier, such as
“ShowDetail”
• How? Attribute Inspector -> Identifier

Object A may specify a delegate (another


object) B
Passing data to the Detail View Controller

• We have two steps here


• In the Detail View Controller, write a method to receive (set) data
• In the Table View Controller, send data by invoking the method in the
Detail View Controller, this operation is done in the method

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)

Object A may specify a delegate (another


object) B
Method to receive data

class DetailViewController: UIViewController {

var data = ""

// This function is called by the TableViewController to set the


message
func initWithData(data: String){
self.data = data
}

Object A may specify a delegate (another


object) B
Method to send data

• class TableViewController: UIViewController {


• @IBOutlet weak var tableView: UITableView!
var name = [Int]() // data source

func tableView(tableView: UITableView,


numberOfRowsInSection section: Int) -> Int {
return name.count
}

func tableView(tableView: UITableView,


cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

var cell =
tableView.dequeueReusableCellWithIdentifier(simpleTableIdentifier)
if (cell == nil) {
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier:
simpleTableIdentifier)
}
cell!.textLabel?.text = String(name[indexPath.row])
return cell!
}
Method to send data

• class TableViewController: UIViewController {


• // continued

// This function is called before the segue "ShowDetail" is made from this view
controller to the destination controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

if segue.identifier == "ShowDetail" {
//get the index of the row selected in the table
let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)!;

//segue to the details screen


let detailVC = segue.destinationViewController as! DetailViewController;

//set the selected rss item in the details view


detailVC.initWithData(String(name[indexPath.row]))
} //
}
Making a segue in general

• Consider:
• the TypingViewController has a Save button
• when the button is pressed
• data is sent to the ViewController
• Steps to be done
• In the ViewController, implement an IBAction to receive the data
• @IBAction func unwindToViewController(sender: UIStoryboardSegue)
{ …} // you may name the function anything

• In the storyboard, connect the Save button to the IBAction in the


ViewController
• Select the Save button in the TypingViewController
• Control-drag the button to the Exit icon (of the TypingViewController)
• A window pops up, select the function unwindToViewController
Code of the ViewController (destination)

class ViewController: UIViewController {

@IBOutlet weak var resultTextField: UITextField!

var writing:String?

override func viewDidLoad() {


super.viewDidLoad()
// 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.
}

@IBAction func unwindToViewController(sender: UIStoryboardSegue) {


if let sourceViewController = sender.sourceViewController as? TypingViewController,
writing = sourceViewController.writing {
// Add a new meal.

self.writing = writing
resultTextField.text = self.writing
}
}
}
Code of the TypingViewController (Source)

class TypingViewController: UIViewController {

@IBOutlet weak var saveButton: UIBarButtonItem!


@IBOutlet weak var inputField: UITextField!

var writing: String?

@IBAction func cancel(sender: AnyObject) {//connect to the cancel button

dismissViewControllerAnimated(true, completion: nil)


}

// This method lets you save data before the segue.


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if saveButton === sender {
self.writing = inputField.text //save data to property writing
}
} //prepareForSegue

// other methods
Import a property list into a table view
table view data source methods
class ViewController: UIViewController, UITableViewDataSource {
var names: [String: [String]]!
var keys: [String]!

let path = NSBundle.mainBundle().pathForResource(


"sortednames", ofType: "plist")
let namesDict = NSDictionary(contentsOfFile: path!)
names = namesDict as! [String: [String]]
keys = (namesDict!.allKeys as! [String]).sort()

keys is a sorted array of string A, B, C, … , Z

func numberOfSectionsInTableView(tableView: UITableView) -> Int {


return keys.count
}

func tableView(tableView: UITableView,


numberOfRowsInSection section: Int) -> Int {
let key = keys[section]
let nameSection = names[key]!
return nameSection.count // number of rows in each section
} //….
table view data source methods
func tableView(tableView: UITableView,
titleForHeaderInSection section: Int) -> String? {
return keys[section] // the letter for the group
}
// to display the index of the table
func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
return keys
}

func tableView(tableView: UITableView,


cellForRowAtIndexPath indexPath: NSIndexPath) ->
UITableViewCell {
let cell =
tableView.dequeueReusableCellWithIdentifier(sectionsTableIdentifier,
forIndexPath: indexPath) as UITableViewCell

let key = keys[indexPath.section] // the key of the section


let nameSection = names[key]! // the letter of the section
cell.textLabel?.text = nameSection[indexPath.row] // the value in the section

return cell
}
How to make a search bar
Three steps for search bar
1.Specify the data to be searched

2.A view controller to display the search


results

3.UISearchController to provide the search bar


and manage the display of the search result

1 and 2 are done in the main (table)view


controller
3 is a new view controller (in our example it is
SearchResultsController
Adding a search bar to the table view
controller
class ViewController: UIViewController, UITableViewDataSource {
//….
var searchController: UISearchController!

let resultsController = SearchResultsController() //defined later


resultsController.names = names
resultsController.keys = keys

searchController = UISearchController( searchResultsController:


resultsController)

let searchBar = searchController.searchBar


searchBar.scopeButtonTitles = ["All", "Short", “Long"]
// short: less than 6 chars, long: at least 6 chars
searchBar.placeholder = "Enter a search term"
searchBar.sizeToFit()
tableView.tableHeaderView = searchBar
searchController.searchResultsUpdater = resultsController
UISearchController
class UISearchController : UIViewController,
UIViewControllerTransitioningDelegate,
UIViewControllerAnimatedTransitioning

Description

A UISearchController object manages the display of search results based


on interactions with a search bar. You use a search controller in tandem with
your existing view controllers. When you have a view controller with searchable
content, incorporate the search bar of a UISearchController object into
your view controller’s interface. When the user interacts with that search bar,
the search controller automatically displays a new view controller with the
search results that you specify.

searchResultsUpdater
weak var searchResultsUpdater:
UISearchResultsUpdating? { get set }

The object responsible for updating the contents of the search results
controller.

Assign an object that adopts the UISearchResultsUpdating protocol.


Use the methods of that protocol to search your content and deliver the
results to your search results view controller. The object contained by
the searchResultsUpdater property is often the view controller that is
set during initialization.

protocol UISearchResultsUpdating
protocol UISearchResultsUpdating : NSObjectProtocol
Description

Use the UISearchResultsUpdating protocol to update search


results based on information the user enters into the search bar.

SearchResultsController
class SearchResultsController: UITableViewController, UISearchResultsUpdating {
private static let longNameSize = 6
private static let shortNamesButtonIndex = 1
private static let longNamesButtonIndex = 2
let sectionsTableIdentifier = “SectionsTableIdentifier"

// search controller needs access to data of the table view controller


var names:[String: [String]] = [String: [String]]()
var keys: [String] = []

var filteredNames: [String] = [] // the results of the search

override func viewDidLoad() {


super.viewDidLoad()

tableView.registerClass(UITableViewCell.self,
forCellReuseIdentifier: sectionsTableIdentifier)
} ..
SearchResultsController 2
class SearchResultsController: UITableViewController, UISearchResultsUpdating {
//…

override func didReceiveMemoryWarning() {


super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

// MARK: Table View Data Source Methods


override func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return filteredNames.count
}

override func tableView(tableView: UITableView,


cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(sectionsTableIdentifier)
cell!.textLabel?.text = filteredNames[indexPath.row]
return cell!
}
updateSearchResultsForSearchController

func updateSearchResultsForSearchController(searchController:
UISearchController)

Description

Called when the search bar becomes the first responder or when the user makes
changes inside the search bar.

This method is automatically called whenever the search bar becomes the first
responder or changes are made to the text in the search bar. Perform any required
filtering and updating inside of this method.

We produce the search result in this function

SearchResultsController 3
class SearchResultsController: UITableViewController, UISearchResultsUpdating {
//…

// MARK: UISearchResultsUpdating Conformance


func updateSearchResultsForSearchController(
searchController: UISearchController) {
if let searchString = searchController.searchBar.text {
let buttonIndex = searchController.searchBar.selectedScopeButtonIndex
filteredNames.removeAll(keepCapacity: true)

if !searchString.isEmpty {
let isAMatch: String -> Bool = { name in // this is a closure
// Filter out long or short names depending on which
// scope button is selected.
let nameLength = name.characters.count
if (buttonIndex == SearchResultsController.shortNamesButtonIndex
&& nameLength >= SearchResultsController.longNameSize)
|| (buttonIndex == SearchResultsController.longNamesButtonIndex
&& nameLength < SearchResultsController.longNameSize) {
return false
}

let range = name.rangeOfString(searchString,


options: NSStringCompareOptions.CaseInsensitiveSearch)
return range != nil
}
The closure isAMatch
isAMatch: String -> Bool

takes a string and returns true if it satisfies the scope


(short, long, all) and matches with the search string
SearchResultsController 4
class SearchResultsController: UITableViewController, UISearchResultsUpdating {
//…

// MARK: UISearchResultsUpdating Conformance


func updateSearchResultsForSearchController(
searchController: UISearchController) {
// ….

for key in keys {


let namesForKey = names[key]! // get all names that starts with the given
letter (key)
let matches = namesForKey.filter(isAMatch) // the filter func is system
defined
filteredNames += matches
}
}

tableView.reloadData()
}
}

func filter(@noescape includeElement: (Self.Generator.Element) throws -> Bool) rethrows ->


[Self.Generator.Element]
Description

Return an Array containing the elements of self, in order, that satisfy the predicate includeElement.

You might also like