设置

在几分钟内为你的 macOS 应用添加设置窗口

只需传入一些视图控制器,此软件包将处理其余事项。内置 SwiftUI 支持。

此软件包兼容 macOS 13,并在 macOS 13 及更高版本的窗口标题中自动使用 设置 而不是 偏好设置

此项目以前称为 Preferences

要求

macOS 10.13 及更高版本。

安装

在 Xcode 的 “Swift Package Manager” 选项卡中添加 https://github.com/sindresorhus/Settings

用法

运行 Example Xcode 项目以尝试实时示例(需要 macOS 11 或更高版本)。

首先,创建一些设置面板标识符

import Settings

extension Settings.PaneIdentifier {
	static let general = Self("general")
	static let advanced = Self("advanced")
}

其次,为你需要的设置面板创建几个视图控制器。与实现普通视图控制器的唯一区别是,你必须添加 SettingsPane 协议并实现 paneIdentifiertoolbarItemTitletoolbarItemIcon 属性,如下所示。如果使用 .segmentedControl 样式,则可以省略 toolbarItemIcon

GeneralSettingsViewController.swift

import Cocoa
import Settings

final class GeneralSettingsViewController: NSViewController, SettingsPane {
	let paneIdentifier = Settings.PaneIdentifier.general
	let paneTitle = "General"
	let toolbarItemIcon = NSImage(systemSymbolName: "gearshape", accessibilityDescription: "General settings")!

	override var nibName: NSNib.Name? { "GeneralSettingsViewController" }

	override func viewDidLoad() {
		super.viewDidLoad()

		// Setup stuff here
	}
}

注意:如果需要支持 macOS 11 之前的 macOS 版本,则必须为 toolbarItemIcon 添加后向兼容性

AdvancedSettingsViewController.swift

import Cocoa
import Settings

final class AdvancedSettingsViewController: NSViewController, SettingsPane {
	let paneIdentifier = Settings.PaneIdentifier.advanced
	let paneTitle = "Advanced"
	let toolbarItemIcon = NSImage(systemSymbolName: "gearshape.2", accessibilityDescription: "Advanced settings")!

	override var nibName: NSNib.Name? { "AdvancedSettingsViewController" }

	override func viewDidLoad() {
		super.viewDidLoad()

		// Setup stuff here
	}
}

如果需要间接响应操作,则设置窗口控制器会将响应链操作转发到活动面板(如果它响应选择器)。

final class AdvancedSettingsViewController: NSViewController, SettingsPane {
	@IBOutlet private var fontLabel: NSTextField!
	private var selectedFont = NSFont.systemFont(ofSize: 14)

	@IBAction private func changeFont(_ sender: NSFontManager) {
		font = sender.convert(font)
	}
}

AppDelegate 中,初始化一个新的 SettingsWindowController 并传入视图控制器。然后为 设置… 菜单项添加一个操作出口,以显示设置窗口。

AppDelegate.swift

import Cocoa
import Settings

@main
final class AppDelegate: NSObject, NSApplicationDelegate {
	@IBOutlet private var window: NSWindow!

	private lazy var settingsWindowController = SettingsWindowController(
		panes: [
			GeneralSettingsViewController(),
			AdvancedSettingsViewController()
		]
	)

	func applicationDidFinishLaunching(_ notification: Notification) {}

	@IBAction
	func settingsMenuItemActionHandler(_ sender: NSMenuItem) {
		settingsWindowController.show()
	}
}

设置选项卡样式

创建 SettingsWindowController 时,可以在基于 NSToolbarItem 的样式(默认)和 NSSegmentedControl 之间进行选择

// …
private lazy var settingsWindowController = SettingsWindowController(
	panes: [
		GeneralSettingsViewController(),
		AdvancedSettingsViewController()
	],
	style: .segmentedControl
)
// …

.toolbarItem 样式

NSToolbarItem based (default)

.segmentedControl 样式

NSSegmentedControl based

API

public enum Settings {}

extension Settings {
	public enum Style {
		case toolbarItems
		case segmentedControl
	}
}

public protocol SettingsPane: NSViewController {
	var paneIdentifier: Settings.PaneIdentifier { get }
	var paneTitle: String { get }
	var toolbarItemIcon: NSImage { get } // Not required when using the .`segmentedControl` style
}

public final class SettingsWindowController: NSWindowController {
	init(
		panes: [SettingsPane],
		style: Settings.Style = .toolbarItems,
		animated: Bool = true,
		hidesToolbarForSingleItem: Bool = true
	)

	init(
		panes: [SettingsPaneConvertible],
		style: Settings.Style = .toolbarItems,
		animated: Bool = true,
		hidesToolbarForSingleItem: Bool = true
	)

	func show(pane: Settings.PaneIdentifier? = nil)
}

与任何 NSWindowController 一样,调用 NSWindowController#close() 关闭设置窗口。

建议

在每个面板中创建用户界面的最简单方法是在 Interface Builder 中使用 NSGridView。有关演示,请参阅此仓库中的示例项目。

SwiftUI 支持

如果你的部署目标是 macOS 10.15 或更高版本,则可以使用捆绑的 SwiftUI 组件来创建面板。使用你的自定义视图和必要的工具栏信息创建 Settings.Pane(使用 AppKit 时为 SettingsPane)。

运行此仓库中 Xcode 项目中的 Example 目标,以查看真实示例。“帐户”选项卡在 SwiftUI 中。

还有一些捆绑的便捷 SwiftUI 组件,例如 Settings.ContainerSettings.Section,可自动实现与 AppKit 的 NSGridView 类似的对齐方式。还有一个 .settiingDescription() 视图修饰符,用于将文本样式设置为设置描述。

提示:Defaults 软件包可以非常轻松地持久化设置。

struct CustomPane: View {
	var body: some View {
		Settings.Container(contentWidth: 450.0) {
			Settings.Section(title: "Section Title") {
				// Some view.
			}
			Settings.Section(label: {
				// Custom label aligned on the right side.
			}) {
				// Some view.
			}
			
		}
	}
}

然后在 AppDelegate 中,初始化一个新的 SettingsWindowController 并将面板视图传递给它。

// …

private lazy var settingsWindowController = SettingsWindowController(
	panes: [
		Pane(
			 identifier: ,
			 title: ,
			 toolbarIcon: NSImage()
		) {
			CustomPane()
		},
		Pane(
			 identifier: ,
			 title: ,
			 toolbarIcon: NSImage()
		) {
			AnotherCustomPane()
		}
	]
)

// …

如果想将 SwiftUI 面板与标准 AppKit NSViewController 一起使用,请将面板视图包装到 Settings.PaneHostingController 中,然后像处理标准面板一样将它们传递给 SettingsWindowController

let CustomViewSettingsPaneViewController: () -> SettingsPane = {
	let paneView = Settings.Pane(
		identifier: ,
		title: ,
		toolbarIcon: NSImage()
	) {
		// Your custom view (and modifiers if needed).
		CustomPane()
		//  .environmentObject(someSettingsManager)
	}

	return Settings.PaneHostingController(paneView: paneView)
}

// …

private lazy var settingsWindowController = SettingsWindowController(
	panes: [
		GeneralSettingsViewController(),
		AdvancedSettingsViewController(),
		CustomViewSettingsPaneViewController()
	],
	style: .segmentedControl
)

// …

完整示例在此。.

后向兼容性

macOS 11 及更高版本支持 SF Symbols,可以方便地用于工具栏图标。如果需要支持较旧的 macOS 版本,则必须添加后备方案。Apple 建议即使对于较旧的系统也使用相同的图标。实现此目的的最佳方法是将相关的 SF Symbols 图标导出到图像,并将它们添加到你的 Asset Catalog。

已知问题

设置窗口不显示

当你不使用自动布局或未设置视图控制器的大小时,可能会发生这种情况。你可以通过使用自动布局或设置显式大小来解决此问题,例如,在 viewDidLoad() 中设置 preferredContentSize我们打算修复此问题。

macOS 10.13 及更早版本上没有动画

SettingsWindowController.initanimated 参数在 macOS 10.13 或更早版本上无效,因为这些版本不支持 NSViewController.TransitionOptions.crossfade

常见问题解答

如何本地化窗口标题?

SettingsWindowController 遵循 macOS 人机界面指南,并使用这组规则来确定窗口标题

为什么我应该使用它而不是手动实现它?

这不应该很难吧?好吧,事实证明它确实很难

MASPreferences 相比,它有什么优势?

相关

你可能还会喜欢 Sindre 的 应用

在这些应用中使用

想告诉全世界你的应用正在使用此软件包吗?打开一个 PR!

维护者