swift ui magic

struct TestView: View {
    var body: some View {
        var descriptionShape: any Shape = Circle()
        descriptionShape = Circle()
        
        return Text("description")
            .clipShape(descriptionShape)
    }
}

Why in the .clipShape(descriptionShape) occurs Type 'any View' cannot conform to 'View' error?

Answered by DTS Engineer in 760991022

There's no entirely satisfactory answer I can give you here, without knowing more about the larger context of your actual code.

You can solve the immediate problem by type-erasing the shape:

struct TestView: View {
    var body: some View {
        var descriptionShape = AnyShape(Circle())
        
        return Text("description")
            .clipShape(descriptionShape)
    }
}

Note the difference here: AnyShape is a concrete type, but it is "type erased" so it can wrap any kind of Shape. It does also conform to Shape. any Shape is an existential type that does not conform to Shape.

However, this is pretty much a brute force approach., and may not perform well if there are frequent View updates.

One alternative would be to make your choice of shape initially as (for example) one of several enum cases, rather than an actual Shape. That gives you the flexibility to manipulate the choice of shape without crossing into existential-land. At the end of the View body, you'd then use (for example) a switch statement to choose the correct return value.

This is likely a bit more code, but is going to perform better than using type erasure,

The problem arises from declaring descriptionSape as any Shape. That gives it an existential (protocol) type, not a concrete type.

OTOH, clipShape requires a concrete View type, but the compiler has no inference path leading from any Shape to any View to View.

How you would get around this depends what you're actually trying to do in your actual code. In this simplified code fragment, you could simply leave the type annotation off the declaration of descriptionShape, but probably doesn't solve the problem you started with.

here is a solution:

struct TestView: View {
    var body: some View {
        Text("description")
            .clipShape(getShape(position: .middle))
    }
}

struct TestView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

enum ElementPosition {
    case first
    case middle
}

func getShape(position: ElementPosition) -> some Shape {
    switch position {
    case .first:
        return AnyShape(Circle())
    case .middle:
        return AnyShape(Rectangle())
    }
}
Accepted Answer

There's no entirely satisfactory answer I can give you here, without knowing more about the larger context of your actual code.

You can solve the immediate problem by type-erasing the shape:

struct TestView: View {
    var body: some View {
        var descriptionShape = AnyShape(Circle())
        
        return Text("description")
            .clipShape(descriptionShape)
    }
}

Note the difference here: AnyShape is a concrete type, but it is "type erased" so it can wrap any kind of Shape. It does also conform to Shape. any Shape is an existential type that does not conform to Shape.

However, this is pretty much a brute force approach., and may not perform well if there are frequent View updates.

One alternative would be to make your choice of shape initially as (for example) one of several enum cases, rather than an actual Shape. That gives you the flexibility to manipulate the choice of shape without crossing into existential-land. At the end of the View body, you'd then use (for example) a switch statement to choose the correct return value.

This is likely a bit more code, but is going to perform better than using type erasure,

swift ui magic
 
 
Q