偏好设置 2.5.0

偏好设置 2.5.0

Sindre Sorhus 管理。



偏好设置 2.5.0

Preferences

在几分钟内将偏好设置窗口添加到您的 macOS 应用中

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





系统要求

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

安装

Swift 包管理器

在 Xcode 中的“Swift 包管理器”选项卡下添加 https://github.com/sindresorhus/Preferences

Carthage

github "sindresorhus/Preferences"

CocoaPods

pod 'Preferences'

使用方法

在 Xcode 中运行 PreferencesExample 目标以尝试实时示例(需要 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 的版本,您必须为 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 并传递相应的视图控制器。然后为 Preferences… 菜单项添加一个操作出口以显示首选项窗口。

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() 来关闭首选项窗口。

推荐

在界面构建器中使用 NSGridView 是在每块中创建用户界面的最简单方法。请查看本仓库中的示例项目进行演示。

SwiftUI支持

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

运行此仓库中Xcode项目中的PreferencesExample目标,以查看实际示例。在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。我们将修复这个问题。《a href="https://github.com/sindresorhus/Preferences/pull/28">我们打算修复。

在 macOS 10.13 及更早版本中没有动画

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

常见问题解答 (FAQ)

如何本地化窗口标题?

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

  • 多个偏好设置标签页:使用当前选中的 preferencePaneTitle 作为窗口标题。本地化您的 preferencePaneTitle 以获取本地化的窗口标题。
  • 单个偏好设置标签页:将窗口标题设置为 `APPNAME Preferences`。应用名称是从您的应用包中获取的。您可以通过编辑其 Info.plist 来自定义标题。标题中的 “Preferences” 来自 “首选项…” 菜单项。从您包中获取应用程序名称的查找顺序
    1. CFBundleDisplayName
    2. CFBundleName
    3. CFBundleExecutable
    4. 如果遗漏了一些设置,将回退到 "<Unknown App Name>" 来显示问题。

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

这听起来不应该很难吧?然而,实际情况并非如此。

为什么它比 MASPreferences 更好?

  • 使用 Swift 编写。(没有桥接头!)
  • 使用协议的 Swift API。
  • 支持分段控制风格标签。
  • 支持 SwiftUI。
  • 完全文档化。
  • 遵循 macOS Human Interface Guidelines
  • 窗口标题将自动通过使用系统字符串进行本地化。

相关

您可能还会喜欢 Sindre 的 应用

在以下应用中使用

想要向世界介绍使用Preference的功能的应用?提交一个PR!

维护者