Home > Software design >  How can i set ForEach with multiple variables on Swift
How can i set ForEach with multiple variables on Swift

Time:06-30

I am looking to call two variables but the method I am trying to implement does not work.

Unfortunately, Xcode says Extra argument in call.

I am a newbie, can someone help me to understand what is going wrong ?

Thank you !!

struct Card {
    let content : [String]
    let names:[String]
    static let allCards:[Card] =     
        [
        Card(content: ["content1","content2"], names: ["name1","name2"]),
        Card(content: ["content3","content4"], names: ["name3","name4"])
        ]
   }
struct Play {
    
    let cards = Card.allCards.shuffled()
    var currentCardIndex = 0
    var currentCard:Card {
        cards[currentCardIndex]
    }
}
class GameViewModel {
    
    @Published var play = Play()
    var  cardContent:[String] {
        play.currentCard.content
    }
    var cardnames:[String] {
        play.currentCard.names
    }
}
struct CardView:View {
    let contentString:String
    let nameString:String
    var body: some View{
        HStack{
        Text(contentString)
        Text(nameString)
        }
    }
}
struct ContentView: View {
    var viewModel = GameViewModel()
    var body: some View {
        HStack{
/ Warning:  Extra argument in call 

            ForEach(viewModel.cardContent, viewModel.cardnames,id:\.self, content:{ card in
                CardView(contentString: card, nameString: card).frame(minWidth: 135, idealWidth: 250, maxWidth: 135, minHeight: 135, idealHeight: 250, maxHeight: 135, alignment: .center).aspectRatio(2/2, contentMode:.fit)
            })
            
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

CodePudding user response:

The root cause: In ForEach(viewModel.cardContent, viewModel.cardnames either viewModel.cardContent or viewModel.cardnames is an extra argument, as ForEach can iterate only one array, and you are passing 2 arrays.

The simplest fix: pass only one array (viewModel.play.cards) in

ForEach(viewModel.play.cards, id:\.self, content: { card in
     CardView(contentString: card.content, nameString: card.names).frame(minWidth: 135, idealWidth: 250, maxWidth: 135, minHeight: 135, idealHeight: 250, maxHeight: 135, alignment: .center).aspectRatio(2/2, contentMode:.fit)
}) 

The example above still may not compile because of CardView(contentString: card constructor, but that's another problem :)

CodePudding user response:

Yes, it throws an error because you are passing more than the available arguments to constructor of ForEach

You can see the below available constructor of ForEach, then you come to know which argument you are passing it as extra to the constructor

init(_ data: Data, @AccessibilityRotorContentBuilder content: @escaping (Data.Element) -> Content)

init(_ data: Data, id: KeyPath<Data.Element, ID>, @AccessibilityRotorContentBuilder content: @escaping (Data.Element) -> Content)

init(_ data: Data, @ViewBuilder content: @escaping (Data.Element) -> Content)

init(_ data: Data, id: KeyPath<Data.Element, ID>, @ViewBuilder content: @escaping (Data.Element) -> Content)

init<C>(_ data: Binding<C>, @ViewBuilder content: @escaping (Binding<C.Element>) -> Content) where Data == LazyMapSequence<C.Indices, (C.Index, ID)>, ID == C.Element.ID, C : MutableCollection, C : RandomAccessCollection, C.Element : Identifiable, C.Index : Hashable

init<C>(_ data: Binding<C>, id: KeyPath<C.Element, ID>, @ViewBuilder content: @escaping (Binding<C.Element>) -> Content) where Data == LazyMapSequence<C.Indices, (C.Index, ID)>, C : MutableCollection, C : RandomAccessCollection, C.Index : Hashable

And in your code

//viewModel.cardnames is the extra argument you are passing to ForEach
            ForEach(viewModel.cardContent, viewModel.cardnames,id:\.self, content:{ card in
                CardView(contentString: card, nameString: card).frame(minWidth: 135, idealWidth: 250, maxWidth: 135, minHeight: 135, idealHeight: 250, maxHeight: 135, alignment: .center).aspectRatio(2/2, contentMode:.fit)
            })
  • Related