iOS

How To Create an Expandable Table View in iOS


A common feature of almost all apps is the fact that they provide multiple view controllers to users to navigate and work with. Those view controllers can be used in many ways, such as to simply display some kind of information on-screen, or to gather complex data from user input. Creating new view controllers for different functionalities of an app is often mandatory, and several times a little bit daunting task. However, sometimes it’s possible to avoid creating new view controllers (and their respective scenes in storyboard) if you just make use of expandable tableviews.

As the term suggests, an expandable tableview is a tableview that “allows” its cells to expand and collapse, showing and hiding that way other cells that in any other case would be always visible. Creating expandable tableviews is a nice alternative when it’s required to gather simple data, or to display on-demand information to users. With them, it’s not necessary to create new view controllers just to ask users for data that in any case should exist in one, default view controller. For example, with expandable cells you can show and hide options of a form that collects data without having to leave that view controller at all.

expandable-uitableview

Whether you should use an expandable tableview or not always depends on the nature of the app you develop. However, as the UI of the cells can be customized by subclassing the UITableViewCell class and creating extra xib files, the look and feel of the app should normally not be a problem. So, at the end, it’s just a matter of requirements.

In this tutorial I’m going to show you a simple but efficient way to create expandable tableviews. Note that what you’ll see here does not consist of a unique method for implementing such a feature. Pretty much the implementation is based on the app needs, but my goal here is to present a quite general way that can be reused in most cases. So, by having said that, move to the next part to get a taste of what we’re about to deal with in this tutorial.

About the Demo App

We are going to see how an expandable tableview is created and works by implementing an app with one view controller, which in turn will contain the tableview. We are going to make a fake form to let user enter data, and for that reason the tableview will contain the following three sections:

  1. Personal
  2. Preferences
  3. Work Experience

Each section is going to contain expandable cells that will trigger the display or hiding of other, additional cells inside the section. Specifically, the top-level cells (those that will expand and collapse) for each section are:

For the “Personal” section:

  1. Full name: It displays the full name of the user, and when it is expanded two sub-cells are available for typing the first and last name.
  2. Date of birth: It displays the date of birth of the user. When it’s expanded, a date picker view is provided for selecting a date, and a button to set it as the selected one to the respective top-level cell.
  3. Marital status: This cell displays whether the user is married or single. When expanded, a switch control is provided for setting the marital status.

For the “Preferences” section:

  1. Favorite sport: Our fake form is supposed to ask for the favorite sport of the user, which is displayed here when selected. When this cell is expanded, four options with sport names become available, and when a sub-option is tapped the cell will “automatically” collapse.
  2. Favorite color: Pretty much same as above, this time though we’ll display three different colors to let the user select from.

For the “Work Experience” section:

Level: When this top-level cell is tapped and expanded, another cell with a slider appears to let user specify a hypothetic work experience level. The allowed values are within the 0…10 range, and we’ll keep only integer values.

The following animated graphic will make clear what we’re about to do:

t45_7_expand_collapse

You notice above that there are various types of cells when expanding the tableview. All of them can be found in the starter project that’s available for you to download, among some other pre-made stuff. All custom cells have been designed in separate xib files, while a custom subclass of the UITableViewCell class (named CustomCell) has been assigned as their custom class:

t45_2_custom_class

Inside the project you’ll find the following cell xib files:

t45_3_cell_list

Their names suggest what each cell is for, but you can get the starter project and explore deeper on your own.

Besides the cells, you’ll also find some code already been implemented. Even though it’s important and completes the functionality of the demo app, that code does not consist of core part in this tutorial, so I just skip to go through it and provide it as already made. All the missing code we’re interested about in this tutorial will be added step by step as we move through the following parts.

So, now you know what our final goal is, therefore we are ready to learn how to create an expandable tableview.

Describing the Cells

All the implementation and the techniques that I’m going to present in this tutorial regarding the expandable tableview lies to a single and simple idea: To describe the details of each cell to the app. That way is possible to let it know which cells are expandable or not, which are visible or not, what’s the value for the text label of each cell, and so on. Actually, the whole idea is based on grouping a bunch of properties that either describe attributes or contain certain values for each cell, and then making them available to the app so it properly displays them.

For this demo app I created and used the properties shown in the next list. Note that new properties can be added in a real app, or the existing ones could be modified as per demand. In any case, the important is you manage to get the big picture here. Then you’ll be able to perform all the changes you desire. The properties list now:

  • isExpandable: It’s a bool value that indicates whether a cell can expand or not. It’s one of the most important properties for us in this tutorial.
  • isExpanded: A bool value again, indicating whether an expandable cell is expanded or collapsed. The top-level cells will be collapsed by default, so initially this value will be set to NO for all of them.
  • isVisible: As the name suggests, indicates whether a cell is visible or not. It’s going to play a significant role a little bit later, as we’ll be based on that property so we display the proper cells in the tableview.
  • value: This property is useful for keeping the value of UI controls (for example, the value of the marital status switch control). Not all cells have such controls, so for most of them this property will remain empty.
  • primaryTitle: It’s the text displayed in the main title label of the cells, and several times contains the actual value that should be displayed on a cell.
  • secondaryTitle: It’s the text displayed either in the subtitle label of a cell, or in secondary labels.
  • cellIdentifier: It’s the identifier of the custom cell matching to the current description. It won’t be used by the app just to dequeue the proper cell, but also to determine the proper actions that should be taken depending on the displayed cell, and to specify the height of each cell.
  • additionalRows: It contains the total number of additional rows that should be displayed when an expandable cell is expanded.

The above collection of properties will be used to describe each single cell that we’re about to have in the tableview. In app-level terms, we’re going to do that easily simply by making use of a property list (plist) file. In this plist file, we’ll properly fill the values of the above properties for all cells, and that way we’ll end up having a full technical description of them that can be used both from us and the app. And all that without writing a single line of code. Isn’t that great?

At this point we would normally create a new property list file in our project, and then we would start populating it with the proper data. But there’s no reason to do that, as you can just download the .plist file. So, get it and add it to the starter project. Setting the properties for all cells here would take a lot of space, it would be meaningless, and it would be also tiring and boring for you to copy-paste or type all the missing values. However, let’s discuss about it a little bit:

First of all, the file you (hopefully) downloaded is called CellDescriptor.plist. The base structure (root) is an array, where each item in it represents a section in the tableview. That means that the base array in the plist file contains three items, as many as the sections we want to exist in the tableview are.

The above items that represent the sections are also arrays, and their own items describe the cells of each section. Actually, the properties presented above are grouped as dictionaries, and each dictionary matches to a single cell representation. Here’s a sample of the property list file:

t45_4_plist_sample

Now it’s the best time to take your time and see a bit more thoroughly the properties and their values for all those cells that we’re going to display in the tableview. It’s easy to understand that by having that cell description at our disposal the required code we need to write for creating and managing expandable cells is significantly decreased, and that we won’t have to dictate the app about the various states of the cells (for example, which cells are expandable, whether it should allow the expansion of a specific cell or not, determine in code if a cell is visible or not, and so on). All that information exists in the property list file you just downloaded.

Loading the Cell Descriptions

It’s time finally to write some code, as even though we save a lot of it by using the cell description technique, there’s still code needed to be added to the project. Now that the cell description property list file exists, the first thing we must programmatically do is to load its contents into an array. That array will be used in the next part as the datasource for the tableview.

For starters, open the ViewController.swift file in the project, and at the top of the class declare the following property:

This array will contain all the cell description dictionaries that will be loaded from the property list file.

Next, let’s implement a new custom function that’s responsible for loading the file contents into the array. We’ll call that function loadCellDescriptors():

What we do here is quite simple: We first make sure that the path to the property list file in the bundle is valid, and then we initialize the cellDescriptors array by loading the file contents.

Calling the above function is the next step, and we’ll do that right before the view gets appeared and after the tableview has been configured (we want the tableview already set up before we display any data on it):

If you type the print(cellDescriptors) command right after the last line in the above snippet and run the app, you’ll see all the plist file contents getting listed in the console. That means that they’ve been successfully loaded in memory.

t45_5_console_plist

Normally, our work in this section should finish now, but we won’t do that; we’ll make one more addition that will be proved vital in the next part. As you’ve found out so far (especially if you’ve examined the CellDescriptor.plist file), not all cells are going to be visible at app launch. Actually, we can’t just know if they’ll ever be visible all together simultaneously, because they can be expanded or collapsed as per user demand.

In the programming world, that means that the row index of each cell cannot be constant (what we programmatically write as indexPath.row when handling cells), therefore we can’t just go through our datasource array using the cell row and display each cell. It’s mandatory to work that out and come up with a solution that will provide the row index of the visible cells only. Any attempt to display cells that have been marked in the description as not visible would lead to a faulty implementation, and of course, to abnormal app behavior.

So, for that reason we’re going to implement a new function called getIndicesOfVisibleRows(). Its name states clearly its purpose: It’s going to get the row index values for the cells that have been marked as visible only. Before we proceed to that implementation, please go once again at the top of the class to make the following declaration:

This two-dimensional array will store the index of the visible cells for each section (one dimension for the sections, one for the rows).

Let’s focus now on the implementation of the new function. As you probably guess, we’ll go through all cell descriptors and we’ll add in the above 2D array the index of the cells that their “isVisible” property is set to YES. Obviously we have to deal with a nested loop, but it’s not difficult to handle it. Here’s the function implementation:

Note that at the beginning it’s required to remove any previous contents from the visibleRowsPerSection array, otherwise we’ll end up having wrong data in it after subsequent calls of this function. Further than that, the implementation is straightforward, so I avoid commenting anything further.

The first time that the above function should be called is right after the cell descriptors get loaded from the file (we’ll call it again later too). So, revisiting the first function we implemented in this part, we modify it as follows:

Even though the tableview isn’t working yet, we trigger a reload action in advance so we make sure that the proper cells will be displayed after launching the app.

Displaying the Cells

Knowing that the cell descriptions are loaded every time the app gets launched, we’re ready to proceed and display the cells in the tableview. We are going to begin in this part by creating another new function that will locate and return the proper cell descriptor from the cellDescriptors array. As you will see in the following code snippet, having populated data in the visibleRowsPerSection array is a prerequisite for this new function to work.

The parameter that the above function accepts is the index path value (NSIndexPath) of the cell that is currently being processed by the tableview, and it returns a dictionary containing all the properties matching to that cell. The first task in its body is to find the index of the visible row that matches to the given index path, and that’s easy to do as all we need is the section and row of the cell. At the moment you don’t have the full picture as we haven’t dealt at all so far with the tableview delegate methods, so I must say in advance that the total number of rows per section will match to the number of visible cells in every section. That means that any indexPath.row value in the above implementation matches to the proper visible cell index in the visibleRowsPerSection.

By having the index of the row for each cell, we can proceed and “extract” the cell description dictionary from the cellDescriptors array. Note that the value specified as the index of the second dimension of that array is the indexOfVisibleRow, and not the indexPath.row. Using the second would lead to return false data.

Once again we built a useful tool that will be proved pretty handy right next, so let’s get going by fixing the existing tableview methods in the ViewController class. Initially, let’s specify the number of sections in the tableview:

You understand that we could not just overlook the case where the cellDescriptor array would be nil. We return the number of its sub-arrays if only it has been initialized and filled with the cell description values.

Then, let’s specify the number of rows per section. As I said before, that number is always equal to the number of visible cells, and we can return that information in just one line:

After that, let’s determine the titles for the sections of the tableview:

Next, it’s time to specify the height of each row:

There’s something that I’d like to underline here: For first time we make use of the getCellDescriptorForIndexPath: function that was implemented earlier in this part. We need to get the proper cell descriptor, because right next it’s necessary to fetch the “cellIdentifier” property, and depending on its value to specify the row height. You can verify the height values for each type of cell in the respective xib files (or just take them as granted as shown here).

Lastly, the actual cell display. Initially each cell must be dequeued:

Once again we begin by getting the proper cell descriptor based on the current index path value. By using the “cellIdentifier” property the correct cell is being dequeued, so we can dive deep down to the special treatment of each cell:

For normal cells, we just set the primaryTitle and secondaryTitle text values to the textLabel and detailTextLabel labels respectively. In our demo app, the cells with the idCellNormal identifier are actually the top-level cells that get expanded and collapsed.

For cells containing a textfield, we just set the placeholder value that is specified by the primaryTitle property of the cell descriptor.

Regarding the cell that contains a switch control, there are two things we do here: We firstly specify the displayed text before the switch (this is constant in our case and can be changed in the CellDescriptor.plist file), and then we set the proper state of the switch, depending on whether it’s been set as “on” or not in the descriptor. Note that later we’ll be able to change that value.

There are also the cells having the “idCellValuePicker” identifier. Those cells are meant to provide a list of options, and when an option is selected the parent cell will automatically collapse. The cell’s text label is specified in the case shown above.

Lastly, there’s the case of the cell containing a slider. Here we just get the current value from the currentCellDescriptor dictionary, we convert it into a float number, and we assign it to the slider control so it shows the proper value all the time (when it’s visible). Later on we’ll change that value and we’ll update the respective cell descriptor.

For the cells for which the identifiers wasn’t explicitly added as a case in the above statement there’s nothing to do in this demo app. However, if you want to handle them in a different way, feel free to modify the code and add any missing parts.

Now you can run the app and see some results for first time. Don’t expect to see much, as what you’ll get is the top-level cells. Don’t forget that we still haven’t enabled the expanding feature, so nothing will happen if you tap on them. However, don’t get discouraged, because what you’ll see means that what we’ve done so far it’s working perfectly.

t45_6_top_level_cells

Expanding and Collapsing

I assume that this part is probably what you’ve been expecting the most, as the actual goal of this tutorial will be achieved here. For first time we’ll manage to make our top-level cells expand and collapse every time they get tapped, and the proper sub-cells appear or hide on demand.

For starters we need to know the index of the tapped row (remember, not the actual indexPath.row but the row index in visible cells only), so we’ll begin by assigning it to a local variable in the following tableview delegate function:

Even though no much code is required to make our cells expanding and collapsing, we’ll go step by step so I’m thorough enough and you get the proper meaning of our actions. Now that we have the actual index of the tapped row, we must check in the cellDescriptors array if the specific cell is expandable or not. In case it is expandable, but not expanded yet, then we’ll indicate (we’ll use a flag value) that the cell must expand, otherwise we’ll indicate that it must collapse:

Once the above flag gets its value and properly indicates whether the cell should expand or not, it’s our duty to save that value to the cell descriptors collection, or in other words, to update the cellDescriptors array. We want to update the “isExpanded” property of the selected cell, so it will behave correctly in subsequent tapping (to collapse if it’s expanded, and to expand if it’s collapsed).

There’s one really important detail we should not forget at this point: If you recall, there’s one property that specifies whether a cell should be displayed or not, named “isVisible” and exists in the description of each cell. This property must be changed in accordance to the above flag, so the additional invisible rows to become visible when the cell is expanded, and to become hidden again when the cell collapse. Actually, by changing the value of that property we actually achieve to produce the effect of the expansion (and back). So, let’s modify the snippet above by updating the additional cells that follow the top-level one that was just tapped:

We’re just a breath away from seeing in action the functionality we’re looking for, but first we must take care of one more important thing: In the code snippet right above we just changed the “isVisible” value of some cells, and that means that the total number of visible rows have changed as well. So, before we reload the tableview, we must ask the app to find the index values of the visible rows from scratch:

As you see I use an animated way to reload the section that the tapped cell belongs to, but you can change that if you don’t like the way it is.

Go and try the app now. The top-level cells expand and collapse on subsequent tappings, and even though nothing happens yet when interacting with the sub-cells, the result looks quite impressive!

t45_7_expand_collapse

Picking Values

From now on we can totally focus on handling the data input and user interaction with the controls of the sub-cells. We’ll begin doing so by implementing the logic that must be put in action when a cell with the “idCellValuePicker” identifier is tapped. In our demo app, those are the cells that list the favorite sports and colors in the “Preferences” section of the tableview. Even though I’ve mentioned about it already, I think that it would be a good idea to refresh our memory and say once again that when such a cell is tapped, we want the respective top-level cell to collapse (and hide the options), and the value shown to the selected cell to be displayed to the top-level cell as well.

The actual reason for which I chose to start dealing with this type of cells is the fact that we’ll continue working in the tableview delegate function of the last part. In it, we’ll add an else clause to handle the case of non-expandable cells, and then we’ll check the identifier value of the tapped cell. If the identifier equals to the “idCellValuePicker” then we have a cell we actually interested in.

Inside the inner if case we’ll perform four distinct tasks:

  1. We’ll find the row index of the top-level cell that is supposed to be the “parent” cell of the tapped one. In truth, we’ll perform a search towards the beginning of the cell descriptors and the first top-level cell that is spotted (the first cell that is expandable) is the one we want.
  2. We’ll set the displayed value of the selected cell as the text of the textLabel label of the top-level cell.
  3. We’ll mark the top-level cell as not expanded.
  4. We’ll mark all the sub-cells of the found top-level one as not visible.

Let’s everything in code now:

Once again we modify the “isVisible” property of certain cells, therefore the number of the visible rows gets changed. Obviously the call to the last two functions at the end is required.

If you run the app now, you’ll see how the app reacts when selecting a favorite sport or color.

t45_8_select_preferences

Responding to Other User Actions

In the CustomCell.swift file you can find the CustomCellDelegate protocol with all the required delegate methods already been declared. By implementing them in the ViewController class we’ll manage to make the app responding in all the other missing user actions.

Let’s turn to the ViewController.swift file again, and initially let’s adopt the above protocol. Go to the top of the class, and add it to the class header line:

Next, in the tableView:cellForRowAtIndexPath: function we must make the ViewController class the delegate of each custom cell. Go there, and right before the function body closing add the line shown below:

Great, now we can start implementing the delegate functions. We’ll begin by displaying the selected date in the date picker view to the respective top-level cell:

Once we specify the number of the proper section and row, we directly set the selected date as a string. Note that this string is a parameter in the delegate method.

Next, let’s handle the cell with the switch control. There are two things we need to do when changing the switch value: First, to set the proper value (“Single” or “Married”) to the respective top-level cell, and to update the switch’s value to the cellDescriptors array, so it has the proper state when the tableview gets refreshed. In the next code snippet, you’ll notice that we first determine the proper values based on the switch state, and then we assign them to the respective properties:

The textfield cells come next. Here we’ll compose the full name dynamically, once the first or last name has been typed in. For our needs, we’ll need to specify the row index of the cell that contains the textfield, and based on that to add the given value to the full name (first name comes first, last name comes second). At the end we update the displayed text (full name) of the top-level cell and we refresh the tableview so as to reflect all changes:

Lastly, there is the cell with the slider control under the “Work Experience” section that we have to take care of. When the user changes the slider value, we want two things to happen at the same time: To update the top-level cell’s text label with the new slider value (“experience level” in our app), and to store the slider’s value to the respective cell descriptor so it remains up to date even after having refreshed the tableview.

The final missing piece of the puzzle was just added, so go ahead and give the app a last try.

Summary

As I said in the beginning, creating expandable tableviews can be sometimes really helpful, as it saves you from the trouble to create new view controllers for app parts that could be handled in such kind of tableviews. In the previous parts of this tutorial I presented you an approach for making expandable tableviews where the main characteristic of it is the description of all cells using specific properties in a property list file (plist). I showed how you can handle this cell description list in code when displaying, expanding and selecting cells; additionally I gave you a way to directly update it with the data entered by the user. Even though a form like the fake one in our demo app could exist in a real app, there are still things needed to be done before it stands as a complete component (for example, save the cell description list back to file). However, that’s out of our goal here; what we initially wanted was to implement an expandable tableview with cells that appear or hide on demand, and that’s what we eventually did. I’d like to believe that you’ll find all the information in this tutorial useful. Definitely you’ll discover ways to improve the given code, or adapt it according to your needs. With that it’s time to leave you; have fun, and never stop experimenting!

For reference, you can download the complete Xcode project at GitHub.

Tutorial
How to Use Xcode Targets to Manage Development and Production Builds
iOS
Storyboards Segue Tutorial: Pass Data Between View Controllers
iOS
A Beginner’s Guide to Access Levels in Swift
  • Fengson

    FengsonFengson

    Author Reply

    Download link for plist is missing 🙂 Other than that, great tutorial, thanks!


  • red

    redred

    Author Reply

    When I try to add your example in my code I have

    let cellDescriptor = cellDescriptors[indexPath.section][indexOfVisibleRow] as! [String: AnyObject]

    Ambiguous use of ‘subscript’

    How can I fix this?


    • red

      redred

      Author Reply

      changed to let cellDescriptor = (cellDescriptors[indexPath.section] as! NSMutableArray)[indexOfVisibleRow] as! [String: AnyObject] – it works


      • Fatue5

        Fatue5Fatue5

        Author Reply

        Red – thank you for posting this! Helped me out a bunch.


      • David West

        David WestDavid West

        Author Reply

        Thanks also. Very useful!


    • David West

      David WestDavid West

      Author Reply

      Thanks also! Very useful.


  • Cor Brink

    Cor BrinkCor Brink

    Author Reply

    Hi Gabriel. Thanks for a great and most helpful tutorial. This came at the right time for me and is just what I needed.
    I have one request/query. Is there a way to dismiss/hide the subrows with the picker/slider views when the user has made their selection? I guess similar to view.hidden = true in DidSelect method but this does not seem to work.
    If this can be implemented I assume you’ll add it in CustomCell.swift?

    Will appreciate your feedback.


  • Cor Brink

    Cor BrinkCor Brink

    Author Reply

    Hi Gabriel, I completed the demo app and it runs fine without issues. When implementing the code in another project I get the following error 8 times:
    “Ambiguous use of ‘subscript’.
    It all seems to do with the NSMutableArray. What bugs me is that the demo runs without issues and I don’t have an idea how to fix this.
    Do you know of a way to fix this?


  • Marc Ibrahim

    Hello, thanks for this tutorial. However, i have some questions: why are the full name and last name input switching when hiding and re expanding the cells? How can i adjust them? And finally, how can i save the data entered by the user and use it? Thanks.


    • Brock Elkins

      I would like to know both these answers too. Possibly by creating another prototype cell with a submit button and having access to all the fields in that CustomCell.swift file?


  • Michael David McKenna

    If I enter values into “full name” and tap full name to close it, then tap it again to open it again to make a change, last name and first name were flipped. What would be the potential cause for this?


    • Brock Elkins

      Has anyone found a solution for this? Mine has the same error.


  • Brock Elkins

    AWESOME tutorial. Thanks a bunch.


  • Lanaya_HSIEH

    When I touch to change slider value, slider will use strange animation. The thumbImage’s animation is bad but the application don’t crush.


  • mdevs

    mdevsmdevs

    Author Reply

    Sounds brilliant. Will I be able to do an Objective-C version of this tutorial? or is it mandatory to use Swift?


  • Sho Morinaga

    Does it not look a bit strange when a user taps on an item like “Date of Birth” or “Favorite Color”, and the cell seems to kind of jump around upon expanding and collapsing? The first cells in the section behave properly, but any other cell seems to drift around and look distracting. I’m referring to the final product downloaded from GitHub.


    • Hertugen

      HertugenHertugen

      Author Reply

      It does! Anybody have a fix for this?


    • Ryan Brear

      Ryan BrearRyan Brear

      Author Reply

      I whole heartedly agree. The reason this happens is because the whole section is being updated rather than just the expanded cell and newly visible cells. It’s a serious enough UI problem that I won’t use this approach unless I can find a solution.

      I am trying to find a way to only refresh / redraw the expanded cell, and the newly visible cells. No luck yet though.

      Anybody have any idea how to do this?


      • Ryan Brear

        Ryan BrearRyan Brear

        Author Reply

        Ok, I figured out how to do it.

        Instead of using reloadSections you can use insertRowsAtIndexPath / removeRowsAtIndexPath. These two methods take an array of indexPaths, so to get it to work simply capture an array of the indexPaths that need to be inserted into the tableView.

        Importantly, your model needs to be updated before you insert/remove rows, so be sure to call getIndicesOfVisibleRows just before you call those methods.

        Results in a much smoother animation.


        • David West

          David WestDavid West

          Author Reply

          Ryan, if you could post how you wrote the indexPath that you refer to that’d be reeally useful. Can’t seem to get either function working, insertRowsAtIndexPath nor deleteRowsAtIndexPath.


          • Ryan Brear

            Ryan BrearRyan Brear

            Author

            Sure.

            // I run the code below in didSelectRowAtIndexPath

            // Step 1: get the index of the info in the main array that stores all the cells info.

            let indexOfInfoInMainArrayForTappedRowBeforeRemovingCells = indicesOfRowsMarkedVisibleInMainArray [indexPath.section][indexPath.row]

            // step 2: get the row index to be inserted into the tableView. I get this by calling a little method that I set up that always returns the correct row to insert.

            let rowToInsert = self.getRowIndexToInsert(section, indexOfRowInMainArray: indexOfInfoInMainArrayForTappedRowBeforeRemovingCells)

            // step 3: create an indexPath of the row to be inserted (combo of row and section)

            let indexPathToInsert = NSIndexPath(forRow: rowToInsert, inSection: indexPath.section)

            // stpe 4: insert the rows.

            self.profileUITV.insertRowsAtIndexPaths([indexPathToInsert], withRowAnimation: .Fade)

            // step 5: make sure my record of visible cells is uptodate.

            self.SetIndicesOfRowsMarkedVisibleInMainArray()


          • David West

            David WestDavid West

            Author

            Thanks Ryan – appreciate you trying to break this down. Unfortunately I’m not of the ability to translate this to the example provided, as you’ve seemingly renamed some variables & functions. Guess I’ll have to stick with .reloadSections until I can find some more capable resource to help me on my project 😉


          • David West

            David WestDavid West

            Author

            Let’s give this a go still….in which case how have you defined the following Ryan?
            – self.getRowIndexToInsert
            – indexOfRowInMainArray
            – indexOfInfoInMainArrayForTappedRowBeforeRemovingCells

            I believe the others are just simply renamed versions of the code above.


          • Marius Schulte

            Can you put this in your example project? Would be super cool to see.


  • sharsad

    sharsadsharsad

    Author Reply

    hi, Am fresher in swift.Can you just send me expandable tableview like favourite products in your example .


  • Ryan Brear

    Ryan BrearRyan Brear

    Author Reply

    Thanks for a great tutorial. May I suggest an improvement: using the refreshSection method causes some unpleasant animation if your section has many rows (since the whole section is being reloaded); it also causes a slight flicker of the section header for the same reason.

    Instead you could use insertRowsAtIndexPath and removeRowsAtIndexPath. All you need to make this work is an array of the indexPaths that you need to insert into the tableView that can be passed to those methods, and b) make sure your model is updated BEFORE you call those methods.

    End result: super smooth animation.

    Thanks again for a great tutorial.


    • David West

      David WestDavid West

      Author Reply

      Would love to see how this code looks in practice….


  • Ploy Goh

    Ploy GohPloy Goh

    Author Reply

    Hi,

    Thanks for the great tutorial.
    Could you suggest me how to apply the multiple selection to this project?


  • Morgan Le Gal

    Hi there,

    Thank you for the tutorial! Very cool!

    I’m trying to add programmatically section and rows to the project. So what I do is to create another NSMutableArray and add this array to the cellDescriptors. Basically what I do for that;

    var myNewNSMutableArray = NSMutableArray() //create empty array

    //adding dictionaries to the NSMutableArray
    myNewNSMutableArray.addObject([“additionalRows”: 2, “cellIdentifier”: “idCellNormal”, “isExpandable”: true , “isExpanded”: false , “isVisible”: true , “secondaryTitle”: “”, “primaryTitle”: “myTitle”, “value” : “”])

    // Adding the array to cellDescriptors
    cellDescriptors.addObject( myNewNSMutableArray )

    It does work great until I select the row to be expanded. The crash occurs at the following line;
    cellDescriptors[indexPath.section][indexOfTappedRow].setValue(shouldExpandAndShowSubRows, forKey: “isExpanded”)

    Where the error shows that the key “isExpanded” is not found;
    Error: this class is not key value coding-compliant for the key isExpanded

    Is there any way to work around this error without having to add manually all the row in the cellDescriptor.plist ?

    Best,
    Morgan


    • lalaphoon

      lalaphoonlalaphoon

      Author Reply

      Hi Morgan, did you figure that out?


  • Wayne Henderson

    Sorry if this is a stupid question, but it seems this example does not actually store any of the user’s preferences choices that were made. So upon restarting the app, it always reverts to the original state. I like everything else and was thrilled to find this well-written tutorial, but it’s not terribly useful unless/until it can save the user’s preferences.

    I’m familiar with using the NSUserDefaults to store settings but have not used a plist. I believe this tutorial anticipates writing the user’s choices to the plist file. For example this line seems to do that:
    cellDescriptors[maritalSwitchCellSection][maritalSwitchCellRow].setValue(valueToStore, forKey: “value”)
    But the new values don’t seem to “stick”.

    Am I missing something?

    BTW, I have the same issue with the first name, last name switching around.


  • Sakthi Kumar

    Could you please give Objective-C version of this tutorial?


  • red

    redred

    Author Reply

    any updates to swift 3?


  • red

    redred

    Author Reply

    do you have any update to swift3
    currentSectionCells[row][“isVisible”] as! Bool == true { – Type element to Any has no subscript
    any ideas?


  • msabo

    msabomsabo

    Author Reply

    Could you provide a swift 3 update


  • Wayne Henderson

    Add me to the list of folks that would appreciate a Swift 3 solution.

    I get the error “Type ‘Any’ has no subscript members” any and every time the cellDescriptors are called.


  • docmarroc

    docmarrocdocmarroc

    Author Reply

    Hello,
    I am running swift 3.0, and neither the original project (straight out of the download) nor the converted version
    (2.3->3.0) display when I attempt a run. I get “Build Succeeded” and no other alerts, but the simulator is blank.

    Everything appears to have loaded properly, and I’ve restarted Xcode (and the project) several times

    I’m REALLY new to programming, so any help would be greatly appreciated!

    Thank you in advance!



  • Greg

    GregGreg

    Author Reply

    Do you have a Swift 4 version of this?


Leave a Reply to David West
Cancel Reply

Shares