I'm in the process of making my first app (ever) using SwiftUI
I've made a fairly simple custom view for my main navigation links
struct MainMenuCapsule: View {
let label: String
private let dest: NavDestination
init(_ label:String, destination: NavDestination) {
self.label = label
self.dest = destination
}
var body: some View {
NavigationLink(label, destination: dest)
.frame(width: 250, height: 100)
.background(Capsule().fill(Color.newPrimaryColor))
.foregroundColor(.black)
.font(.system(size: 28, weight: .light))
.padding()
}
}
For my home screen, I want to use something clean like a ForEach iterating over a list of (label: String, dest: View) pairs to generate the list of capsule-links. (I briefly tried using List but the automatic NavLink formating didn't fit my design and I struggled to edit it - comments on using that approach are also welcomed.)
I've been googling and playing with things for a few hours it seems, and it doesn't seem possible (with the original issue being "Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional" - if I do that, it allows the array's declaration but issue ensue with trying to use it) but I wanted to check here before resorting to an explicit call for each MainMenuCapsule instance
struct ContentView: View {
let menuItems = ["Recommended", "Browse", "Guidelines"]
// The style of array I want to use
// let menuItems = [("Recommended", Dots(2)),
// ("Browse", Placeholder()),
// ("Guidelines", Placeholder())]
var body: some View {
VStack {
// Nav Icons
HStack {
Image(systemName: "info.circle.fill")
Spacer()
Image(systemName: "gear")
}
.padding(.horizontal)
NavigationView {
VStack(spacing: 30) {
// Title and welcome message
HStack {
VStack(alignment: .leading) {
Text("Welcome")
.font(.largeTitle)
Text("Let's learn some maths!")
.font(.system(size: 20, weight: .light))
}
.padding(.horizontal, 30)
Spacer()
}
// Main menu
ForEach(0..<menuitems.count) {i="" in<br=""> MainMenuCapsule(self.menuItems[i], destination: Placeholder())
}
Spacer()
}
}
}
}
}
PS Any comments on bad swift code style are also welcomed, I take pride in trying to follow standards and doing things in the "best" way, but I've got a lot to learn;)
As you found, you cannot create an Array of different Views practical enough to use it.
One possible way to handle this situation might be a type-erasure.
You can create an Array of a single type `AnyView`.
struct ContentView: View {
let menuItems: [(label: String, dest: AnyView)] = [
("Recommended", AnyView(Dots(2))),
("Browse", AnyView(Placeholder())),
("Guidelines", AnyView(Placeholder()))
]
var body: some View {
VStack {
// Nav Icons
//...
NavigationView {
VStack(spacing: 30) {
// Title and welcome message
//...
// Main menu
ForEach(0..<menuItems.count) {i in
MainMenuCapsule(self.menuItems[i].label, destination: self.menuItems[i].dest)
}
Spacer()
}
}
}
}
}
The type `NavDestination` is sort of a mystery that I'm not sure you can make it work, but I believe it's worth trying.