UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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.

3      

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())
    }

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.