Reputation: 27
I have 8 views for where in a horizontal scroll group, the user would tap the image and go to the corresponding view. I know I could do this manually but using a ForEach loop will save a lot of code, I've done similar things with text as you can see below and I tried to do so with the array of views, but the parameter requirements for a navigation link makes it difficult to refer to the view itself, as it would be ie. [dogs_and_cats but the navigation link wants it to be dogs_and_cats()]. Yet this doesn't work due to the errors: Type 'Any' has no member 'init' in the nav link line and Cannot convert value of type 'Barcelona_Museum_of_Contemporary_Art.Type' to expected element type 'Any.Protocol' for each of the array elements. If you were in my shoes how would you create a array of view objects if that is possible, and loop through them for each nav link?
let museumNamesForLink = [Whitney_Museum_of_American_Art,
The_Andy_Warhol_Museum,
Museum_of_Modern_Art, Nakamura_Keith_Haring_Collection,
Tate_Modern,
The_Broad_Museum,
Museum_fu_r_Moderne_Kunst,
Barcelona_Museum_of_Contemporary_Art]
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(museumNames.indices) { index in
VStack {
NavigationLink(destination: museumNamesForLink[index]()) {
Image(museumNames[index])
.resizable()
.aspectRatio(contentMode: .fit)
}
Text(museumNames[index])
.bold()
.font(.callout)
.foregroundColor(.white)
.multilineTextAlignment(.center)
.padding(.leading)
}
}
}
}
Upvotes: 1
Views: 612
Reputation: 52387
I'd probably approach this with an enum
to represent the different museum types and then a @ViewBuilder
function with a switch
statement that can give you a different View
based on which enum
is fed to it.
struct ContentView: View {
enum MuseumTypes: CaseIterable {
case whitney, warhol, moma
var name : String {
switch self {
case .whitney:
return "Whitney Museum"
case .warhol:
return "Warhol Musuem"
case .moma:
return "Museum of Modern Art"
}
}
}
@ViewBuilder func museumViewForType(type: MuseumTypes) -> some View {
switch type {
case .whitney:
WhitneyView()
case .warhol:
WarholView()
case .moma:
MomaView()
}
}
var body: some View {
NavigationView {
VStack {
ForEach(MuseumTypes.allCases, id: \.self) { museum in
NavigationLink(destination: museumViewForType(type: museum)) {
Text(museum.name)
}
}
}
}
}
}
struct WhitneyView : View {
var body: some View {
Text("Whitney")
}
}
struct WarholView : View {
var body: some View {
Text("Warhol")
}
}
struct MomaView : View {
var body: some View {
Text("MOMA")
}
}
An alternate approach is to store all of your views in the array and wrap them with AnyView()
so that they're homogeneous types (see https://stackoverflow.com/a/66057289/560942), but I think that's not as clean or as clear as the approach I detailed above). Plus, by using an enum
instead of an array, you'll get warnings from the compiler if you forget a case and it's guaranteed you won't miss something and go outside the bounds of your array indexes.
Upvotes: 1