偏好设置 2.5.0

偏好设置 2.5.0

Sindre Sorhus维护。



Preferences

将偏好设置窗口添加到您的macOS应用仅需几分钟

只需传入一些视图控制器,这个包将处理其余部分。内置SwiftUI支持。





要求

  • macOS 10.10+
  • Xcode 12.5+
  • Swift 5.4+

安装

Swift包管理器

Xcode的“Swift包管理器”标签页中添加https://github.com/sindresorhus/Preferences

Carthage

github "sindresorhus/Preferences"

高速 conflicts-tooltipJapanese

pod 'Preferences'

使用方法

在 Xcode 中运行 PreferencesExample target 以尝试实时示例(需要 macOS 11 或更高版本)。

首先,创建一些首选项面板标识符

import Preferences

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

其次,创建您想要的首选项面板的视图控制器。与实现正常的视图控制器相比,唯一的区别是您必须添加 PreferencePane 协议并实现 preferencePaneIdentifiertoolbarItemTitletoolbarItemIcon 属性,如下所示。如果您正在使用 .segmentedControl 风格,则可以省略 toolbarItemIcon

GeneralPreferenceViewController.swift

import Cocoa
import Preferences

final class GeneralPreferenceViewController: NSViewController, PreferencePane {
	let preferencePaneIdentifier = Preferences.PaneIdentifier.general
	let preferencePaneTitle = "General"
	let toolbarItemIcon = NSImage(systemSymbolName: "gearshape", accessibilityDescription: "General preferences")!

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

	override func viewDidLoad() {
		super.viewDidLoad()

		// Setup stuff here
	}
}

注意:如果您需要支持低于 macOS 11 的 macOS 版本,您必须为 fallback for the toolbarItemIcon 添加备用。

AdvancedPreferenceViewController.swift

import Cocoa
import Preferences

final class AdvancedPreferenceViewController: NSViewController, PreferencePane {
	let preferencePaneIdentifier = Preferences.PaneIdentifier.advanced
	let preferencePaneTitle = "Advanced"
	let toolbarItemIcon = NSImage(systemSymbolName: "gearshape.2", accessibilityDescription: "Advanced preferences")!

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

	override func viewDidLoad() {
		super.viewDidLoad()

		// Setup stuff here
	}
}

如果您需要间接响应操作,PreferencesWindowController 将将响应链操作转发到活动面板,如果活动面板响应该选择器。

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

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

AppDelegate 中,初始化一个新的 PreferencesWindowController 并将视图控制器传递给它。然后添加一个操作出口来显示首选项窗口。

AppDelegate.swift

import Cocoa
import Preferences

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

	private lazy var preferencesWindowController = PreferencesWindowController(
		preferencePanes: [
			GeneralPreferenceViewController(),
			AdvancedPreferenceViewController()
		]
	)

	func applicationDidFinishLaunching(_ notification: Notification) {}

	@IBAction
	func preferencesMenuItemActionHandler(_ sender: NSMenuItem) {
		preferencesWindowController.show()
	}
}

首选项选项卡样式

在创建 PreferencesWindowController 时,您可以选择基于 NSToolbarItem 的样式(默认)和 NSSegmentedControl

//
private lazy var preferencesWindowController = PreferencesWindowController(
	preferencePanes: [
		GeneralPreferenceViewController(),
		AdvancedPreferenceViewController()
	],
	style: .segmentedControl
)
//

.toolbarItem样式

NSToolbarItem based (default)

.segmentedControl样式

NSSegmentedControl based

API

public enum Preferences {}

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

public protocol PreferencePane: NSViewController {
	var preferencePaneIdentifier: Preferences.PaneIdentifier { get }
	var preferencePaneTitle: String { get }
	var toolbarItemIcon: NSImage { get } // Not required when using the .`segmentedControl` style
}

public final class PreferencesWindowController: NSWindowController {
	init(
		preferencePanes: [PreferencePane],
		style: Preferences.Style = .toolbarItems,
		animated: Bool = true,
		hidesToolbarForSingleItem: Bool = true
	)

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

	func show(preferencePane: Preferences.PaneIdentifier? = nil)
}

与任何 NSWindowController 一样,调用 NSWindowController#close() 关闭首选项窗口。

推荐

在每个面板中创建用户界面的最简单方法是在 Interface Builder 中使用 NSGridView。请参见本存储库中的示例项目。

SwiftUI 支持

如果您的部署目标为 macOS 10.15 或更高版本,您可以使用捆绑的 SwiftUI 组件来创建面板。使用您的自定义视图和必要的工具栏信息创建一个 Preferences.Pane(当使用 AppKit 时,取代 PreferencePane)。

运行本存储库中 Xcode 项目中的 PreferencesExample 目标,查看实际示例。Accounts 标签位于 SwiftUI 中。

还有一些捆绑的便利 SwiftUI 组件,如 Preferences.ContainerPreferences.Section,以便自动实现与 AppKit 的 NSGridView 相似的对齐。还有一个 .preferenceDescription() 视图修饰符,可以用于将文本样式设置为偏好设置描述。

提示:Defaults 软件包使用户可以轻松保存偏好设置。

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

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

//

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

//

如果您想同时使用 SwiftUI 面板和标准 AppKit NSViewController,请将面板视图包装进 Preferences.PaneHostingController,然后将它们作为标准面板一样传递给 PreferencesWindowController

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

	return Preferences.PaneHostingController(paneView: paneView)
}

//

private lazy var preferencesWindowController = PreferencesWindowController(
	preferencePanes: [
		GeneralPreferenceViewController(),
		AdvancedPreferenceViewController(),
		CustomViewPreferencePaneViewController()
	],
	style: .segmentedControl
)

//

完整的示例请参见此处。.

向后兼容性

macOS 11 及更高版本支持 SF Symbols,可以作为工具栏图标方便地使用。如果您需要支持较旧的 macOS 版本,则需要添加回退。Apple 建议即使是旧系统也使用相同的图标。实现此目的的最佳方法是导出相关的 SF Symbols 图标到图像中,并将它们添加到您的资产目录中。

已知问题

偏好设置窗口未显示

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

在macOS 10.13及以下版本中没有动画效果

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

常见问题解答

如何本地化窗口标题?

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

  • 多个偏好设置面板:使用当前选定的preferencePaneTitle作为窗口标题。本地化您的preferencePaneTitle以获取本地化窗口标题。
  • 单个偏好设置面板:将窗口标题设置为APPNAME Preferences。应用名称从您的应用包中获取。您可以本地化其Info.plist以自定义标题。Preferences部分摘自“偏好设置…”菜单项,见#12。从您的包中查找应用名称的顺序
    1. CFBundleDisplayName
    2. CFBundleName
    3. CFBundleExecutable
    4. 如果您丢失了一些设置,将回退到"<未知应用名称>"以显示。

为什么我会选择使用它而不是手动实现呢?

这难道不是一项简单的任务吗?事实上,并非如此。

它比 MASPreferences 有什么优点?

  • 使用 Swift 编写。 (无需桥接头!)
  • 使用协议的 Swifty API。
  • 支持分段控制风格选项卡。
  • 支持 SwiftUI。
  • 全面文档。
  • 遵循 macOS 人机界面指南
  • 窗口标题自动使用系统字符串进行本地化。

相关

您可能还会喜欢 Sindre 的 应用程序

在这些应用中使用

想要告诉世界你的应用正在使用 Preferences?请提交一个 PR!

维护者