1

I have a menu which I display using SwiftUI List. An example on the image below:

enter image description here

What I want to do is to use NavigationLink with different destination views. I tried to use switch statement in the List block but Xcode throws an error:

Closure containing control flow statement cannot be used with function builder 'ViewBuilder'

Here is my code below:

struct HomeView: View {

    enum MenuItem: String, CaseIterable, Identifiable {
        var id : MenuItem {
            self
        }

        case firstCase = "Staff"
        case secondCase = "Projects"
        case thirdCase = "Invoices"
    }

    var body: some View {
        NavigationView {
            List(MenuItem.allCases) { itemText in

               switch itemText {
               case .firstCase:
                   NavigationLink(destination: StaffDetail()) {
                       HomeMenuRow(itemText: itemText)
                   }
                   break
               case .secondCase:
                   NavigationLink(destination: ProjectsDetail()) {
                       HomeMenuRow(itemText: itemText)
                   }
                   break

               case .thirdCase:
                   NavigationLink(destination: InvoicesDetail()) {
                       HomeMenuRow(itemText: itemText)
                   }
                   break
               }

            }
            .navigationBarTitle(Text("Menu"))
        }
    }
}

Looks like find some solution here but not sure how to use it in the List object.

2
  • looks like I figured out I need to call such code via internal kind of factory method function. am I right? Commented Apr 13, 2020 at 13:14
  • 1
    You need to move your logic outside of your body and just call a function, then return what you need. Commented Apr 13, 2020 at 13:19

3 Answers 3

4

try this:

struct StaffDetail : View  {
    var body: some View {
        Text("1")
    }
}

struct ProjectsDetail : View  {
    var body: some View {
        Text("2")
    }
}

struct InvoicesDetail : View  {
    var body: some View {
        Text("3")
    }
}

struct HomeMenuRow : View  {
    var itemText: String
    var body: some View {
        Text(itemText)
    }
}

struct ContentView: View {

    enum MenuItem: String, CaseIterable, Identifiable {
        var id : MenuItem {
            self
        }

        case firstCase = "Staff"
        case secondCase = "Projects"
        case thirdCase = "Invoices"
    }

    func getDestination(itemText: String) -> AnyView {

        let value = MenuItem(rawValue: itemText)

        switch value {

        case .some(.firstCase):
            return AnyView(InvoicesDetail())
        case.some(.secondCase):
            return AnyView(ProjectsDetail())
        case .none:
            return AnyView(Text("a"))
        case .some(.thirdCase):
            return AnyView(StaffDetail())
        }
    }

    var body: some View {
        NavigationView {
            List(MenuItem.allCases) { itemText in

                NavigationLink(destination: self.getDestination(itemText: itemText.rawValue)) {
                    HomeMenuRow(itemText: itemText.rawValue)
                }

            }.navigationBarTitle(Text("Menu"))

        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

3

A little bit different approach when a menu has sections:

import SwiftUI

struct MenuView: View {
    enum MenuItem: String {
        case staff = "Staff"
        case projects = "Projects"
        case invoices = "Invoices"
        case about = "About"
        case feedback = "Fourth"

        static let infoSection: [MenuItem] = [.staff, .projects, .invoices]
        static let helpSection: [MenuItem] = [.about, .feedback]
    }

    var body: some View {
        NavigationView {
            List {
                makeSection(title: "Info", items: MenuItem.infoSection)
                makeSection(title: "Help", items: MenuItem.helpSection)
            }
            .navigationBarTitle("Menu")
        }
    }

    private func makeSection(title: String, items: [MenuItem]) -> some View {
        Section(header: Text(title)) {
            ForEach(items, id: \.self) { item in
                NavigationLink(destination: self.destination(forItem: item)) {
                    Text(item.rawValue)
                }
            }
        }
    }

    private func destination(forItem item: MenuItem) -> some View {
        switch item {
            case .staff: return AnyView(Text("Staff View"))
            case .projects: return AnyView(Text("Projects View"))
            case .invoices: return AnyView(Text("Invoices View"))
            case .about: return AnyView(Text("About View"))
            case .feedback: return AnyView(Text("Feedback View"))
        }
    }
}

Comments

0

I could make it work using if-else statements:

        NavigationView {
            List(MenuItem.allCases) { itemText in
                if itemText == MenuItem.firstCase {
                    NavigationLink(destination: Text("first")) {
                        Text(itemText.rawValue)
                    }
                } else if itemText == MenuItem.secondCase {
                    NavigationLink(destination: Text("bar")) {
                        Text(itemText.rawValue)
                    }
                } else {
                    NavigationLink(destination: Text("baz")) {
                        Text(itemText.rawValue)
                    }
                }
            }
            .navigationBarTitle(Text("Menu"))
        }

I explicitly added MenuItem to make the compiler happy. Apparently, if-else clauses in the function builder can't deal with complex type inference.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.