iOS Mobile Table

From Xojo Documentation

(Redirected from UserGuide:iOS Table)

A Table is used to display lists of data. Tables can display information in sections, can have "accessories" to indicate that more information is available. Each row of a Table can display images and up to 2 lines of text. In addition you can customize the rows of a table to show pretty much whatever you want.

If you have created desktop or web projects, you can think of an Table as analogous to a single-column ListBox. A Table can have a single column (called a cell) that contains a main line of text and optional smaller detail text underneath it. It can also have an image that displays in front of the cell. For more sophisticated display you can also create your own custom table cells to display almost anything you want, including multiple columns and controls.

Below is a list of commonly used events, properties and methods. Refer to iOSMobileTable in the Language Reference for the complete list.


AccessoryPressed - Called when the Detail accessory for a row is tapped.

SelectionChanged - Called when a row is selected. You are provided the section and row that was tapped.

ApplyActionsForRow - Use this event to set up any row actions that are available for the row.

Refreshed - Called when the user initiates a "pull-to-refresh" action.

RowActionSelected - Called when a row action has been selected.

ApplyRowEditingStyle - Indicates the editing style for the row when the table is put into edit mode.


AllowRefresh - Enable usage of the "pull-to-refresh" action for the table.

DataSource - Use this configure a Table to gets its data from a data source class rather than populating it using the methods.

EditingEnabled - Indicates the table is currently in edit mode.

Format - Indicates how the table is displayed. There are two choices: Plain and Grouped.

iOS Table Sections Plain Style
iOS Table Sections Grouped Style

SectionCount - The number of sections in the Table. Use this in conjunction with the various methods to get row counts and other information about the table.

Visible - A boolean that indicates if the Table is visible when your app runs.


AddRow, AddRowAt - Adds new rows to the end or inserts rows at a specific position of the table.

AddSection, AddSectionAt - Add new sections to the end or inserts sections at a specific position of the table.

CreateCell - Creates new cells (of type MobileTableCellData) to display in the table.

CreateCustomCell - Creates new custom cells that are made using MobileTableCustomCell.

ReloadDataSource, ReloadDataInSection, ReloadRow - Reloads all the data in the table, just the data for a specific section or a single row.

RemoveAllRows, RemoveRowAt, RemoveSectionAt - Removes all rows in the table, a single row in a section or an entire section.

RowCount - The count of rows in the specified section.

RowCellData - Returns an instance of MobileTableCellData that contains all the information about a row in a section.

SectionTitleAt - Gets or sets the title of a section.

Cell Data

When working with a Table, you will need to manage its rows. The class MobileTableCellData contains information about a row in a table. To populate a table manually, you can create instances of MobileTableCellData (using iOSMobileTable.CreateCell or iOSMobileTable.CreateCustomCell), set its properties and then add it to the Table. Or you can add rows using just a subset of values manually.

Use the RowCellData method to get the MobileTableCellData for a specific row, which you can then use or modify to change what is displayed.

Below are commonly used properties. Refer to MobileTableCellData in the Language Reference for the complete list.


AccessoryType - The type of (optional) accessory to display for the row. This uses the AccessoryTypes enumeration to choose the type of accessory: None, Disclosure, Detail, Checkmark.

DetailText - The Detail Text is smaller text that displays below the main text for the row.

Image - When an image is specified, it appears in front of the row Text.

Tag - The Tag is a Variant that can be used to store any useful, related information about the cell for retrieval later (such as a primary key to a database or an instance of a class containing additional information).

Text - The main text that is displayed for the cell row.

Data Source

Although you can directly add rows to a Table using the AddRow methods, this is only practical for when there are a small number of rows. When you have 50 or more rows in the table you should consider using a Data Source to provide the data for the table to display. This makes much better use of the resources available on iOS.

iOSMobileTableDataSource is an Interface that you implement on a class that will provide the data to the Table. The table is then populated by the class so you do not have to use the AddRow/AddRowAt methods.

These are the methods of the interface that have to be implemented in a class:

RowCount - Returns the number of the rows in the specified section.

RowData - This method is used to create the MobileTableCellData for the specified section and row.

SectionCount - Returns the total number of sections.

SectionTitle - Returns the title for the specified section.


Starting simply, you can manually add rows to a table. A table always has to have at least one section, even if you don't display it. If you add rows without specifying a section number, an invisible section 0 will be created:

Table1.AddRow("First Row")

Using this version of the AddRow method only lets you provide the text for the row. If you need to set other row values, such as the DetailText or an Image, you will need to first create an MobileTableCellData, set its properties and then add it to the Table:

Var cell As MobileTableCellData
cell = Table1.CreateCell
cell.Text = "Product Name"
cell.DetailText = "Product Details"
cell.Image = ProductImage

AddRowAt works the same as AddRow but has an additional paramter to specify the row position within the section for the newly added row.

Using these methods, you can populate a table by looping through the data. For example, if you have an array of customer names, you can loop through them and populate the Table like this:


Table1.AddSection("") // add heading for first letter of name
For i As Integer = 0 To CustomerNames.LastRowIndex
iOS Table Showing Checkmark Accessory

To add rows with accessories, you have to use MobileTableCellData because that is where the AccessoryType property is. This code adds rows to a table and sets the accessory to a checkmark:

Var cellData As MobileTableCellData

For i As Integer = 0 To 50
cellData = Table1.CreateCell
cellData.Text = "Line " + i.ToString
cellData.AccessoryType = MobileTableCellData.AccessoryTypes.Checkmark

Table1.AddRow(0, cellData)

To toggle the checkmark on and off when the cell is tapped, you can put this code in the Table's AccessoryPressed event handler:

Var cell As MobileTableCellData
cell = Me.RowCellData(section, row)

If cell.AccessoryType = MobileTableCellData.AccessoryTypes.Checkmark Then
cell.AccessoryType = MobileTableCellData.AccessoryTypes.None
Me.ReloadRow(section, row)
cell.AccessoryType = MobileTableCellData.AccessoryTypes.Checkmark
Me.ReloadRow(section, row)
End If

This code gets the selected cell and tests if the accessory is a checkmark. If it is, then it sets the accessory to None. If there is no accessory, then it sets it to a checkmark. It then reloads the changed row to show the change to the accessory.

The AccessoryTypes enumeration has other accessories you can use instead: Disclosure, Detail and None. The default for cells is None.

If you use an AccessoryType of Detail then tapping the detail indicator calls the Table's AccessoryPressed event. In this event you can put the code that performs an accessory-specific action, which is typically to display a new Screen with additional details. One technique is to have the additional details stored in the Tag of the cell so that you can get it to send to the new Screen like this:

Var details As New DetailScreen
details.ApplyData(Me.RowData(section, row).Tag)

The RowCellData method is used to get the details of a cell for a specific row. You can then use the data or you can modify it to change what is displayed. After modifying cell data, you'll need to reload the row (or rows) that were modified. This Pressed event code changes the text of a row that was tapped:

Var cell As MobileTableCellData
cell = Me.RowCellData(section, row)
cell.DetailText = "This row was tapped."
Me.ReloadRow(section, row)

To display an image you also have to use MobileTableCellData. This adds a row using an image from the project:

Var cell As MobileTableCellData
cell = Table1.CreateCell
cell.Image = Product1Image
cell.Text = "Product 1"
Table1.AddRow(0, cell)

Sections can be used to provide ways to group the displayed data. One example might be to group a list of names by the first character of their name. So all names that start with "D" would be in their own group. Assuming you have an array of names, you can sort the array and then populate the Table, creating a new group when the first letter of the name changes:

Var names() As Text = Array("Anna", "Andy", "Bob", "Chris", "Chuck", "Dave", "Ed", _
"Fred", "Greg", "Harry", "Henry")

Var lastSectionTitle As String
Var sectionNum As Integer
For i As Integer = 0 To names.LastRowIndex
If names(i).Left(1) <> lastSectionTitle Then
lastSectionTitle = names(i).Left(1)
sectionNum = Table1.AddSection(lastSectionTitle)
End If
Table1.AddRow(sectionNum, names(i))

Using a Data Source

In each of the above examples, the Table was populated manually using the various Add methods. Another way you can populate a Table is by using a class that implements the iOSMobileTableDataSource interface as the DataSource. The class is then responsible for getting the data and returning it using the methods provided by the iOSMobileTableDataSource interface. This is a bit more work to set up, but does provide a noticable performance boost for large numbers of rows and it's a nice separation of the Table user interface and its data. The class you create can get the data from anywhere. It could have its data built-in, it could fetch data from a file, the Internet or a database.

To use a DataSource, you need to create a class that implements the iOSMobileTableDataSource interface (refer to the UserGuide:Interfaces section if you need to re-familiarize yourself with this). In your class, you have to implement the four interface methods: RowCount, RowData, SectionCount and SectionTitle.

iOS Table Names in Sections

As an exercise, this is how you would create a Data Source class to populate the names in a section as shown previously.

First, create a new class called "NamesData" and set add the iOSMobileTableDataSource interface to it. In this new class, add two properties:

Names() As String
SectionNames() As String

Add a Constructor to the class and add the code to initialize these properties:

Names = Array("Anna", "Andy", "Bob", "Chris", "Chuck", "Dave", "Ed", _
"Fred", "Greg", "Harry", "Henry")

Var lastSectionTitle As String
Var sectionNum As Integer
For i As Integer = 0 To names.LastRowIndex
If Names(i).Left(1) <> lastSectionTitle Then
lastSectionTitle = Names(i).Left(1)
End If

Now you can add the code to the four methods to return the correct information.

This is the code for the SectionCount method:

Return SectionNames.LastIndex + 1

This is the code for the SectionTitle method:

Return SectionNames(section)

This is the code for the RowCount method:

// Count the items in the array that start with the character
// of the specified section.
Var count As Integer
For i As Integer = 0 To Names.LastRowIndex
If Names(i).Left(1) = SectionNames(section) Then
count = count + 1
End If

Return count

This is the code for the RowData method:

// Find the appropriate item in the array
Var lookupRow As Integer
For i As Integer = 0 To Names.lastRowIndex
If Names(i).Left(1) = SectionNames(section) Then
// Found the start of this section, which now
// serves as an offset into the array.
lookupRow = i
Exit For
End If

Var cell As MobileTableCellData
cell = table.CreateCell
cell.Text = Names(lookupRow + row)

Return cell

This sets up the Data Source. Yes, it is a bit more complicated, but it eliminates the need for any code on the Screen to populate the Table and it will be much faster when there are a lot of rows. To set up the Table, go to Screen1 and add a Table (Table1). Also add a property:

Names As NamesData

In the Opening event handler for the Screen, create a new instance of NamesData and assign it to the DataSource for the Table:

Names = New NamesData
Table1.DataSource = Names

You may be wondering why it is necessary to have the Names property to store the NamesData instance. This is so that the instance does not go out of scope, preventing its data from being loaded into the table.

Now when the table is displayed, it will request only the rows that need to be shown from the data source.

Example Project: iOS/Controls/Table/SimpleTableExample

More Loading Data From a Data Source

Baseball Teams Shown in an iOS Table Using a Data Source

Although loading data manually using the AddRow method is quick and easy, performance can really start to suffer once a lot ( > 100) of rows have been added. So in most cases you are going to want to use a separate data source to provide the data for the table to display. To provide a data source, you create a class that implements the iOSMobileTableDataSource interface and then use its methods to provide data from whatever your source of data is. This data can be in a file, a database, text or anything else you want.

The example below displays MLB teams using a data source.

The first thing to do is to create a new class (named BaseballTeams). In the Inspector, click the Choose button for the Interfaces property and select the iOSMobileTableDataSource interface. This will add these methods to your class:

  • RowCount
  • RowData
  • SectionCount
  • SectionData

But before you add any code to those methods, you first want to set up the actual data you will display. There are 30 MLB teams, separated into six divisions. A simple way to manage this data is to use a Dictionary for the divisions and then for each division in the Dictionary, store an array of the team names. To start, create a property on the class:

Divisions As Dictionary

Now add a Constructor method with the code to populate the Dictionary:

Divisions = New Dictionary

Var ALEast() As String = Array("Red Sox", "Yankees", "Rays", "Orioles", "Blue Jays")
Var ALCentral() As String = Array("Tigers", "White Sox", "Twins", "Royals", "Indians")
Var ALWest() As String = Array("Angels", "Mariners", "Rangers", "Athletics", "Astros")

Var NLEast() As String = Array("Mets", "Braves", "Nationals", "Marlins", "Phillies")
Var NLCentral() As String = Array("Cardinals", "Brewers", "Reds", "Pirates", "Cubs")
Var NLWest() As String = Array("Dodgers", "Pardres", "Giants", "Diamondbacks", "Rockies")

Divisions.Value("AL East") = ALEast
Divisions.Value("AL Central") = ALCentral
Divisions.Value("AL West") = ALWest

Divisions.Value("NL East") = NLEast
Divisions.Value("NL Central") = NLCentral
Divisions.Value("NL West") = NLWest

Now you'll want one other helper method to get the name for a specific section number. Create a new method:

GetSectionName(section As Integer) As String

And use this code:

Var name As String
Select Case section
Case 0
name = "AL East"
Case 1
name = "AL Central"
Case 2
name = "AL West"
Case 3
name = "NL East"
Case 4
name = "NL Central"
Case 5
name = "NL West"
End Select

Return name

With this all set up, you can now populate the interface methods. The RowCount method returns the number of rows in a specific section. This is the number of rows in the array that belongs to the Dictionary key for the section. You get it like this:

Var teams() As String
teams() = Divisions.Value(GetSectionName(section))
Return teams.Count

The SectionCount method returns the number of sections. This is simply the count of keys in the Dictionary:

Return Divisions.Count

The SectionTitle method returns the name for a specific section. You can use the GetSectionName method get the name:

Return GetSectionName(section)

Lastly, this leaves the RowData method. Here you provide the data to display in the form of an MobileTableCellData instance. This code gets the team name for the appropriate section and row and sets it as the Text for the cell data:

Var teams() As String
teams() = Divisions.Value(GetSectionName(section))

Var cell As MobileTableCellData = table.CreateCell
cell.Text = teams(row)

Return cell

And that's it to set up the data source. The last step is to add a Table to a Screen and tell it to use your new data source. On the Screen, you'll want to add a property to reference the data source:

Teams As BaseballTeams

And then in the Open event handler for the Table, you can create an instance of your data source and assign it to the table:

Teams = New BaseballTeams
Me.DataSource = Teams

Now you can run the project and you'll see the MLB team names grouped by division.

Example Project: iOS/Controls/Table/TableDataSource

Getting the Selected Row

When a row in the table is tapped, the SelectionChanged event handler is called passing in the section and row that were selected. Use the RowData method to get the data in the cell for the selected row:

Var cell As MobileTableCellData = Me.RowData(section, row)

Row Actions

Row actions are the buttons that appear when a user swipes left across a row of a table. For example, if you swipe left across an email message in the Mail app, you'll see "More", "Flag" and "Trash" buttons. Xojo row action buttons can only contain text (not icons) and are added using the iOSMobileTableRowAction class in conjunction with the iOSMobileTable.ApplyActionsForRow event. When the user selects one of the buttons, then the iOSMobileTable.RowActionSelected event is called.

Row actions only works for tables that use the iOSMobileTableDataSourceEditing interface. In the interface implementation you will load your data as described above and in the RowIsEditable method you'll need to Return True.

To set up row actions, you specify them in the ActionsForRow method. This code adds Flag and Archive actions for all rows:

Var Actions(1) As iOSMobileTableRowAction
Const kFlagTag As String = "Flag"
Const kArchiveTag As Text = "Archive"

// Create the Flag button
Actions(0) = New iOSMobileTableRowAction(iOSMobileTableRowAction.Styles.Normal, "Flag", kFlagTag)

// Create the Archive button
Actions(1) = New iOSMobileTableRowAction(iOSMobileTableRowAction.Styles.Destructive, "Archive", kArchiveTag)

Return Actions

The "destructive" action appears on the far right and displays as red.

You then check which row action was selected in the RowActionSelected event handler:

Me.ReloadRow(section, row) // Reload the row to clear the action
Select Case actionTag
Case "Flag"
// Call flag code
Case "Archive"
// Call archive code
End Select

The user can choose to "cancel" a row action by tapping anywhere but on one of the buttons.

Example Project: iOS/Controls/Table/TableActions

Row Editing and Re-Ordering

Both row editing and row re-ordering are enabled by putting the table into "editing" mode. You do this by setting the EditingEnabled property to True. Row editing displays "+" and "-" widgets to the left or each row that can be used to add and remove rows in the table. Row re-ordering displays a "drag" control to the right of each row that you can drag around to move the row in the table. Putting the table into edit mode is simple. You'll probably have an "Edit" button on the Navigation toolbar (called EditButton) with code like this to turn editing on an off:

Select Case button
Case EditButton
If SampleTable.EditingEnabled Then
SampleTable.EditingEnabled = False
EditButton.Caption = "Edit"
SampleTable.EditingEnabled = True
EditButton.Caption = "Done"
End If
End Select

Row Editing is supported by using the iOSMobileTableDataSourceEditing data source interface and row re-ordering uses the iOSMobileTableDataSourceReordering interface. This means that in order to use these two features, you table must be populated from a data source. Both of these two additional interfaces aggregate (or extend) the iOSMobileTableDataSource interface, so several methods are shared. And remember, you can select multiple interfaces for your data source class. So to support a data source, row editing and row re-ordering, you can select these three interfaces in the interface selector for your class: iOSMobileTableDataSource, iOSMobileTableDataSourceEditing, iOSMobileTableDataSourceReordering.

Technically, because iOSMobileTableDataSouceEditing and iOSMobileTableDataSourceReordering "aggregate" iOSMobileTableDataSource, you do not actually have to separately select iOSMobileTableDataSource. But you may want to do so for clarity.

Row Editing

The iOSMobileTableDataSourceEditing interface adds two additional methods to the iOSMobileTableDataSource interface that you need to implement: RowIsEditable and RowEditingCompleted.

Before worrying about the interface implementation, you need to specify the editing style that appears for each row when the table is put into edit mode. You do this by returning an iOSMobileTable.RowEditingStyles enumeration from the iOSMobileTable.RowEditingStyle event handler. This code sets all rows to display the deletion control when the table is edited:

Return iOSMobileTable.RowEditingStyles.Delete

Moving on to the interface implemention, you want to Return True from RowIsEditable to allow the row to display the editing controls when the table is put into edit mode:

Return True // allow all rows to be edited

Because this method supplies the section and row numbers as parameters, you can choose to only allow row editing for specific sections and rows by only returning True for those that match your criteria. For example, this only enables row editing for rows in section 2:

If section = 2 Then
Return True // Only rows in section 2 can be edited
Return False
End If

Also supplied to the method is a reference to the table itself. You can use this to look up the contents of the row to determine if it contains something you want to allow to be deleted. For example, you may want to add a tag value to some of the cell data to indicate that it is default data and should not be deleted.

When the users selects one of the editing buttons to delete the row, then the RowEditingCompleted method is called in your class. Use the iOSMobileTable.RowEditingStyles enum to determine which button was selected. This code checks if the row was selected to be deleted and then removes it from the data source:

If action = iOSMobileTable.RowEditingStyles.Delete Then
table.RemoveRow(section, row)
End If

It is important to realize that you have to specifically remove the row from your data source. In the above code that means removing it from the array containing the data, but if you are using something else (such as a database), you'll want to remove the row as appropriate. You also have to specifically remove the row from being displayed in the table by calling its RemoveRow method as shown in the above code.

Example Project: iOS/Controls/Table/TableEditing

Row Re-Ordering

Row re-ordering works similarly to row editing. You add the iOSMobileTableDataSourceReordering interface to your data source class which adds two new methods: RowIsMoveable and RowMoved.

In the RowIsMoveable method, you return True for the rows you want to allow to be moved. In most cases you'll probably want to allow all rows to be moved:

Return True // Allow all rows to be moved/re-ordered

The RowMoved event handler is called after a user drags a row to move it and then drops it into its new position. In this method, you need to update your data source to reflect the new state of the data. The table user interface is automatically updated for you, so you may be deceived into thinking you don't need to do any other work. But it is important that your data source is properly updated to reflect the new ordering. This code uses an array (a property called Data) as the source of the data that is displayed in the table. It removes the item from its original location in the array and then inserts the item at its new position so that the array continues to match the order of the data that is now displayed in the table:

// Remove row from its original location in the Data array
Var origValue As String = Data(sourceRow)

// Add it to its new location in the Data array
Data.AddAt(destRow, origValue)

Example Project: iOS/Controls/Table/TableEditing

Custom Cell Data

To really get fancy with what you display in your tables, you'll want to create custom cells. With a custom cell, you can put any control you want into a cell. To do this you create an MobileTableCustomCell with the actual controls you want displayed in each cell. Then you create a custom cell for the table, supplying the custom table cell class. MobileTableCustomCell is a subclass of MobileContainer and allows you to add and layout any controls you want using the Layout Editor.

Custom cells are incredibly powerful. You can create multiple columns in a table by creating a column with several labels lined up in a column. Using a label also allows you to change the font, size and other properties of the displayed text. But you don't have to stick with simple text. You can also add user interface controls such as Switches, Buttons or anything else you want to show.

For best results you will really want to use a DataSource to populate the table. You can get away with adding the rows yourself manually, but only if your table has just a few rows.

When using a data source, it is important to understand that the instances of the container are re-used as necessary. This means that you want to be sure to properly set (or reset) all the values of the container each time you set up the RowData. For example, if you change the font for a label in one container, but do not reset it, then that changed font could end up being used for a completely different set of data that is displayed. Below is an example that updates the MLB team data example from above to use custom table cells.

The first thing to do before writing any code is to create the custom table cell itself. Go to the Library and drag an MobileTableCustomCell to the Navigator to add a subclass to your project.

Instead of dragging Custom Table Cell from the Library, you can add a Container Control to your project and change its Super to MobileTableCustomCell.

Change its name to TeamTableCell. As this is a subclass of MobileContainer, you'll see a Layout Editor where you can place controls. For the purposes of this example, you'll add a single Label (TeamNameLabel) to display the team name, a Button (TestButton) to display a cheer for the team and an ImageViewer (LogoImage) to just show some simple graphics.

Your layout might look like this:

iOS Custom Table Cell Layout.png

Now you can go to the RowData method on the BaseballTeams data source class your created in the data source example. Get rid of the existing code that is there and replace it with this code that adds a team custom cell to the table, assigning the name to the label and setting the TeamLogo to a simple filled oval:

Var teams() As String
teams() = Divisions.Value(GetSectionName(section))

Var cell As MobileTableCellData = table.CreateCustomCell(GetTypeInfo(TeamTableCell))
Var container As TeamTableCell = TeamTableCell(cell.Control)

container.TeamNameLabel.Text = teams(row)

// Draw a blue oval
Var logo As New Picture(64, 64, 1.0)
logo.Graphics.DrawingColor = &c0000ff
logo.Graphics.FillOval(10, 10, 50, 50)
container.TeamLogo.Image = logo.Image

Return cell

The trickiest part of this code is that you have to use GetTypeInfo to pass in the type of your class. You do this rather than passing in an instance of the class because the instances are re-used by the table to make things more efficient.

This example showed you how to use a few controls in a MobileTableCustomCell to display whatever you want in your iOSMobileTables. Remember, you can use any control you want. The table will resize to fit the controls in your layout.

If your custom table cells have different sizes you can set the MobileTableCustomCell.AllowDynamicHeight property to true so that the table resizes the row as necessary.

Example Projects: iOS/Controls/Table/CustomCellsAndScroll, CustomCellDynamicHeight

Example Projects

These example projects demonstrate various features of iOS Tables:

  • Examples/iOS/Controls/Table/CustomCellDynamicHeight
  • Examples/iOS/Controls/Table/CustomCellsAndScroll
  • Examples/iOS/Controls/Table/GroupTableExample
  • Examples/iOS/Controls/Table/SectionTableExample
  • Examples/iOS/Controls/Table/SelectableTable
  • Examples/iOS/Controls/Table/SimpleTableExample
  • Examples/iOS/Controls/Table/TableActions
  • Examples/iOS/Controls/Table/TableCellSizes
  • Examples/iOS/Controls/Table/TableCheckmark
  • Examples/iOS/Controls/Table/TableDataSource
  • Examples/iOS/Controls/Table/TableDetail
  • Examples/iOS/Controls/Table/TableDisclosure
  • Examples/iOS/Controls/Table/TableEditing
  • Examples/iOS/Controls/Table/TableDataSourceDatabase
  • Examples/iOS/Apps/XojoNotes
  • Examples/iOS/Database/SQLiteExample
  • Examples/Sample Applications/EddiesElectronics/iOS/EEiOS

See Also

iOSMobileTable, MobileTableCellData, MobileTableCustomCell, iOSMobileTableRowAction classes; iOSMobileTableDataSource, iOSMobileTableDataSourceEditing, iOSMobileTableDataSourceReordering interfaces