Indexed Table Views In Swift

Let’s start creating a new project in Xcode. We will start with Single View Application Template. In Xcode go File->New-Project in the menu and choose a Single View Application template.

1 

Fill in Product Name.

2

Then choose where your Xcode project will be stored.

Now in ViewController.swift class change base class from UIViewController to UITableViewController.

3

In Main.storyboard remove existing view controller from canvas.

4

Look at Object Library which is on bottom right. Find Table View Controller there. Drag it to the canvas and drop it there. In Attributes Inspector on the right turn on “Is Initial View Controller” check box.

5

In storyboard choose your view controller and in Identity Inspector enter ViewController as class name of the class.

6

Now if you Build and Run you will see empty table in simulator.

6. Now if you Build and Run you will see empty table in simulator

Choose cell in storyboard and fill in Identifier field in Attributes inspector. That’s cell reuse identifier which will be used in code.

7

Now let’s do some coding. In ViewController.swift add protocol UITableViewDataSource to class declaration. That means our class ViewController intends to implement some methods from protocol UITableViewDataSource.

8

Add array to the class. Array will be our data source.

 

var array = ["Moscow", "Saint Petersburg", "Novosibirsk", "Novosibirsk", "Nizhny Novgorod", "Samara", "Omsk", "Kiyv", "Odessa", "Donetsk", "Harkiv", "Lviv", "Uzhgorod", "Zhytomyr", "Luhansk", "Mikolayv", "Kherson", "Germany", "Berlin", "Hamburg", "Munich", "Cologne", "Frankfurt", "Stuttgart", "Düsseldorf", "Dortmund", "Essen", "Bremen", "Abilene", "Akron", "Albuquerque", "Alexandria", "Allentown", "Amarillo", "Anaheim", "Anchorage", "Ann Arbor", "Antioch", "Arlington", "Arvada", "Athens", "Atlanta", "Augusta", "Aurora", "Austin", "Bakersfield", "Baltimore", "Baton Rouge", "Beaumont", "Bellevue", "Berkeley", "Billings", "Birmingham", "Boise", "Boston", "Boulder", "Bridgeport", "Broken Arrow", "Brownsville", "Buffalo", "Burbank", "Cambridge", "Cape Coral", "Carlsbad", "Carrollton", "Cary", "Cedar Rapids", "Centennial", "Chandler", "Charleston", "Charlotte", "Chattanooga", "Chesapeake", "Chicago", "Chula Vista", "Cincinnati", "Clarksville", "Clearwater", "Cleveland", "College Station", "Colorado Springs", "Columbia", "Columbus", "Concord", "Coral Springs", "Corona", "Corpus Christi", "Costa Mesa", "Dallas", "Daly City", "Davenport", "Dayton", "Denton", "Denver", "Des Moines", "Detroit", "Downey", "Durham", "Edison", "El Cajon", "El Monte", "El Paso", "Elgin", "Elizabeth", "Elk Grove", "Erie", "Escondido", "Eugene", "Evansville", "Everett", "Fairfield", "Fargo", "Fayetteville", "Fontana", "Fort Collins", "Fort Lauderdale", "Fort Wayne", "Fort Worth", "Fremont", "Fresno", "Frisco", "Fullerton", "Gainesville", "Garden Grove", "Garland", "Gilbert", "Glendale", "Grand Prairie", "Grand Rapids", "Green Bay", "Greensboro", "Gresham", "Hampton", "Hartford", "Hayward", "Henderson", "Hialeah", "High Point", "Hollywood", "Honolulu", "Houston", "Huntington Beach", "Huntsville", "Independence", "Indianapolis", "Inglewood", "Irvine", "Irving", "Jackson", "Jacksonville", "Jersey City", "Joliet", "Kansas City", "Kent", "Killeen", "Knoxville", "Lafayette", "Lakeland", "Lakewood", "Lancaster", "Lansing", "Laredo", "Las Cruces", "Las Vegas", "Lewisville", "Lexington", "Lincoln", "Little Rock", "Long Beach", "Los Angeles", "Louisville", "Lowell", "Lubbock", "Madison", "Manchester", "McAllen", "McKinney", "Memphis", "Mesa", "Mesquite", "Miami", "Miami Gardens", "Midland", "Milwaukee", "Minneapolis", "Miramar", "Mobile", "Modesto", "Montgomery", "Moreno Valley", "Murfreesboro", "Murrieta", "Naperville", "Nashville", "New Haven", "New Orleans", "New York", "Newark", "Newport News", "Norfolk", "Norman", "North Charleston", "North Las Vegas", "Norwalk", "Oakland", "Oceanside", "Oklahoma City", "Olathe", "Omaha", "Ontario", "Orange", "Orlando", "Overland Park", "Oxnard", "Palm Bay", "Palmdale", "Pasadena", "Paterson", "Pearland", "Pembroke Pines", "Peoria", "Peoria", "Philadelphia", "Phoenix", "Pittsburgh", "Plano", "Pomona", "Pompano Beach", "Port St. Lucie", "Portland", "Providence", "Provo", "Pueblo", "Raleigh", "Rancho Cucamonga", "Reno", "Rialto", "Richardson", "Richmond", "Riverside", "Rochester", "Rockford", "Roseville", "Round Rock", "Sacramento", "Saint Paul", "Salem", "Salinas", "Salt Lake City", "San Antonio", "San Bernardino", "San Diego", "San Francisco", "San Jose", "San Mateo", "Santa Ana", "Santa Clara", "Santa Clarita", "Santa Maria", "Santa Rosa", "Savannah", "Scottsdale", "Seattle", "Shreveport", "Simi Valley", "Sioux Falls", "South Bend", "Spokane", "Springfield", "St. Louis", "St. Petersburg", "Stamford", "Sterling Heights", "Stockton", "Sunnyvale", "Surprise", "Syracuse", "Tacoma", "Tallahassee", "Tampa", "Temecula", "Tempe", "Thornton", "Thousand Oaks", "Toledo", "Topeka", "Torrance", "Tucson", "Tulsa", "Tyler", "Vallejo", "Vancouver", "Ventura", "Victorville", "Virginia Beach", "Visalia", "Waco", "Warren", "Washington", "Waterbury", "West Covina", "West Jordan", "West Palm Beach", "West Valley City", "Westminster", "Wichita", "Wichita Falls", "Wilmington", "Winston–Salem", "Woodbridge", "Worcester", "Yonkers", "York"]

 

 

This array is array of strings which we will display in our table. Array is quite big. To make navigating through table easier we are willing to order it and display cells grouped in sections according to their first letter.

Add this line to viewDidLoad method to sort array to it’s alphabetical order:

 

array.sort { $0 < $1 }

 

 

Now we have to split this array on groups of elements, each will be displayed in separate section of table view. We won’t split array literally but just bring into play another supplementary array which will help us to remember where each section starts, how many elements are in a section and what title of a section is.

 

var sections : [(index: Int, length :Int, title: String)] = Array()

 

 

Elements of this array are tuples, special Swift type. Tuple has a number of named elements. index is index in array array of first element of section. length is number of elements in section. And title is one character string which will be used for section title in table view.

Now we have to build this array. Add code below to viewDidLoad method just under sorting line:

 

var index = 0;

for ( var i = 0; i < array.count; i++ ) {

let commonPrefix = array[i].commonPrefixWithString(array[index], options: .CaseInsensitiveSearch)

if (countElements(commonPrefix) == 0 ) {

let string = array[index].uppercaseString;

let firstCharacter = string[string.startIndex]

let title = "\(firstCharacter)"

let newSection = (index: index, length: i - index, title: title)

sections.append(newSection)

index = i;

}

}

 

 

We traverse through array beginning from the first element. Inside of for loop we look for common prefix of elements in array array with indexes i and index. If these strings don’t have common index, that means we have finished with section. We make new tuple and insert it into array sections. Then we remember where next section starts and repeat loop.

Lets implement UITableViewDataSource protocol methods. numberOfSectionsInTableView returns number of sections in table. In will be count of our sections array.

 

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

return sections.count

}

 

 

numberOfRowsInSection returns number of rows in particular section. We take this from sections array.

 

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return sections[section].length

}

 

 

And cellForRowAtIndexPath makes cell and fills it with data.

 

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

var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell;

cell.textLabel?.text = array[sections[indexPath.section].index + indexPath.row]

return cell

}

 

 

To make index work we need to implement another three methods. titleForHeaderInSection will return appropriate title of the section. We get title from sections array:

 

override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String {

return sections[section].title

}

 

 

sectionIndexTitlesForTableView should return array of all section titles. We have all section titles in sections array. To make new array with titles only we use map function:

 

override func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]! {

return sections.map { $0.title }

}

 

 

sectionForSectionIndexTitle returns section number. In our case it will be index parameter of this method.

 

override func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {

return index

}

 

 

9

Build and Run and see table view with index.

11. Build and Run and see table view with index

To make app a little bit neat we can make table view go just below status bar. There are different ways of doing that. Most simple way – just embed table view controller into navigation controller. To do that choose ViewController in canvas. Then go in menu Editor -> Embed In -> Navigation Controller:

10

Build and run:

13

That’s it.

Leave a Comment:

8 comments
Manolo says November 18, 2015

Hi Chetana, nice tutorial, I am trying to adapt your code to use with a core data, instead of array. Do you have it? Thanks.

Reply
Reinhold says November 25, 2015

Hi!

Isn’t the code for splitting the array buggy because it doesn’t include the last section (letter)?

Reply
Edip says January 8, 2016

Hello,

I was trying to implement the code but i found an mistake in the algorithm.

It doesn’t get the last section which is Z for “Zhtomyr” in this case.

Reply
James says February 11, 2016

You algorithm has a bug for Lists with one entry.

Reply
David Ong says February 20, 2016

Nice tutorial and I am using it in my code. But, I encountered a small problem which caused the array item that has prefix begins with “Z” (Zhytomyr) doesn’t appeared in the table view.

I believed that the problem is occurred at this line of code
if (countElements(commonPrefix) == 0 )
because there have a different condition when reach the end of the array.

Reply
laxman says April 11, 2016

What is countElements in if statement.

Reply
Timur says May 6, 2016

In your array you have city “Zhytomyr”, in your results the last city is with index Y (York). Where is “Z” index ?

Reply
Josh Black says July 20, 2016

I notice that there is a city starting with “Z”but is is excluded from the index. With my implementation of your code, when I scroll down to the bottom of the list I see that no city starting with “Z” in in the main list either. I was unable to modify the code to make the Z-results appear. Thoughts?

Reply
Add Your Reply