Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: stretch LinearGradient on a rectangle

I am trying to render a linear gradient on a rectangle. Here is the code:

Rectangle()
        .foregroundColor(Color.red)
        .overlay(
            LinearGradient(gradient: Gradient(colors: [.red, .yellow, .blue]), startPoint: .topLeading, endPoint: .bottomTrailing)
        )
        .frame(width: 300, height: 200)
        .position(x: 170, y: 120)

When I render it on square, everything looks correct:

square is great

When I render it on a rectangle, however, it stops looking like it's going from topLeading corner to bottomTrailing. It just looks like it's the same one which was clipped:

enter image description here

Here is how it's rendered in svg (blue is not the same it seems, but that's not the important part), and how I want it to look in swift:

enter image description here

The yellow diagonal should go directly from one corner to another, since I am specifying startPoint: .topLeading, endPoint: .bottomTrailing. It says here SwiftUI diagonal LinearGradient in a rectangle that this is a standard behaviour, and that's ok - I'm not trying to say SwiftUI renders it incorrectly, I just need a way to make it look like in svg.

like image 983
f3dm76 Avatar asked Aug 02 '26 10:08

f3dm76


1 Answers

What a fun problem :)

The "naive" approach would be to render the gradient as a square and then either rotate it or squeeze or stretch it to fit the rectangle. Rotating requires more maths so here's the "squeezing" version:

struct ContentView: View {
    var body: some View {
        
        Rectangle()
            .overlay(
                GeometryReader { g in
                    LinearGradient(
                        gradient: Gradient(colors: [.red, .yellow, .blue]),
                        startPoint: .topLeading,
                        endPoint: .bottomTrailing
                    )
                    .frame(width: g.size.width, height: g.size.width)
                    .scaleEffect(x: 1.0, y: g.size.height / g.size.width, anchor: .top)
                }
            )
            .frame(width: 300, height: 200)
        
    }
}

Notes:

  • To do the squeeze, we need to know the proportions of the rectangle, you were already using an overlay() and overlays are bounded by the Rectangle() in this case, so we can just read the size with the GeometryReader();
  • We need to start with the gradient applied to a square and there is a couple of ways to do squares. As we already know the size of the rectangle, I went with a square with a side equal to the width of the enclosing rectangle;
  • Then we apply the scaleEffect(), squeezing the height of the square to the height of the rectangle.

Please let me know if that's what you were looking for or if there is a better way to do to this!

Cheers, –Baglan

like image 107
Baglan Avatar answered Aug 05 '26 09:08

Baglan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!