4

I am making a SwiftUI app for iOS 14, and I have a sidebar list that uses the new children: attribute of list to make the list expandable:

However, at the moment I am only able to expand this list when I click precisely on the disclosure arrow. I would like to be able to expand this list when I click on the 'My Cool Project' text as well, for instance.

This is possible in the Files app - I can see all of the children of the Locations item when I click on the text saying 'Locations', but I can't figure out how to do this in my app.

To clarify, List item for each project is a Text view and the child list items are NavigationLink

Would this behaviour be possible in SwiftUI, even programatically through onTapGesture or with something other than a List? Thanks in advance for any help/advice!

Deitsch
  • 1,610
  • 14
  • 28
themathsrobot
  • 581
  • 6
  • 20
  • [How to create a Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) – Asperi Aug 03 '20 at 10:51
  • @Asperi - I will try to update this with example code later on, however I've tested it with the code from this tutorial and only the disclosure arrow can be clicked: https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-expanding-lists – themathsrobot Aug 03 '20 at 11:03

2 Answers2

18

Here is a demo of approach (of course in your project you'd move expand/collapse state into view model)

demo

struct DemoDisclosureGroups: View {
    let items: [Bookmark] = [.example1, .example2, .example3]
    @State private var flags: [Bool] = [false, false, false]

    var body: some View {
        List {
            ForEach(Array(items.enumerated()), id: \.1.id) { i, group in
                DisclosureGroup(isExpanded: $flags[i]) {
                    ForEach(group.items ?? []) { item in
                        Label(item.name, systemImage: item.icon)
                    }
                } label: {
                    Label(group.name, systemImage: group.icon)
                        .contentShape(Rectangle())
                        .onTapGesture {
                            withAnimation {
                                self.flags[i].toggle()
                            }
                        }
                }
            }
        }
    }
}

struct Bookmark: Identifiable {
    let id = UUID()
    let name: String
    let icon: String
    var items: [Bookmark]?

    // some example websites
    static let apple = Bookmark(name: "Apple", icon: "1.circle")
    static let bbc = Bookmark(name: "BBC", icon: "square.and.pencil")
    static let swift = Bookmark(name: "Swift", icon: "bolt.fill")
    static let twitter = Bookmark(name: "Twitter", icon: "mic")

    // some example groups
    static let example1 = Bookmark(name: "Favorites", icon: "star", items: [Bookmark.apple, Bookmark.bbc, Bookmark.swift, Bookmark.twitter])
    static let example2 = Bookmark(name: "Recent", icon: "timer", items: [Bookmark.apple, Bookmark.bbc, Bookmark.swift, Bookmark.twitter])
    static let example3 = Bookmark(name: "Recommended", icon: "hand.thumbsup", items: [Bookmark.apple, Bookmark.bbc, Bookmark.swift, Bookmark.twitter])
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • 6
    I found that to make the whole list item row tappable (as opposed to just where the label content is) I had to add `.frame(maxWidth: .infinity, alignment: .leading)` above `.contentShape(Rectangle())` – Lou Zell Sep 12 '20 at 17:47
  • .listRowBackground(Color.black) is not working with this. Is there any other way to change background color for such lists? – Niharika Jun 01 '22 at 12:34
0

Also you can use background color and .onTapGesture as in this code:

struct DisclosureGroupNew: View {

@State var show = false
@State var selectedCountry = ""

var body: some View {
    VStack {
        DisclosureGroup(isExpanded: $show) {
            ScrollView {
                LazyVStack(alignment: .leading, spacing: 15) {
                    ForEach(0...20, id: \.self) { index in
                        Text("Country \(index)")
                            .onTapGesture {
                                selectedCountry = "Country \(index)"
                                withAnimation {
                                    show.toggle()
                                }
                            }
                    }
                }
            }
            .padding(.top)
            .frame(height: 150) // if you need a fixed size

        } label: {
            Text("Label")
        }
        .padding()
        .background(Color.black.opacity(0.06).onTapGesture {
            show.toggle()
        })
        .cornerRadius(12)
        .padding()
        
        Text("Selected country: \(selectedCountry)")
        Spacer()
    }
}}
Vladimir Moor
  • 81
  • 1
  • 5