How to make Expandable List with SwiftUI

Last updated on: May 27, 2023

DISCLAIMER: In this tutorial, I’m using ScrollView instead of List because when you change the size of a View inside a List, the cell jiggles, and the movement is not smooth. It’s a bug in SwiftUI. The disadvantage of this is you can’t add or delete cells as the List provides. If you’re ok with that, keep reading.

Preparing the Data and Model

Before we start, create an empty swift file, name it Data and paste the following code inside:

import Foundation

class Data {
    var items = [
        DataModel(
            question: "Item 0",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
        ),
        DataModel(
            question: "Item 1",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam"
        ),
        DataModel(
            question: "Item 2",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut"
        ),
        DataModel(
            question: "Item 3",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
        ),
        DataModel(
            question: "Item 4",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
        ),
        DataModel(
            question: "Item 5",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
        ),
        DataModel(
            question: "Item 6",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident"
        ),
        DataModel(
            question: "Item 7",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
        ),
        DataModel(
            question: "Item 8",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
        ),
        DataModel(
            question: "Item 9",
            answer: "Lorem ipsum dolor sit amet"
        ),
        DataModel(
            question: "Item 10",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
        ),
        DataModel(
            question: "Item 11",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
        ),
        DataModel(
            question: "Item 12",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"
        ),
        DataModel(
            question: "Item 13",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
        ),
        DataModel(
            question: "Item 14",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore"
        ),
        DataModel(
            question: "Item 15",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"
        ),
        DataModel(
            question: "Item 16",
            answer: "Lorem ipsum"
        ),
        DataModel(
            question: "Item 17",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in"
        ),
        DataModel(
            question: "Item 18",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat"
        ),
        DataModel(
            question: "Item 19",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
        ),
        DataModel(
            question: "Item 20",
            answer: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua"
        )
    ]
}Code language: Swift (swift)

As you see the model (DataModel), contains:

  • Question
  • Answer

Create another empty swift file, name it DataModel and paste:

import Foundation

class DataModel {
    var question: String
    var answer: String
    
    init(question: String, answer: String) {
        self.question = question
        self.answer = answer
    }
}Code language: Swift (swift)

Creating the Expandable Cell

First, let’s create a new SwiftUI View for the pop-up.

Go to your project folder, Right-Click > New File…

Select SwiftUI View, press Next and name it ListCell

Paste the following code inside:

struct ListCell: View {
    @State private var tapped: Bool = false
    var row: Int
    var question: String
    var answer: String

    var body: some View {
        VStack(spacing: 0) {
            Text(question)
                .padding()
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(Color(#colorLiteral(red: 0.737254902, green: 0.1294117647, blue: 0.2941176471, alpha: 1)))
                .foregroundColor(.white)
                .onTapGesture(perform: {
                    withAnimation(.easeInOut(duration: 0.5)) {
                        tapped.toggle()
                    }
                })

            if tapped {
                Text(answer)
                    .padding()
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .background(Color(#colorLiteral(red: 0.6196078431, green: 0.1098039216, blue: 0.2509803922, alpha: 1)))
                    .clipped()
                    .background(Color.clear)
                    .foregroundColor(.white)
            }
        }
    }
}Code language: Swift (swift)

In this file, we have a VStack with two Texts (question & answer). When we tap the question Text we change the @State variable tapped to true and we unveil the answer Text with a nice fade animation.

Creating the List (ScrollView)

In the parent View, as I said at the beginning, we’re going to use ScrollView instead of List to avoid this animation bug:

struct ContentView: View {
    var itemsArray = Data().items

    init() {
        UINavigationBar.appearance().barTintColor = #colorLiteral(red: 0.737254902, green: 0.1294117647, blue: 0.2941176471, alpha: 1)
        UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.white]
        UINavigationBar.appearance().isTranslucent = false

        // Use expanded view's color as the ScrollView background color to make it look better when the cell expands/collapses
        UIScrollView.appearance().backgroundColor = UIColor(Color(#colorLiteral(red: 0.6196078431, green: 0.1098039216, blue: 0.2509803922, alpha: 1)))
    }

    var body: some View {
        NavigationView {
            ScrollView {
                VStack(spacing: 0) {
                    ForEach(0..<itemsArray.count) { row in
                        ListCell(row: row, question: itemsArray[row].question, answer: itemsArray[row].answer)
                    }
                }
            }
            .navigationBarTitleDisplayMode(.inline)
            .navigationTitle("ExpandableListSwiftUIExample")
        }
    }
}Code language: Swift (swift)
You can find the final project here

If you have any questionsplease feel free to leave a comment below

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments