Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI - How to add foreground linear gradient on image

I am unable to find any related documentation on how to do a linear gradient on the foreground for an image I have with SwiftUI.

I have tried to do it like so:

Image("IconLoseWeight")
  .frame(width: 30.0, height: 30.0)
  .padding(.leading, 17)
  .foregroundColor(LinearGradient(gradient: Gradient(colors: [.white, .black]), startPoint: .top, endPoint: .bottom))

Actually, the code shown above doesn't display any errors, but it breaks the code with warnings that make no sense in the top level Stacks (which I think is a bug with Xcode or SwiftUI). If I remove the foreground modifier, the code runs perfectly.

like image 207
Rodrigo Mata Avatar asked Jul 01 '19 22:07

Rodrigo Mata


People also ask

How do I fill a gradient image?

The Gradient Fill Image is added to the Shader Tree using the Add Layer option found under Add Layer > Image Map > image > Gradient Fill. Once created, the layer itself can be positioned, and its Effect defined in the Shader Tree.

How do I set gradient background color in SwiftUI?

One way to do it is using Color(red: ..., green: ..., blue: ...) . You can provide the RGB (Red, Green, blue) value on a 0-1 range. You can also initialize color with UIColor (from UIKit) and NSColor (from AppKit). Let's try to initialize hot pink with UIColor this time.


Video Answer


4 Answers

That's because foregroundColor wants a Color, but LinearGradient is a struct that conforms to the protocols ShapeStyle and View.

If I understand you correctly you want to fill the intransparent area of an image with a gradient?

ZStack {
  Color.white // For the background. If you don't need a background, you don't need the ZStack.
  LinearGradient(gradient: Gradient(colors: [.green, .blue]), startPoint: .top, endPoint: .bottom)
    .mask(Image("AssetWithTransparency")
      .resizable()
      .padding()
      .aspectRatio(contentMode: .fit))
  }.cornerRadius(15)

The result looks like this:

enter image description here

like image 94
RyuX51 Avatar answered Oct 21 '22 23:10

RyuX51


The task here is to display gradient over an image. To display one view over another SwiftUI provides ZStack view, so, the code can have the next structure:

ZStack {
    <Image>
    <Rectangle with gradient>
}

Additionally, to make sure the image we use is resized correctly to the specified frame resizable modifier should be applied with correct contentMode:

Image("IconLoseWeight")
    .resizable()                     // Make it resizable
    .aspectRatio(contentMode: .fit)  // Specifying the resizing mode so that image scaled correctly

After all, we need to apply frame and padding parameter to ZStack so that gradient has the same size as the image.

The result would look like that:

ZStack {
    Image("IconLoseWeight")
        .resizable()                    // Making the image resizable to the container size
        .aspectRatio(contentMode: .fit) // Setting up resizing mode so that the image scaled correctly

    Rectangle()                         // Shapes are resizable by default
        .foregroundColor(.clear)        // Making rectangle transparent
        .background(LinearGradient(gradient: Gradient(colors: [.clear, .black]), startPoint: .top, endPoint: .bottom), cornerRadius: 0)   
                                        // Specifying gradient (note that one color is .clear)
}
.frame(width: 30, height: 30)           // Applying frame
.padding(.leading, 17)                  // Applying padding

Note, that we use a gradient from .clear to .black as we need a transparent gradient to make the image visible.

like image 43
Denis Avatar answered Oct 22 '22 00:10

Denis


Agree with @RyuX51's answer and it's working well. But some how size and alignment of my image got changed. Because LinearGradient's frame isn't set.
So here i came up with the solution for just applying gradient to the Image,

VStack{
    Spacer()
    Button(action: {
        print("Add Photos")
    }, label: {

        LinearGradient(gradient: Gradient(colors: [.green, .blue]), startPoint: .top, endPoint: .bottom)
            .mask(Image(systemName: "plus.circle.fill")
            .resizable()
            .aspectRatio(contentMode: .fit)
        ).frame(width: 70, height: 70, alignment: .center)
    })
}
like image 3
Anjali Kevadiya Avatar answered Oct 22 '22 00:10

Anjali Kevadiya


The best way to do this as of the most recent SwiftUI release would be to use the .foregroundStyle() view modifier. I'm not sure when this approach became available but this code was tested with Xcode 14 and iOS 16.

Sample code below:

let gradient = Gradient(colors: [.purple, .cyan, .orange])

var body: some View {
    Image(systemName: "figure.strengthtraining.traditional")
        .font(.title)
        .foregroundStyle(.linearGradient(gradient, startPoint: .top, endPoint: .bottom))
}
like image 1
Chris Gaafary Avatar answered Oct 22 '22 00:10

Chris Gaafary