macos 如何为 Mac 创建 Cocoa App 首选项?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10148788/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create Cocoa App Preferences for Mac?
提问by Cristian
I have created a simple app in Xcode for Mac, it builds and compiles fine.
我在 Xcode 中为 Mac 创建了一个简单的应用程序,它可以很好地构建和编译。
How do I create a menu for preferences? Is there an easy way or do I have to create a new interface? How do I then get and put values into these preferences?
如何为首选项创建菜单?有没有简单的方法或者我必须创建一个新界面?那么我如何获取并将值放入这些偏好中?
I did find one tutorial but it was for iOS, and from what I can see the 'Settings bundle' isn't available if you're developing for Mac.
我确实找到了一个教程,但它是针对 iOS 的,从我所看到的,如果您正在为 Mac 开发,则“设置包”不可用。
EDIT: The following link is perfect for this: https://developer.apple.com/cocoa/cocoabindings.html
编辑:以下链接非常适合:https: //developer.apple.com/cocoa/cocoabindings.html
回答by Vervious
This is the documentation you want.More specifically How to provide a Preferences Interface.
Anyhow, you'll have to make your own preferences menu/window as you would make any other window, and have some controls that allow modifying values that are then stored in the User Defaults dictionary, which is where user preferences are usually stored (a process which Cocoa Bindings makes extremely easy).
无论如何,您必须像制作任何其他窗口一样制作自己的首选项菜单/窗口,并具有一些允许修改值的控件,这些值然后存储在用户默认值字典中,这是用户首选项通常存储的位置(一个Cocoa Bindings 制作的过程非常简单)。
Otherwise, manually, you would probably want a reference to [NSUserDefaults standardUserDefaults]
so you can -setObject:ForKey:
and so forth. It would be wise to read the entire documentation linked to above (including the other sections).
否则,手动,您可能需要一个引用,[NSUserDefaults standardUserDefaults]
以便您可以-setObject:ForKey:
等等。阅读上面链接的整个文档(包括其他部分)是明智的。
For more information on how Cocoa Bindings can be used to interface with NSUserDefaults
to store/retrieve preferences, see the Apple docs here.
有关如何使用 Cocoa Bindings 与NSUserDefaults
存储/检索首选项进行交互的更多信息,请参阅此处的 Apple 文档。
回答by Richard H.
For the window itself, i would recommend the RHPreferencesframework.
对于窗口本身,我会推荐RHPreferences框架。
Available on GitHub. BSD Licensed.
Its a simple and easy Preferences window controller with multiple tabs for your next Mac application.
它是一个简单易用的首选项窗口控制器,为您的下一个 Mac 应用程序提供多个选项卡。
It also provides:
它还提供:
- Auto resizing between different sized tab views (With animation)
- Custom NSToolbarItem support
- Persistence of the last used tab
- Support for placeholder NSToolbarItems (eg NSToolbarFlexibleSpaceItemIdentifier & NSToolbarShowFontsItemIdentifier)
- 在不同大小的选项卡视图之间自动调整大小(带动画)
- 自定义 NSToolbarItem 支持
- 上次使用的选项卡的持久性
- 支持占位符 NSToolbarItems(例如 NSToolbarFlexibleSpaceItemIdentifier 和 NSToolbarShowFontsItemIdentifier)
回答by Vlad
NSTabViewController.TabStyle.toolbar– A style that automatically adds any tabs to the window's toolbar. The tab view controller takes control of the window's toolbar and sets itself as the toolbar's delegate.
NSTabViewController.TabStyle.toolbar– 一种自动将任何选项卡添加到窗口工具栏的样式。选项卡视图控制器控制窗口的工具栏并将自己设置为工具栏的委托。
https://developer.apple.com/documentation/appkit/nstabviewcontroller/tabstyle
https://developer.apple.com/documentation/appkit/nstabviewcontroller/tabstyle
Keeping in mind above we can create NSWindowController
with NSWindow.contentViewController
set to NSTabViewController
to get standard macOS Preferences window.
记住上面我们可以NSWindowController
使用NSWindow.contentViewController
set to创建NSTabViewController
标准的 macOS Preferences 窗口。
Here is a Preferences window made from code (Swift 4):
这是一个由代码(Swift 4)制作的首选项窗口:
File: PreferencesPageID.swift– Keeps Preference page properties. Used in callbacks.
文件:PreferencesPageID.swift– 保留首选项页面属性。在回调中使用。
enum PreferencesPageID: Int, CaseIterable {
case generic, misc
var image: NSImage? {
switch self {
case .generic:
return NSImage(named: NSImage.folderSmartName)
case .misc:
return NSImage(named: NSImage.networkName)
}
}
var title: String {
switch self {
case .generic:
return "Some"
case .misc:
return "Other"
}
}
}
File: PreferencesTabView.swift– Represents Preference page content view.
文件:PreferencesTabView.swift– 代表首选项页面内容视图。
class PreferencesTabView: View {
let id: PreferencesPageID
init(id: PreferencesPageID) {
self.id = id
super.init()
}
required init?(coder decoder: NSCoder) {
fatalError()
}
override func setupUI() {
switch id {
case .generic:
backgroundColor = .red
setIntrinsicContentSize(CGSize(width: 400, height: 200))
case .misc:
backgroundColor = .blue
setIntrinsicContentSize(CGSize(width: 400, height: 300))
}
}
}
File: PreferenceItemViewController.swift- Controller which keeps Preference page content view. Needed mostly to fulfil macOS SDK requirements.
文件:PreferenceItemViewController.swift- 保持 Preference 页面内容视图的控制器。主要用于满足 macOS SDK 要求。
class PreferenceItemViewController: ViewController {
private let contentView: PreferencesTabView
override func loadView() {
view = contentView
}
init(view: PreferencesTabView) {
contentView = view
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError()
}
override func viewDidLayout() {
super.viewDidLayout()
preferredContentSize = view.intrinsicContentSize
}
}
File: PreferencesViewController.swift– Used as a NSWindow.contentViewController
.
文件:PreferencesViewController.swift– 用作NSWindow.contentViewController
.
class PreferencesViewController: TabViewController {
enum Event {
case selected(PreferencesPageID)
}
var eventHandler: ((Event) -> Void)?
override func setupUI() {
tabStyle = .toolbar // This will "turn" View Controller to standard Preferences window.
transitionOptions = .allowUserInteraction
canPropagateSelectedChildViewControllerTitle = false
let views = [PreferencesTabView(id: .generic), PreferencesTabView(id: .misc)]
views.forEach {
let item = NSTabViewItem(viewController: PreferenceItemViewController(view: private class PreferencesWindowController: NSWindowController {
private(set) lazy var viewController = PreferencesViewController()
init() {
let rect = CGRect(x: 400, y: 200, width: 400, height: 300)
let window = NSWindow(contentRect: rect, styleMask: [.titled, .closable], backing: .buffered, defer: true)
super.init(window: window)
setupHandlers()
let frameSize = window.contentRect(forFrameRect: window.frame).size
viewController.view.setFrameSize(frameSize)
window.contentViewController = viewController
}
required init?(coder: NSCoder) {
fatalError()
}
// MARK: - Private
private func setupHandlers() {
viewController.eventHandler = { [weak self] in
switch public class Application: NSApplication {
private lazy var preferencesController = PreferencesWindowController()
// Called from code or via IBAction
private func showPreferences() {
preferencesController.showWindow(nil)
}
}
{
case .selected(let id):
self?.window?.title = "Preferences — " + id.title
}
}
}
}
))
item.label = // AppDelegate.m
// CocoaToolBars
// Created by Debasis Das on 4/30/15.
// Copyright (c) 2015 Knowstack. All rights reserved.
#import "AppDelegate.h"
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
_toolbarTabsArray = [self toolbarItems];
_toolbarTabsIdentifierArray = [NSMutableArray new];
for (NSDictionary *dict in _toolbarTabsArray){
[_toolbarTabsIdentifierArray addObject:dict[@"identifier"]];
}
_toolbar = [[NSToolbar alloc] initWithIdentifier:@"ScreenNameToolbarIdentifier"];
_toolbar.allowsUserCustomization = YES;
_toolbar.delegate = self;
self.window.toolbar = _toolbar;
}
-(NSArray *)toolbarItems {
NSArray *toolbarItemsArray = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:@"Find Departments",@"title",@"Department-50",@"icon",@"DepartmentViewController",@"class",@"DepartmentViewController",@"identifier", nil],
[NSDictionary dictionaryWithObjectsAndKeys:@"Find Accounts",@"title",@"Business-50",@"icon",@"AccountViewController",@"class",@"AccountViewController",@"identifier", nil],
[NSDictionary dictionaryWithObjectsAndKeys:@"Find Employees",@"title",@"Edit User-50",@"icon",@"EmployeeViewController",@"class",@"EmployeeViewController",@"identifier", nil],
nil];
return toolbarItemsArray;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {
NSDictionary *itemInfo = nil;
for (NSDictionary *dict in _toolbarTabsArray) {
if([dict[@"identifier"] isEqualToString:itemIdentifier]) {
itemInfo = dict;
break;
}
}
NSAssert(itemInfo, @"Could not find preferences item: %@", itemIdentifier);
NSImage *icon = [NSImage imageNamed:itemInfo[@"icon"]];
if(!icon) {
icon = [NSImage imageNamed:NSImageNamePreferencesGeneral];
}
NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
item.label = itemInfo[@"title"];
item.image = icon;
item.target = self;
item.action = @selector(viewSelected:);
return item;
}
-(void)viewSelected:(id)sender {
NSToolbarItem *item = sender;
[self loadViewWithIdentifier:item.itemIdentifier withAnimation:YES];
}
-(void)loadViewWithIdentifier:(NSString *)viewTabIdentifier
withAnimation:(BOOL)shouldAnimate {
NSLog(@"viewTabIdentifier %@",viewTabIdentifier);
if ([_currentView isEqualToString:viewTabIdentifier]) {
return;
} else {
_currentView = viewTabIdentifier;
}
//Loop through the view array and find out the class to load
NSDictionary *viewInfoDict = nil;
for (NSDictionary *dict in _toolbarTabsArray) {
if ([dict[@"identifier"] isEqualToString:viewTabIdentifier]) {
viewInfoDict = dict;
break;
}
}
NSString *class = viewInfoDict[@"class"];
if(NSClassFromString(class)) {
_currentViewController = [[NSClassFromString(class) alloc] init];
NSView *newView = _currentViewController.view;
NSRect windowRect = self.window.frame;
NSRect currentViewRect = newView.frame;
windowRect.origin.y = windowRect.origin.y + (windowRect.size.height - currentViewRect.size.height);
windowRect.size.height = currentViewRect.size.height;
windowRect.size.width = currentViewRect.size.width;
self.window.title = viewInfoDict[@"title"];
[self.window setContentView:newView];
[self.window setFrame:windowRect display:YES animate:shouldAnimate];
} else {
NSAssert(false, @"Couldn't load %@", class);
}
}
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar {
NSLog(@"%s %@",__func__,_toolbarTabsIdentifierArray);
return _toolbarTabsIdentifierArray;
}
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar {
NSLog(@"%s",__func__);
return [self toolbarDefaultItemIdentifiers:toolbar];
}
- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar {
NSLog(@"%s",__func__);
return [self toolbarDefaultItemIdentifiers:toolbar];
}
- (void)toolbarWillAddItem:(NSNotification *)notification {
NSLog(@"%s",__func__);
}
- (void)toolbarDidRemoveItem:(NSNotification *)notification {
NSLog(@"%s",__func__);
}
@end
.id.title
item.image = ##代码##.id.image
addTabViewItem(item)
}
}
override func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
super.tabView(tabView, didSelect: tabViewItem)
if let view = tabViewItem?.viewController?.view as? PreferencesTabView {
eventHandler?(.selected(view.id))
}
}
}
File: PreferencesWindowController.swift– Preferences window controller.
文件:PreferencesWindowController.swift– 首选项窗口控制器。
##代码##Usage:
用法:
##代码##回答by Deepak Thakur
In MainMenu.xib
of your project, you will see a drop down list after you click on your app name. Cntrl + Click + Drag Preferences
to your project's AppDelegate.h
file and create and IBAction method.
在MainMenu.xib
您的项目中,单击您的应用程序名称后,您将看到一个下拉列表。Cntrl + 单击 + 拖动Preferences
到您的项目AppDelegate.h
文件并创建 IBAction 方法。
Create a class with NSWindowController (PreferenceWindowController) with xib, create a strong property of that PreferenceWindowController, alloc init it and add [self.preferenceWindowController showWindow:self];
in AppDelegate.m. This will create a window of Preferences
for your OS X app.
使用 xib 创建一个带有 NSWindowController (PreferenceWindowController) 的类,创建该 PreferenceWindowController 的强属性,分配初始化它并添加[self.preferenceWindowController showWindow:self];
到 AppDelegate.m 中。这将为Preferences
您的 OS X 应用程序创建一个窗口。
回答by Jeff Szuhay
Create a .nib and a controller (.h & .m) for each of your preference panes. Then hook them up dynamically in the AppDelegate.m of your app. I'm using it in my app that consists of a number dynamically loaded bundles such that each bundle has its own preferences.
为每个首选项窗格创建一个 .nib 和一个控制器(.h 和 .m)。然后在您的应用程序的 AppDelegate.m 中动态连接它们。我在我的应用程序中使用它,它由许多动态加载的包组成,这样每个包都有自己的首选项。
You can see a really good concise example here: http://www.knowstack.com/nstoolbar-sample-code-objectivec/
您可以在这里看到一个非常好的简洁示例:http: //www.knowstack.com/nstoolbar-sample-code-objectivec/
In this example, it dynamically creates the NSToolbar and the NSToolbarItem. What you do in each window controller for each preference pane is up to you.
在这个例子中,它动态地创建了 NSToolbar 和 NSToolbarItem。您在每个首选项窗格的每个窗口控制器中做什么取决于您。
Here's the main parts of the AppDelegate.m:
这是 AppDelegate.m 的主要部分:
##代码##