SwiftUI在macOS上,如何在按钮上使用自定义图标符号?

4

请仔细阅读问题,这是一款MAC应用程序,systemName在mac OS上不可用。

我正在尝试使用SwiftUI在macOS上构建一个简单的应用程序,但是我遇到了一些问题,无法显示某些图标。

现在我已经阅读了任何地方都需要下载SF Symbols应用并导出符号才能使用它们的信息,因此我这样做了,然后将导出的符号添加到.xcassets中,现在我正在尝试创建一个带有图像的按钮,因此以下是代码:

import SwiftUI

struct ActionBar: View {

    var body: some View {
        HStack {
            Spacer()
            Button(action: {

            }) {
                Image("Plus")
                    .font(Font.system(size: 24, weight: .light))
                    .foregroundColor(Color.red)
                Text("Test")
            }
        }
        .frame(maxWidth: .infinity)
        .padding()
        .background(Color.init(red: 0.8, green: 0.8, blue: 0.8))
    }
}

struct ActionBar_Previews: PreviewProvider {
    static var previews: some View {
        ActionBar()
    }
}

我尝试了许多变化,例如:

Image(nsImage: NSImage(name: "Plus"))

但是所有的信息,包括苹果自己的文档都只讨论了UIImage,这是我所理解的UIKit的一部分,它是iOS版本的UI框架,有人将其用于macOS吗?

非常感谢!

编辑:该资产被导入为Image Symbol Set,Xcode没有抛出任何错误,因为我只是使用SF Symbols应用程序生成的svg并直接放置在资源上。

编辑2:我刚刚遇到了这篇帖子,其中指出SVG支持不稳定... 我尝试将SVG转换为PNG,但xcode不接受PNG作为符号集... 所以,我想这个功能就是坏掉了?太糟糕了...


据苹果公司文档记载,SF Symbols 官方并不支持 macOS。如果您尝试使用它们,只能采用 hacky 方法,不能保证一定有效。 - Asperi
4个回答

6
在 MacOS 中,没有可用的 systemImages。但是 NSImage.Name 提供了替代选项。这个 链接 展示了大部分 MacOS 的系统默认图标。
下面是一个关于 NSImage.Name 的示例:
Icon(NSImage.refreshTemplateName)

以下是 Icon 视图的定义:
struct Icon: View {
    var image: NSImage.Name
    var body: some View {
        Image(nsImage: NSImage(named: image)!)
            .renderingMode(.original)
            .resizable()
            .scaledToFit()
    }
    init(_ image: NSImage.Name){
        self.image = image
    }
}

P.S. Image视图没有名为.font()的修饰符。请查看其文档以获取正确的语法。

编辑: 您可以通过遵循以下格式来跳过额外的视图。

Image(nsImage: NSImage(name: NSImage.addTemplateName))

NSImage.Names 的列表在此处可找到:这里

嗯,这与我所寻找的相关,但并非完全符合我的要求,不过还是谢谢你,也许以后我会尝试一下。目前我决定使用图片作为图标,这是更直接的方法。 - Oscar Franco
1
@Oscar,我已经添加了一个编辑。这是Xcode内置的最直接的选项。其他选项需要从其他应用程序导入图像并手动链接,需要更多的工作。 - NikzJon
那个完美地解决了问题,虽然没有完全回答我的问题,但是这是目前最好的选择,非常感谢!我希望我能选择多个正确答案 :) - Oscar Franco
太棒了!正是我正在寻找的。 - John Smith

1

2021年,SwiftUI 2

使用SF Symbols符号库的示例

import SwiftUI

@available(OSX 11.0, *)
public extension Text {
    static func sfSymbol( _ sysName: String) -> Text {
        return Text(Image(systemName: sysName))
    }
    
    /// You need to use .fixedSize() manually!!!!
    static func sfIcon( _ sysName: String, size: CGFloat) -> Text {
        return Text(Image(systemName: sysName))
            .font(.system(size: size))
    }
    
    
    static func sfSymbolAndText(sysName: String, text: String) -> Text {
        return Text("\(sysName.asSfSymbol())\(text)")
    }
}

@available(OSX 11.0, *)
public extension String {
    func asSfSymbol() -> Image {
        Image(systemName: self)
    }
}

用法:

Text.sfIcon("doc.on.doc.fill", size: 13)

Text.sfSymbol("doc.on.doc.fill")

将SF符号放在文本中间:

Text("Start of the text \( "doc.on.doc.fill".asSfSymbol() ) end of the text"

1

你不需要自己下载或导出符号,只需这样做,它将在iOS、Mac Catalyst以及EDIT:macOS Monterey和macOS 11上运行:

import SwiftUI

struct ContentView: View {
var body: some View {
    HStack {
        Spacer()
        Button(action: {

        }) {
            Image(systemName: "plus")
                .font(Font.system(size: 24, weight: .light))
                .foregroundColor(Color.red)
            Text("Test")
        }
    }
    .frame(maxWidth: .infinity)
    .padding()
    .background(Color.init(red: 0.8, green: 0.8, blue: 0.8))
}
}

2
systemName 只能在 iOS 上使用,这是一款 Mac 应用程序。 - Oscar Franco
这适用于macOS,使用Mac Catalyst。在Xcode中,在部署信息中勾选Mac框。 - workingdog support Ukraine
我需要特定于Mac OS的功能,因此对我来说,使用Catalyst不是一个选项,但感谢您的答案,我以后可能会尝试一下。 - Oscar Franco
@workingdog,你能具体说明一下吗?我在“部署信息”中找不到“Mac”的选项框? - mobibob
我找到了这个网址:https://developer.apple.com/tutorials/mac-catalyst/turning-on-mac-catalyst,但我的项目并没有显示所有的操作系统选项。 - mobibob
哈,我明白了,如果你从一个iOS应用程序开始并添加Mac Catalyst选项,那么这些选项就会出现。然而,我有一个仅适用于macOS的应用程序,“systemName”肯定是可用的。我展示的代码在我使用的macOS 12.beta上可以工作,在使用xcode 13.beta针对macOS 12的目标时也可以工作。 - workingdog support Ukraine

1

这是我的解决方案,但这很大程度上是一个hack,我不知道苹果是否会批准。所以我想这个答案只适用于“教育目的”。

概念

主要思路是修改SF Symbols应用程序导出的SVG文件,使其仅包含所选符号的一个实例,而不是完整模板,并使用SVGKit框架通过NSViewRepresentable对象显示它。

导出

在您的Mac上使用SF Symbols应用程序并选择要使用的符号,然后执行“导出自定义符号模板”。

修改

在文本编辑器(如Sublime Text或CotEditor)中打开导出的SVG文件。

删除文件中除标题和“Symbols”字段之外的所有内容,该字段应仅包含您想要使用的符号变体。然后更改呈现画布的大小并正确对齐符号。

例如,如果您想使用“plus”符号的“Regular-L”版本,则最终的“plus.svg”文件应该像这样:

<?xml version="1.0" encoding="UTF-8"?>
<!--Generator: Apple Native CoreSVG 123-->
<!DOCTYPE svg
PUBLIC "-//W3C//DTD SVG 1.1//EN"
       "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
 <!--glyph: "uni10017C.medium", point size: 100.000000, font version: "Version 15.0d7e11", template writer version: "5"-->

 <g id="Symbols">
  <g id="Regular-L" transform="matrix(1 0 0 1 0 100)">
   <path d="M 67.4316 17.3828 C 70.0684 17.3828 72.2168 15.2832 72.2168 12.7441 L 72.2168 -30.3711 L 114.062 -30.3711 C 116.65 -30.3711 118.848 -32.5684 118.848 -35.1562 C 118.848 -37.793 116.65 -39.9414 114.062 -39.9414 L 72.2168 -39.9414 L 72.2168 -83.1055 C 72.2168 -85.6445 70.0684 -87.7441 67.4316 -87.7441 C 64.8438 -87.7441 62.6953 -85.6445 62.6953 -83.1055 L 62.6953 -39.9414 L 20.8496 -39.9414 C 18.2617 -39.9414 16.0645 -37.793 16.0645 -35.1562 C 16.0645 -32.5684 18.2617 -30.3711 20.8496 -30.3711 L 62.6953 -30.3711 L 62.6953 12.7441 C 62.6953 15.2832 64.8438 17.3828 67.4316 17.3828 Z"/>
  </g>
</g>
</svg>

总之:
- 保留标题 - 保留`id="Symbols"`字段 - 在符号字段中,仅保留一个类别,在我的示例中为`id="Regular-L"` - 更改标题中的大小,在我的示例中为`width="128" height="128"` - 通过更改变换字段的最后两个参数来对齐符号,在我的示例中为`transform="matrix(1 0 0 1 0 100)"`(x 0和y 100)
安装SVGKit:
- 关闭Xcode - 使用Cocoapods版本安装SVGKit(https://github.com/SVGKit/SVGKit) - 打开Cocoapods创建的.xcworkspace文件
制作视图:
将修改后的`plus.svg`文件添加到项目中(不在资源目录中,而是在项目本身中)
像这样添加一个`NSViewRepresentable`结构:
import SVGKit

struct IconView: NSViewRepresentable {

    let name: String

    func makeNSView(context: Context) -> SVGKFastImageView {
        let img = SVGKImage(named: name)!
        return SVGKFastImageView(svgkImage: img)!
    }

    func updateNSView(_ nsView: SVGKFastImageView, context: Context) {
        // not implemented
    }

}

使用视图

然后在您的ContentView中使用它(给它一个框架,以便它不会填满整个窗口),例如:

struct ContentView: View {
    var body: some View {
        VStack {
            IconView(name: "plus")
                .frame(width: 200, height: 200, alignment: .center)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

谢谢你的回答,虽然只有一个图像在SVG中需要很多工作,但在这一点上,我认为将图标简单地导出为PNG并以此方式使用可能更有意义。你怎么看? - Oscar Franco
这取决于您是否需要调整图像大小。使用我的解决方案,图像是矢量SVG,因此可以在任何大小下显示,而不会创建伪影或丢失细节。如果您使用PNG,则无法调整其大小,必须按照PNG文件定义的大小使用它。 - Eric Aya
好的,如果它是一张图片,那么它会根据设备的分辨率进行调整,对吧?但无论如何,目前似乎对此的支持不存在,非常感谢! - Oscar Franco
例如:如果您可以在应用程序中不调整大小地使用模板图像,则nuke1010的答案很好,但是一旦调整大小,它就会变得像素化。使用SVG,您可以以任何大小显示。但正如您所说,无论如何都没有官方支持SVG,而我的答案只是一种技巧,可以使用SF符号,否则确实过度冗长。 :) - Eric Aya
请注意,Xcode 12 已经为在 macOS 11 上运行的 Mac 应用程序添加了对 SF Symbols 的支持。 - Eric Aya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接