如何在 SwiftUI 中淡化 SF 符号的顶部或底部边缘?

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

我正在尝试重新创建一个包含 3 个 SF 符号的视觉效果。 中间的图像是 100% 纯色无褪色。 我想淡化最顶部图像的顶部边缘 并淡化最底部图像的底部边缘。

我正在使用顺丰符号。

我想重现以下内容:

(图像顶部淡化)

图像

图像

图像

(图像底部淡化)

我该如何完成?

首先,请记住 SF 符号有一些使用规则。可能允许也可能不允许以这种方式修改它们。特别是有一个列表 "As-Is" symbols 不能用于任意目的。但这并没有改变答案。

第二点要注意的是,SF Symbols 是介于文本和图像之间的怪兽。他们经常表现得像文字,而且经常。这会产生问题。 您可以通过将它们强制为链接答案中讨论的真实图像来解决这个问题。

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

最后,只要你能得到一个Image有正确的框架,剩下的就可以了。就算是SF Symbol也没关系

Asperi 的回答解释了如何使用 resizable.

解决此问题
let image = Image(systemName: "star.fill").resizable().aspectRatio(contentMode: .fit)

(我添加了 aspectRatio 所以它不会变形。)

首先,定义渐变。这就是淡入淡出的工作方式,您可以使用一组颜色或单个色标对其进行配置。这里唯一重要的是不透明度。

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

(有关使用停止的示例,请参阅 Asperi 的回答,这使您可以更好地控制淡入淡出。)

这样,您就可以将此渐变用作遮罩:

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))
        }
    }
}

这会引发有关大小调整的问题。这种方法是一个灵活的堆栈。它会长大以填满它放入的任何容器。您可能想通过几种方式对其进行约束。

首先,您可以通过在其上放置一个框架来决定整个堆栈的高度:

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

或者您可以改为固定每张图片的大小:

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

这是我的替代品(没有使用 UIKit)

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