Rob N
Rob N

Reputation: 16399

Statement in SwiftUI closure body causes type error

This modifier on a SwiftUI view compiles fine:

.background(GeometryReader { p in
    return Rectangle().opacity(0)
})

(I know I don't need the return, but I'm about to add another line.)

When I add a print call, it no longer compiles.

.background(GeometryReader { p in
    print("hi")
    return Rectangle().opacity(0)
})

The error points to background and says:

Expression type '(_, Alignment) -> some View' is ambiguous without more context

I don't understand why it's confused now about the type of thing passed to .background(...). It's got the same clear return expression as before. How can I fix the code to satisfy the type checker?

Upvotes: 2

Views: 512

Answers (2)

Sweeper
Sweeper

Reputation: 270980

This error occurs because multi-statement closures don't take part in type inference. The Content generic parameter of the initialiser of GeometryReader cannot be inferred because the only information you provide to it, the closure, is not considered for type-inference!

So you just need to specify what type you are returning:

.background(GeometryReader {
    p -> Rectangle in // note here
    print("Hello")
    return Rectangle()
})

You probably shouldn't do this though, because you are supposed to only put Views in a view builder closure. SwiftUI is designed to be very declarative, almost its own DSL, I would say.

Edit: I found this answer, which adds a View called Print. I guess you can put one of these alongside whatever view you are setting the background of, in a Group.

Upvotes: 3

jnblanchard
jnblanchard

Reputation: 1200

Writing my own modifier I was able to create a background for views. Sweeper I think has the right idea, you'll need to add a return statement because geometry reader is no longer a single line implicit return.

extension View {
  func myBackground() -> some View {
    print("hi")
    return background(GeometryReader { geometry in
      Rectangle().path(in: geometry.frame(in: .local))
    })
  }
}

Then I tested the modifier in the context below.

struct ContentView: View {

  var body: some View {
    Text("Foreground Label").foregroundColor(.green).myBackground()
  }

}

Upvotes: 1

Related Questions