"Function declares an opaque return type [...]" error when declaring a view as a variable inside the body of a View in SwiftUI

Viewed 14829

Assume I have a View with an Image that has a shadow property:

struct ContentView: View {
    var body: some View {
        Image("turtlerock").shadow(radius: 10)
    }
}

Now imagine I want to access the value of the shadow radius. I assumed I could do this:

struct ContentView: View {
    var body: some View {
        let myImage = Image("turtlerock").shadow(radius: 10)
        print(myImage.modifier.radius)
    }
}

However, this returns an error:

Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type

Is there a way to accomplish this somehow?

5 Answers

When modifying and building views, you can do this without a return statement and a building block one above the other without commas. This is called a multi-statement closure. When you try to create a variable inside a multi-statement closure, the compiler is going to complain because there is a mismatch in types (you can only combine views one after another, nothing more). See this answer for more details: https://stackoverflow.com/a/56435128/7715250

A way to fix this is to explicitly return the views you are combining, so you don't make use of the multi-closure statements:

struct MyView: View {
    var body: some View {
        let image = Image("Some image").shadow(radius: 10)
        let myRadius = image.modifier.radius

        // Do something with myRadius

        return image // No multi closure statements.
    }
}

If your view you want to reference is inside a stack, you should declare it outside the stack like this:

var body: some View {
    let myImage = Image("image").shadow(radius: 10)

    let stack = HStack {
        myImage
        Image("image2")
    }

    return stack
}

You can define the image outside body:

let myImage = Image("turtlerock").shadow(radius: 10)

var body: some View {
  myImage
}

To print the radius you can do like so:

var body: some View {
  myImage
    .tapAction {
      print(self.myImage.modifier.radius) // 10.0
  }
}

When it happens to me in a testing environment I just nest everything in the body inside a

return ZStack{ ...}

A bit quick and dirty, but it works for my purposes.

I'm using Group {}:

func makeContentView() -> some View {
    Group {
        if some_condition_here {
            Text("Hello World")
                .foregroundColor(.red)
                .font(.system(size: 13, weight: .bold, design: .monospaced))
            } else {
                Rectangle()
                    .fill(Color.gray20)
            }
        }
    }
}
Related