简体   繁体   中英

How Do I Fade the Top or Bottom Edge of a SF Symbol in SwiftUI?

I'm trying to recreate a visual, that consist of 3 SF Symbols. The image in the middle is 100% solid no fade. I would like to fade top edge of the top most image and fade the bottom edge of the bottom most image.

I am using SF Symbols.

I would like to reproduce the following:

(fade top of image)

IMAGE

IMAGE

IMAGE

(fade bottom of image)

How would I accomplish this?

First, keep in mind that there are usage rules around SF Symbols. It may or may not be allowed to modify them in this way. In particular there is a list of "As-Is" symbols that cannot be used for arbitrary purposes. But that doesn't change much about the answer.

The second thing to note is that SF Symbols are a strange beast between text and images. They often act like text, and they often draw outside their frames . That creates problems. You can fix that by forcing them to be real images as discussed in the linked answer.

 
 
 
  
  let config = UIImage.SymbolConfiguration(scale: .large) let image = Image(uiImage: UIImage(systemName:"star.fill", withConfiguration: config)!)
 
 

In the end, as long as you can get an Image that has the correct frame, the rest of this will work. It doesn't matter that it's a SF Symbol.

Asperi's answer explains how to fix this by using resizable .

let image = Image(systemName: "star.fill").resizable().aspectRatio(contentMode: .fit)

(I've added aspectRatio so it doesn't distort.)

First, define your Gradient. This is how the fade will work, and you can configure it with a set of colors or individual color stops. The only thing that matters here is the opacity.

let fade =  Gradient(colors: [Color.clear, Color.black])

(See Asperi's answer for an example using stops, which give you more control over the fade.)

With that, you can apply this gradient as a mask:

let fade =  Gradient(colors: [Color.clear, Color.black])
let image = Image(systemName: "star.fill")
    .resizable().aspectRatio(contentMode: .fit).frame(height: 48)

struct ContentView: View {
    var body: some View {
        VStack {
            image
                .mask(LinearGradient(gradient: fade, startPoint: .top, endPoint: .bottom))
            image
            image
                .mask(LinearGradient(gradient: fade, startPoint: .bottom, endPoint: .top))
        }
    }
}

This raises issues about sizing. This approach is a flexible stack. It will grow to fill whatever container it's put in. You may want to constrain it a couple of ways.

First, you can decide the height of the entire stack by putting a frame on it:

    VStack {
        image
            .mask(LinearGradient(gradient: fade, startPoint: .top, endPoint: .bottom))
        image
        image
            .mask(LinearGradient(gradient: fade, startPoint: .bottom, endPoint: .top))
    }.frame(height: 128)
     ^^^^^^^^^^^^^^^^^^^

Or you could fix the size of each image instead:

let image = Image(systemName: "star.fill")
    .resizable().aspectRatio(contentMode: .fit).frame(width: 48)
                                               ^^^^^^^^^^^^^^^^^

顶部和底部渐变的三个符号

Here is my alternate (without UIKit usage)

在此处输入图像描述

struct TestSFSymbolsFade: View {
    var body: some View {
        VStack {
            Image(systemName: "ant.fill").resizable()
            Image(systemName: "ant.fill").resizable()
            Image(systemName: "ant.fill").resizable()
        }
        .mask(LinearGradient(gradient: Gradient(stops: [
            .init(color: .clear, location: 0),
            .init(color: .black, location: 0.25),
            .init(color: .black, location: 0.75),
            .init(color: .clear, location: 1)
        ]), startPoint: .top, endPoint: .bottom))
        .frame(width: 40, height: 160) // < just for demo, can be any or absent
    }
}

backup

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM