Reputation: 1
I have a playerView view that currently displays two things: the player's hand and the total value of their cards. It is defined like so:
struct playerView: View{
@ObservedObject var player:Player
var isDealt:Bool
var body: some View{
VStack{
ZStack{
ForEach(player.viewCards.indices, id: \.self){ cardNumber in
//The first card has no offset so need conditional
if cardNumber == 0{
if player.viewCards[cardNumber]{
Image(player.hand[cardNumber].suit.rawValue + String(player.hand[cardNumber].rank) ).resizable().frame(width:120, height:160)
}
} else{
if player.viewCards[cardNumber]{
Image(player.hand[cardNumber].suit.rawValue + String(player.hand[cardNumber].rank) ).resizable().frame(width:120, height:160).offset(x: 40, y: -40)
}
}
}
}.padding(.top, 140)
//Display player card Score
if isDealt{
if player.hasAce{
Text("Card Score\n" + String(player.cardScore) + " / " + String(player.cardScore + 10)).font(.body).bold().foregroundColor(.white).lineLimit(2).multilineTextAlignment(.center)
} else{
Text("Card Score\n" + String(player.cardScore)).font(.body).bold().foregroundColor(.white).lineLimit(2).multilineTextAlignment(.center)
}
}
}
}
}
And I keep getting the error "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions" whenever I try to add almost anything to the code. For example, if I try to change the offset of the Image in the else part of the ForEach loop to be (x: 40 * cardNumber, y: -40 * cardNumber), I will get the error. I think that to fix this I will need to simplify my code further by perhaps creating another view to be used within this view. But I am not entirely sure how/why because this view doesn't seem very complex to me. Could someone give me an example of what I should do to simplify it further?
My understanding of what I should do is to take the blocks of code and turn them into views. So take the ZStack and turn it into a new view and call it within this view. But to do that I will need to pass the player into the new view, which is already passed into this view. Is this the right way to simplify views & get rid of this error?
Upvotes: 0
Views: 126
Reputation: 385600
Here are some things we can do to help the compiler.
For the card score text, you're using string concatenation where interpolation would be more idiomatic. Try this instead:
Text("""
Card Score
\(player.cardScore) / \(player.cardScore + 10)
"""
)
We can also combine your two card score branches into one:
let aceAlt = player.hasAce ? " / \(player.cardScore + 10)" : ""
Text("""
Card Score
\(player.cardScore)\(aceAlt)
"""
)
.font(.body)
.bold()
.foregroundColor(.white)
.lineLimit(2)
.multilineTextAlignment(.center)
It would reasonable to extract the image name computation into a separate property:
extension Card {
fileprivate var imageName: String { "\(suit.rawValue)\(rank)" }
}
Then we can create the Image
like this:
Image(player.hand[cardNumber].imageName)
Since the only difference between the cardNumber == 0
case and the else case is the offset
, we can just use offsets of zero in the cardNumber
case:
Image(player.hand[cardNumber].imageName)
.resizable()
.frame(width:120, height:160)
.offset(x: cardNumber == 0 ? 0 : 40, y: cardNumber == 0 ? 0 : -40)
If you're still having trouble, extract the card image and the card score to helper functions:
extension Card {
fileprivate var imageName: String { "\(suit.rawValue)\(rank)" }
}
struct PlayerView: View {
@ObservedObject var player: Player
var isDealt: Bool
var body: some View {
VStack {
ZStack {
ForEach(player.viewCards.indices, id: \.self) { cardNumber in
if player.viewCards[cardNumber] {
cardImage(for: player.hand[cardNumber], isFirst: cardNumber == 0)
}
}
}
.padding(.top, 140)
if isDealt {
cardScoreView
}
}
}
private func cardImage(for card: Card, isFirst: Bool) -> some View {
return Image(card.imageName)
.resizable()
.frame(width: 120, height: 160)
.offset(
x: isFirst ? 0 : 40,
y: isFirst ? 0 : -40)
}
private var cardScoreView: some View {
let aceAlt = player.hasAce ? " / \(player.cardScore + 10)" : ""
return Text("""
Card Score
\(player.cardScore)\(aceAlt)
"""
)
.font(.body)
.bold()
.foregroundColor(.white)
.lineLimit(2)
.multilineTextAlignment(.center)
}
}
Upvotes: 1
Reputation: 36304
assuming "player.viewCards[cardNumber]" returns a Bool, you could try re-structuring your if, like this:
if player.viewCards[cardNumber] {
if cardNumber == 0 {
Image(player.hand[cardNumber].suit + String(player.hand[cardNumber].rank) ).resizable().frame(width:120, height:160)
} else {
Image(player.hand[cardNumber].suit + String(player.hand[cardNumber].rank) ).resizable().frame(width:120, height:160).offset(x: 40, y: -40)
}
} else {
EmptyView()
}
Upvotes: 0