William Wang
William Wang

Reputation: 27

How would you create an array of views to loop through, to create a navigation link to each one of the views?

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

Answers (1)

jnpdx
jnpdx

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

Related Questions