1

I have a Binding variable, I am trying to make it a Generi Array. I will take two different models in it and I want to reach the image property in the model. How can I do that ?

I use two different models in different page transitions. How can I make the array holding the model generic?

Model 1

struct PoliceSignContainer: Codable, Hashable {
    var policeQuestions: [PoliceSign]?
}

enum PoliceSignSectionType: String, Codable, Hashable {
    case A = "A"
    case B = "B"
    case C = "C"
}

struct PoliceSign: Codable, Hashable {
    var id: Int?
    var image: String?
    var sections: [PoliceSignSectionType.RawValue : String]?
    var correct: String?
}

Model 2

struct TrafficSignContainer: Codable, Hashable {
    var trafficQuestions: [TrafficSign]?
}

enum TrafficSignSectionType: String, Codable, Hashable {
    case A = "A"
    case B = "B"
}

struct TrafficSign: Codable, Hashable {
    var id: Int?
    var image: String?
    var sections: [TrafficSignSectionType.RawValue : String]?
    var correct: String?
}



struct QuestionCardView: View {
    @EnvironmentObject var optionConfigure: OptionConfigure
    @Binding var questions: [Any]
    var body: some View {

        VStack {

            ....

            ZStack {
                ForEach((questions.indices).reversed(), id: \.self) { index -> AnyView in

                    let relativeIndex = index - optionConfigure.step
                    switch relativeIndex {
                    case 0...2:
                        return AnyView(

              ImageCard(image: .constant("\(questions[index].image ?? "p1")")) // here
                          ...

                        )
                    default:
                        return AnyView(EmptyView())
                    }
                }
            }
            .animation(.spring())
            OptionView()
        }
        ...
    }
}
3
  • Does this answer your question? Is it possible to make SwiftUI ListMenu with different behaviors? Commented Jul 25, 2021 at 22:41
  • Do you mean two different models at the same time (a heterogeneous collection), or will each instance of the view take one type for the array? Commented Jul 25, 2021 at 22:58
  • @jnpdx not two models at the same time. Commented Jul 25, 2021 at 23:02

2 Answers 2

2

For making your Binding array aka questions became generic you can simply just conform to Hashable, for accessing the String conform to CustomStringConvertible. see the example code:

struct QuestionCardView<T: Hashable & CustomStringConvertible>: View {
    @EnvironmentObject var optionConfigure: OptionConfigure
    @Binding var questions: [T]
    var body: some View {
        Text("")
        VStack {

            ....

            ZStack {
                ForEach((questions.indices).reversed(), id: \.self) { index -> AnyView in

                    let relativeIndex = index - optionConfigure.step
                    switch relativeIndex {
                    case 0...2:
                        return AnyView(

              ImageCard(image: String(describing: questions[index].image) ?? "p1")")) // here
                          ...

                        )
                    default:
                        return AnyView(EmptyView())
                    }
                }
            }
            .animation(.spring())
            OptionView()
        }
        ...
    }
}

Working Example:

struct ContentView: View {
    
    @State private var array: [String] = [ "applelogo", "alarm", "person.fill.questionmark", "cloud.drizzle.fill", "swift"]
    
    var body: some View {

        CustomView(array: $array)
        
    }
    
}



struct CustomView<T: Hashable & CustomStringConvertible>: View {
    
    @Binding var array: [T]
    
    var body: some View {
        

        VStack {
            
            ForEach(array, id:\.self) { item in

                ImageView(imageString: String(describing: item))
                
            }
            
        }
        
    }
    
}

struct ImageView: View {
    
    let imageString: String
    
    var body: some View {
        
        Image(systemName: imageString)
            .resizable()
            .scaledToFit()
            .frame(width: 50, height: 50, alignment: Alignment.center)
        
    }
    
}

enter image description here

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

7 Comments

Making it Hashable alone wouldn't allow access to the image property on the line that starts with ImageCard
yes, it need to conform to CustomStringConvertible as well.
CustomStringConvertable doesn't have an image property.
both can see the example! @UfukKöşker: I do not have access to your project I cannot help you in that way, see the example and take the idea.
I understood now. I will try your code again.
|
0

Here's a basic example showing a view that has a generic requirement for the @Binding array like you have:


enum TrafficSignSectionType: String, Codable, Hashable {
    case A = "A"
    case B = "B"
}

struct TrafficSign: Codable, Hashable, Question { //<-- Here
    var id: Int?
    var image: String?
    var sections: [TrafficSignSectionType.RawValue : String]?
    var correct: String?
}

enum PoliceSignSectionType: String, Codable, Hashable {
    case A = "A"
    case B = "B"
    case C = "C"
}

struct PoliceSign: Codable, Hashable, Question { //<-- Here
    var id: Int?
    var image: String?
    var sections: [PoliceSignSectionType.RawValue : String]?
    var correct: String?
}


protocol Question : Identifiable { //<-- Here
  var image: String? { get set }
}

struct ContentView : View {
    @State private var questions : [PoliceSign] = []
    @State private var questions2 : [TrafficSign] = []
    
    var body: some View {
        QuestionCardView(questions: $questions)
        QuestionCardView(questions: $questions2)
    }
}

struct QuestionCardView<T: Question>: View {
    @Binding var questions: [T]
 
    var body: some View {
        ForEach(questions) { question in
            Text(question.image ?? "")
        }
    }
}

You'll want a protocol that defines the requirements for the type. In your original code, it looked like image was the only obvious property.

Your original code will have:

struct QuestionCardView<T: Question>: View {
    @EnvironmentObject var optionConfigure: OptionConfigure
    @Binding var questions: [Question]

5 Comments

I edited my question sir. Can you check ?
I've updated my answer. All you need to do is add the Question protocol, and then make sure that the two models conform to it. No need for the container types. Other than making image an Optional property, it's basically the same as the previous version of my answer.
Thank you so much for your answer but I have little problem sir. I need to add two different enums to the protocol. How can I do that ? Because in the next page that opens I need to access Enums.
@UfukKöşker I'm not sure what that means exactly. If you feel like it's directly related to the original question, maybe edit the question to add more info explaining it. It actually sounds like it may be a new question, though. If you've found this answer helpful, please consider upvoting it.
If you mean the sections property, because you have it set up as a Dictionary with the raw values of the enums as the keys, you can just add var sections: [String:String]? { get set } to the protocol.

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.