Reputation: 27133
I have a menu which I display using SwiftUI List
. An example on the image below:
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.
Upvotes: 1
Views: 3430
Reputation: 2615
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"))
}
}
}
Upvotes: 3
Reputation: 1902
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.
Upvotes: 0
Reputation: 8091
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"))
}
}
}
Upvotes: 4