I am working on a custom SwiftUI button that has a special shape depending on configuration.
A utility function must return a Shape depending on configuration. Since the path/shape is complicated to programme, transformations are used to get variations depending on configuration.
For demonstration purpose, a simplified version of the utility function looks like this:
func shape(i: Int) -> some Shape {
let path = Path()
switch i {
case 0:
return path
case 1:
return path.transform(CGAffineTransform(translationX:0,y:0))
default:
break
}
return path
}
Unfortunately the compiler generate the well known error:
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
I am sure this happens due to the transformation, although it returns a struct
func transform(_ transform: CGAffineTransform) -> TransformedShape<Self>
that conforms to Shape (like Path do):
struct TransformedShape<Content> where Content : Shape
Documentation for Path states that Path conforms to Shape, and this makes me think that I am overlooking something or that this may be a bug in the compiler.
If someone knows a workaround or can see that I am doing something wrong - please let me known.
Thanks.
The problem is that you're promising to return a specific type, but you return two different types: Path and TransformedShape. To fix that, always return TransformedShape by applying CGAffineTransform.identity
in cases where you don't want to change the shape. For example in your case:
func shape(i: Int) -> some Shape {
let path = Path()
switch i {
case 0:
return path.transform(.identity)
case 1:
return path.transform(CGAffineTransform(translationX:0,y:0))
default:
break
}
return path.transform(.identity)
}
Though I would probably write it this way (assuming that there are more cases, and that the zero-translate transform is just for demonstration):
func shape(i: Int) -> some Shape {
let transform: CGAffineTransform
switch i {
case 1:
transform = CGAffineTransform(translationX:0, y:0)
default:
transform = .identity
}
return Path().transform(transform)
}
some
types do not mean "any type that conforms." They are opaque types, which means "this function returns some specific, known-at-compile-time type, but don't tell the caller what precise type it is."
You must return the same concrete type in all branches when using a some
return
In your case, I’d set up identity transforms so that you always end up with the same type.
Alternatively, you could erase the type. Depending on what you are doing, it could cause performance issues.
let result: Shape = path
return result
If you do this as part of a SwiftUI View hierarchy, then you will slow down the differ when it inspects this part of the view tree. You should not do that if you can help it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With