Iterate over collection of 'shapes'

I'm trying to create a Set of cards. Each card has an object assigned, that is then shown with a different colour and appears in different numbers on each card.

The issue appears when trying to work with the "Shape" Type associated with my individual Cards. The error message reads:

"Value of protocol type 'Shape' cannot conform to 'View'; only struct/enum/class types can conform to protocols"

import Foundation

import SwiftUI

var setCardSet: Array<SetCard> = [ ]

var counter: Int = 0

func createSet() -> Array<SetCard> {

for object in 0..<3 {

    var chosenShape = objectLibrary[object]

    for numberOfShapes in 0..<3 {
        counter += 1

        setCardSet.append(SetCard(id: counter, shape: chosenShape, numberOfShapes: numberOfShapes))
        }
    }

return setCardSet

}

struct SetCard: Identifiable {
    var id: Int
    var shape: Shape
    var numberOfShapes: Int
}

var possibleSetCardShapes = [Diamond(), Tube(), Tube()] **as** [ **Any** ]

struct GameObject {

var name: String

var object: Shape

}

let objectLibrary = [Diamond(), Tube(), Swirl()]

At a later step, the individual objects are shown and "stacked" on the same card:

struct CardView: View {
var card: SetCard
var body: some View {
    ZStack {
        VStack{
            ForEach(0 ..< card.numberOfShapes+1) {_ in
                card.shape
                }
            }
        }
    }
}

In CardView you try to create a VStack of the card's shape property. For this to work, card.shape needs to be a type that conforms to the View protocol. In your code, card.shape conforms to the Shape type.

It looks like Shape is a protocol. So can you just get Shape to conform to View to fix this? Unfortunately it's not that simple. The definition of the View protocol looks like this:

public protocol View {
    associatedtype Body : View
    @ViewBuilder var body: Self.Body { get }
}

In particular, it has an assocciatedtype. That means you can't write something like this:

struct SetCard: Identifiable {
    var id: Int
    var shape: Shape // Error
    var numberOfShapes: Int
}

The problem is that when we make a SetCard we'd have no way of knowing what the associated Body type is of the shape, which the compiler needs to know.

I think your best solution (though there are others) is to make the shapes an existing type that conforms to View. For example, what about Image? You could have

struct SetCard: Identifiable {
    var id: Int
    var shape: Image
    var numberOfShapes: Int
}

let diamond = Image(systemName: "suit.diamond")
let card = SetCard(id: 1, shape: diamond, numberOfShapes: 3)

Then card.shape could be happily used in a VStack

I hope that helps

1 Like

Thanks, that helps!

I found a getaround on stack overflow

struct AnyShape: Shape {
    private let builder: (CGRect) -> Path

    init<S: Shape>(_ shape: S) {
        builder = { rect in
            let path = shape.path(in: rect)
            return path
        }
    }

    func path(in rect: CGRect) -> Path {
        return builder(rect)
    }
}

To then defining my "shapes" as AnyShape.

I haven't fully understood why it works, but your explanation has clarified that.

This post was flagged by the community and is temporarily hidden.

Terms of Service

Privacy Policy

Cookie Policy