NEW: Start my new Ultimate Portfolio App course with a free Hacking with Swift+ trial! >>

SOLVED: List being presented horizonally instead of vertically.

Forums > SwiftUI

I've got a strange situation and I'm not sure why it's happening.

I have a list view that is supposed to present its contents sorted/filtered by a number of criteria. So basically, each choice is really a different subview:

struct LibraryView: View {
    @State private var sorting: Sorting = .titleAscending
    @State private var filtering: Filtering = .none

    @EnvironmentObject var database: LibraryDatabase

    var body: some View {
        Group {
            switch (filtering, sorting) {
                // MARK: - Sorted, no filter
                case (.none, .titleAscending): sectionedList(books: database.books)
                case (.none, .author): bookListSectionedByAuthor(authors: database.authors)
                // etc etc
            }
        }
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Menu(content: {
                    Picker("Filter by...", selection: $filtering) {
                        ForEach(Filtering.allCases, id: \.self) { option in
                            HStack {
                                Text(option.rawValue)
                                Image(systemName: option.image)
                            }
                        }
                    }
                },
                label: {
                    Image(systemName: "line.horizontal.3.decrease")
                })
            }
            ToolbarItem(placement: .navigationBarTrailing) {
                Menu("Sort") {
                    Picker("Sort by...", selection: $sorting) {
                        ForEach(Sorting.allCases, id: \.self) { option in
                            HStack {
                                Text(option.rawValue)
                                Image(systemName: option.image)
                            }
                        }
                    }
                }
            }
        }
    }
}

The first option in the switch above work as it should:

The second, however, does this:

I really don't understand why that's happening. It was working properly when I had the code as part of the Sorting enum, as seen here:

    func view(_ database: LibraryDatabase) -> some View {
        return Group {
            switch self {
                case .titleAscending: titleListView(database)
                case .author: authorListView(database)
            }
        }
    }

    private func authorListView(_ database: LibraryDatabase) -> some View {
        let authors = database.authors.sorted(by: { $0.description < $1.description })
        return List {
            ForEach(authors) { author in
                Section(header: Text(author.description)) {
                    ForEach(author.books.sorted(by: {$0.title < $1.title})) { book in
                        BookRowView(book)
                    }
                }
            }
        }
        .navigationTitle("Authors")
        .listStyle(InsetListStyle())
    }

The only thing that changed was that I added extensions to get the sorting/filtering code out of the functions that return views, so that those functions were a lot cleaner:

extension Array where Element == Audiobook {
    var sorted: [Audiobook] {
        self.sorted(by: {$0.title < $1.title})
    }
}

extension Array where Element == Author {
    var sorted: [Author] {
        self.sorted(by: {(SortableOptional(optional: $0.name.familyName), SortableOptional(optional: $0.name.givenName)) <
                        (SortableOptional(optional: $1.name.familyName), SortableOptional(optional: $1.name.givenName))})
    }
}

and then I moved the functions to an extension of the view itself instead of having them in the enum:

extension LibraryView {
    /// A view that presented the array of books as a list in ascending order by title
    ///
    /// If the array of books contains less than 20 entries, the list will be unsectioned.
    ///
    /// Otherwise, there will be one section for each first character
    func sectionedList(books: [Audiobook]) -> some View {
        List {
            if books.count <= 20 {
                ForEach(books.sorted) { book in
                    BookRowView(book)
                }
            } else {
                ForEach(books.firstCharacters) { char in
                    Section(header: Text(String(char))) {
                        ForEach(books.filteredByFirstCharacter(char).sorted) { book in
                            BookRowView(book)
                        }
                    }
                }
            }
        }
        .listStyle(InsetListStyle())
    }

    /// Presents a list of books sectioned by author
    func bookListSectionedByAuthor(authors: [Author]) -> some View {
        List(authors) { author in
            Section(header: Text(author.description)) {
                ForEach(author.books.sorted) { book in
                    BookRowView(book)
                }
            }
        }
        .listStyle(InsetListStyle())
    }
}

I don't see why it would be presenting the list horizonally instead of vertically in the second sorting option.

   

Actually, cancel that. I answered my own question, and I'm sharing it here because it seems like a mistake others might make and could learn from. As I was reviewing what I had posted, I noticed one other change I'd made, so minor I didn't even think about it:

This presents the list vertically, as it should:

    func bookListSectionedByAuthor(authors: [Author]) -> some View {
        List {
            ForEach(authors) { author in
                Section(header: Text(author.description)) {
                    ForEach(author.books.sorted) { book in
                        BookRowView(book)
                    }
                }
            }
        }
        .listStyle(InsetListStyle())
    }
}

This doesn't:

    func bookListSectionedByAuthor(authors: [Author]) -> some View {
        List(authors) { author in
            Section(header: Text(author.description)) {
                ForEach(author.books.sorted) { book in
                    BookRowView(book)
                }
            }
        }
        .listStyle(InsetListStyle())
    }

   

Hacking with Swift is sponsored by Instabug

SPONSORED Catch bugs as soon as they happen and know exactly why a crash occurred. Instabug's SDK grabs all the logs they need to fix bugs, crashes and performance issues in minutes instead of days. Get screenshots, device details, network logs, repro steps, and tons of other critical insights needed to resolve issues and prioritize product backlogs straight from your dashboard. It only takes a minute to integrate!

Get started now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.