1

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)
                                }
                            }
                        }
                    }

1 Answer 1

1

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.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.