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      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

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.