GithubHelp home page GithubHelp logo

sindresorhus / settings Goto Github PK

View Code? Open in Web Editor NEW
1.4K 19.0 93.0 7.17 MB

⚙ Add a settings window to your macOS app in minutes

License: MIT License

Swift 100.00%
preferences macos swift-package swift-package-manager carthage cocoapods swiftui preferences-window swiftui-components settings

settings's Introduction

Settings

Add a settings window to your macOS app in minutes

Just pass in some view controllers and this package will take care of the rest. Built-in SwiftUI support.

This package is compatible with macOS 13 and automatically uses Settings instead of Preferences in the window title on macOS 13 and later.

This project was previously known as Preferences.

Requirements

macOS 10.13 and later.

Install

Add https://github.com/sindresorhus/Settings in the “Swift Package Manager” tab in Xcode.

Usage

Run the Example Xcode project to try a live example (requires macOS 11 or later).

First, create some settings pane identifiers:

import Settings

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

Second, create a couple of view controllers for the settings panes you want. The only difference from implementing a normal view controller is that you have to add the SettingsPane protocol and implement the paneIdentifier, toolbarItemTitle, and toolbarItemIcon properties, as shown below. You can leave out toolbarItemIcon if you're using the .segmentedControl style.

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
	}
}

Note: If you need to support macOS versions older than macOS 11, you have to add a fallback for the 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
	}
}

If you need to respond actions indirectly, the settings window controller will forward responder chain actions to the active pane if it responds to that selector.

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)
	}
}

In the AppDelegate, initialize a new SettingsWindowController and pass it the view controllers. Then add an action outlet for the Settings… menu item to show the settings window.

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()
	}
}

Settings Tab Styles

When you create the SettingsWindowController, you can choose between the NSToolbarItem-based style (default) and the NSSegmentedControl:

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

.toolbarItem style:

NSToolbarItem based (default)

.segmentedControl style:

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)
}

As with any NSWindowController, call NSWindowController#close() to close the settings window.

Recommendation

The easiest way to create the user interface within each pane is to use a NSGridView in Interface Builder. See the example project in this repo for a demo.

SwiftUI support

If your deployment target is macOS 10.15 or later, you can use the bundled SwiftUI components to create panes. Create a Settings.Pane (instead of SettingsPane when using AppKit) using your custom view and necessary toolbar information.

Run the Example target in the Xcode project in this repo to see a real-world example. The Accounts tab is in SwiftUI.

There are also some bundled convenience SwiftUI components, like Settings.Container and Settings.Section to automatically achieve similar alignment to AppKit's NSGridView. And also a .settiingDescription() view modifier to style text as a setting description.

Tip: The Defaults package makes it very easy to persist the settings.

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.
			}
			
		}
	}
}

Then in the AppDelegate, initialize a new SettingsWindowController and pass it the pane views.

// …

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

// …

If you want to use SwiftUI panes alongside standard AppKit NSViewController's, instead wrap the pane views into Settings.PaneHostingController and pass them to SettingsWindowController as you would with standard panes.

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
)

// …

Full example here..

Backwards compatibility

macOS 11 and later supports SF Symbols which can be conveniently used for the toolbar icons. If you need to support older macOS versions, you have to add a fallback. Apple recommends using the same icons even for older systems. The best way to achieve this is to export the relevant SF Symbols icons to images and add them to your Asset Catalog.

Known issues

The settings window doesn't show

This can happen when you are not using auto-layout or have not set a size for the view controller. You can fix this by either using auto-layout or setting an explicit size, for example, preferredContentSize in viewDidLoad(). We intend to fix this.

There are no animations on macOS 10.13 and earlier

The animated parameter of SettingsWindowController.init has no effect on macOS 10.13 or earlier as those versions don't support NSViewController.TransitionOptions.crossfade.

FAQ

How can I localize the window title?

The SettingsWindowController adheres to the macOS Human Interface Guidelines and uses this set of rules to determine the window title:

  • Multiple settings panes: Uses the currently selected paneTitle as the window title. Localize your paneTitles to get localized window titles.
  • Single settings pane: Sets the window title to APPNAME Settings. The app name is obtained from your app's bundle. You can localize its Info.plist to customize the title. The Settings part is taken from the "Settings…" menu item, see #12. The order of lookup for the app name from your bundle:
    1. CFBundleDisplayName
    2. CFBundleName
    3. CFBundleExecutable
    4. Fall back to "<Unknown App Name>" to show you're missing some settings.

Why should I use this instead of just manually implementing it myself?

It can't be that hard right? Well, turns out it is:

How is it better than MASPreferences?

  • Written in Swift. (No bridging header!)
  • Swifty API using a protocol.
  • Supports segmented control style tabs.
  • SwiftUI support.
  • Fully documented.
  • Adheres to the macOS Human Interface Guidelines.
  • The window title is automatically localized by using the system string.

Related

You might also like Sindre's apps.

Used in these apps

Want to tell the world about your app that is using this package? Open a PR!

Maintainers

settings's People

Contributors

chriszielinski avatar cool8jay avatar davidwernhart avatar divinedominion avatar francisfeng avatar fredyshox avatar kyle-ye avatar luin avatar metacodes avatar mortennn avatar nrivard avatar samusaranx avatar shikisuen avatar sindresorhus avatar tonyarnold avatar wes-nz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

settings's Issues

Bundle identifier without dot characters will cause "Invalid Code Signature Identifier" error

Hi @sindresorhus, thank you for the nice product!

I'm using this package with Carthage.
When I archive and validate my app, I get an "Invalid Code Signature Identifier" error.

invalid-code-signature-identifier

As I investigated, I found that if the framework has no dot characters in its bundle identifier, Xcode appends auto-generated string to signing identifier.
And this causes the "Invalid Code Signature Identifier" error.

sindresorhus/KeyboardShortcuts seems to have the same problem, so I'd recommend prepending com.sindresorhus. or something to their bundle identifier.

My environment:

  • macOS 10.15.5
  • Xcode 11.5

How to top-align section title with content?

I have a section containing only one SwiftUI List view. How can I top-align the section title with and the list?

Can I achieve this with current version of Preference?

If not, maybe offer a parameter to customize this? I think I can help with it becuase I really need this.

The code:

struct ColorThemePane: View {
  let colorThemeOptions = ColorTheme.allCases
  @State var selectKeeper: Set<ColorTheme> = [Defaults[.colorTheme]]
  
  var body: some View {
    Preferences.Container(contentWidth: Preferences.PaneWidth) {
      
      Preferences.Section(title: "Color Theme:", bottomDivider: true) {
        List(colorThemeOptions, id: \.self, selection: $selectKeeper){ name in
          Text(name.rawValue)
        }.listStyle(PlainListStyle())
        .frame(width: 290, height: 150)
        .cornerRadius(6)
        
      }
      
    }
  }
}

The result:
image

Related: #56

PreferencesWindowController doesn't forward selectors to content

PreferencesWindowController (as a top-level member of the responder chain) doesnt forward any selectors to content, specifically the selected view controller. And since PWC is also final you can't subclass to do so. So there are 2 solutions:

  1. make PWC open so folks can subclass and handle responder chain actions at that layer
  2. forward the selected view controller for possible handling of responder chain actions

I will make a PR that focus on option #2 as I prefer PWC to be pretty self contained and if all actions are forwarded properly, we can accomplish what we want at the VC level

Preferences Window has zero height

Hi there,

So I was trying out this project because it looks great, but I'm having some trouble. First I tried the published version (0.2.1) in Cocoapods and it worked fine. However it doesn't include some of the recent changes such as using the segmented control. So I decided to use the version in master instead.

Now with this I am having trouble. I simply created a new project and did what was explained in the README (this project can be found here), and the preference window is not showing. It gets created and I can focus it, but it's not visible. I looked at the frame of the window, and it shows that the window has a height of 0 pixels... But I am not sure why that is happening...

I would really appreciate some help here 🙏

Thank you,
Kevin

Switching between tabs could cause windows resize shaking on the second monitor

Issuehunt badges

I was able to reproduce this issue many times on my second monitor.
However, I was not able to reproduce on my main monitor(the docker monitor)

If I switch my docker to the second monitor to make it become the main monitor, then the problem can be reproduced on the old main monitor(now it become new secondary monitor)

Check is gif animation:
2019-03-20 21_35_48

My iMac main monitor is 5k (retina). My secondary monitor is Apple cinema display which is 2560x1440 (non-retina)

macOS 10.14

There is a $80.00 open bounty on this issue. Add more on Issuehunt.

manage close window event

Hi,
I want to manage the close event (click on red button on the top left).
in the app delegate I catch the function
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool

in this event I want to call a function in the panels.
Is there a way to manage this?
Thanks

Is there a way to make the preferences window entirely aero-glass?

Previously in Interface Builder I can achieve this by
using NSVisualEffectView in the main window
and set the panel material to "sidebar" (for better readability).

image

// This turns things non-transparent when window is not activated:
image

I don't know how to do this with this "Preference" package.

Could anyone please give me some instructions on where and how to modify?

`loadView()` override needed if no interface builder

Thanks for the great project! Just wanted to mention that AFAICT if you are not using interface builder, then you need to write loadView() manually in the controllers, otherwise you get a crash:

  override func loadView() {
    self.view = NSView()
  }

If this is correct, then maybe it is worth mentioning in the README.

Function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Owned To Guaranteed> error

Hi, I've just replaced the MASPreferences with your package, amazing work! It works perfectly on macOS Big Sur 11.0.1.

But I've faced with next issue when I open the preferences window on macOS Catalina 10.15.6. My app crashes with this error:

function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Owned To Guaranteed> of SnipperApp_2.GeneralPreferencesController.init(nibName: Swift.String?, bundle: __C.NSBundle?) -> SnipperApp_2.GeneralPreferencesController GeneralPreferencesController.swift:0
SnipperApp 2
@objc SnipperApp_2.GeneralPreferencesController.init(nibName: Swift.String?, bundle: __C.NSBundle?) -> SnipperApp_2.GeneralPreferencesController <compiler-generated>:0
SnipperApp 2
SnipperApp_2.AppDelegate.preferencesWindowController.getter : Preferences.PreferencesWindowController <compiler-generated>:0
SnipperApp 2
function signature specialization <Arg[0] = Dead> of SnipperApp_2.AppDelegate.showPreferences(Swift.AnyObject) -> () AppDelegate.swift:420
SnipperApp 2
merged @objc SnipperApp_2.AppDelegate.showPreferences(Swift.AnyObject) -> ()
AppKit
-[NSApplication(NSResponder) sendAction:to:from:]
AppKit
-[NSMenuItem _corePerformAction]
AppKit
-[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:]
AppKit
-[NSMenu performActionForItemAtIndex:]
AppKit
-[NSMenu _internalPerformActionForItemAtIndex:]
AppKit
-[NSCarbonMenuImpl _carbonCommandProcessEvent:handlerCallRef:]
AppKit
NSSLMMenuEventHandler
HIToolbox
DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*)
HIToolbox
SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*)
HIToolbox
SendEventToEventTarget
HIToolbox
SendHICommandEvent(unsigned int, HICommand const*, unsigned int, unsigned int, unsigned char, void const*, OpaqueEventTargetRef*, OpaqueEventTargetRef*, OpaqueEventRef**)
HIToolbox
SendMenuCommandWithContextAndModifiers
HIToolbox
SendMenuItemSelectedEvent
HIToolbox
FinishMenuSelection(SelectionData*, MenuResult*, MenuResult*)
HIToolbox
MenuSelectCore(MenuData*, Point, double, unsigned int, OpaqueMenuRef**, unsigned short*)
HIToolbox
_HandleMenuSelection2
AppKit
_NSHandleCarbonMenuEvent
AppKit
_DPSEventHandledByCarbon
AppKit
-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]
AppKit
-[NSApplication run]
AppKit
NSApplicationMain
SnipperApp 2
main AppDelegate.swift:32
libdyld.dylib
start

Preferences version: 2.1.0, installed via cocoapods.

SwiftUI support

Issuehunt badges

Would be nice if you could specify an array of SwiftUI Views's instead of NSViewController's.

Could probably use https://developer.apple.com/documentation/swiftui/nshostingcontroller for this.


IssueHunt Summary

fredyshox fredyshox has been rewarded.

Backers (Total: $60.00)

Submitted pull Requests


Tips

Support segmented button style toolbar

I don't always have time to do nice icons and would like to use this style:

image

Note how it's segmented controls in a narrow window toolbar.

NSTabView actually has a style for this (which is also the default), but the segmented controls ends up in the window and not in the toolbar, like this:

screen shot 2018-06-29 at 20 26 38

Help appreciated 🙌

The only solution I can think of is to hide the NSTabView tabs, put an NSSegmentedControl centered in an NSToolbar, listen to when the active segment changes, and then forward that to the NSTabView. That is way more complicated than it should, so I'm hoping for someone to propose a better solution.

Remember the last active tab and show it by default

Issuehunt badges

From the HIG:

Restore the last viewed preference pane. If the user switches preference panes, your app should remember this change and show the same pane immediately the next time the user opens your preferences window.

We don't currently do that. I think we should do it when users call .show() without an argument, and mention in the docs that that should be the preferred way to show the preferences window, and refer to the HIG docs. This would be another benefit of having #14 and we could enforce it there.

divinedominion earned $80.00 by resolving this issue!

Preferences crash with 10.14

Charts Environment

**Preference version Number:Master
**Xcode version:10.0
**Swift version:4.2
**macOS version running Xcode:10.14

i have a big problem

I do not know what to tell you
I'm just testing the program

2018-09-27 16:39:05.212472+0200 PreferencesExample[9389:1538460] [Layout] Unable to simultaneously satisfy constraints:
(
    "<NSLayoutConstraint:0x600002121ae0 'NSStackView.Edge.Top' V:|-(>=0)-[_NSToolbarItemViewerLabelView:0x60000350c580]   (active, names: '|':NSStackView:0x10120f2c0 )>",
    "<NSLayoutConstraint:0x600002121b80 'NSStackView.Edge.Bottom' V:[_NSToolbarItemViewerLabelView:0x60000350c580]-(>=0)-|   (active, names: '|':NSStackView:0x10120f2c0 )>",
    "<NSLayoutConstraint:0x600002122260 NSToolbarButton:0x600003e08200.bottom <= NSStackView:0x10120f2c0.top - 2   (active)>",
    "<NSLayoutConstraint:0x600002122210 V:|-(>=3)-[NSToolbarButton:0x600003e08200]   (active, names: '|':NSToolbarItemViewer:0x10120eb50 )>",
    "<NSLayoutConstraint:0x6000021222b0 NSStackView:0x10120f2c0.bottom == NSToolbarItemViewer:0x10120eb50.bottom - 4   (active)>",
    "<NSLayoutConstraint:0x6000021223a0 'NSView-Encapsulated-Layout-Height' NSToolbarItemViewer:0x10120eb50.height == 1   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600002121b80 'NSStackView.Edge.Bottom' V:[_NSToolbarItemViewerLabelView:0x60000350c580]-(>=0)-|   (active, names: '|':NSStackView:0x10120f2c0 )>

Set the NSUserDefault NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints to YES to have -[NSWindow visualizeConstraints:] automatically called when this happens.  And/or, set a symbolic breakpoint on LAYOUT_CONSTRAINTS_NOT_SATISFIABLE to catch this in the debugger.
2018-09-27 16:39:05.213752+0200 PreferencesExample[9389:1538460] [Layout] Unable to simultaneously satisfy constraints:
(
    "<NSLayoutConstraint:0x600002122260 NSToolbarButton:0x600003e08200.bottom <= NSStackView:0x10120f2c0.top - 2   (active)>",
    "<NSLayoutConstraint:0x600002122210 V:|-(>=3)-[NSToolbarButton:0x600003e08200]   (active, names: '|':NSToolbarItemViewer:0x10120eb50 )>",
    "<NSLayoutConstraint:0x6000021222b0 NSStackView:0x10120f2c0.bottom == NSToolbarItemViewer:0x10120eb50.bottom - 4   (active)>",
    "<NSLayoutConstraint:0x6000021223a0 'NSView-Encapsulated-Layout-Height' NSToolbarItemViewer:0x10120eb50.height == 1   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600002122260 NSToolbarButton:0x600003e08200.bottom <= NSStackView:0x10120f2c0.top - 2   (active)>

Set the NSUserDefault NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints to YES to have -[NSWindow visualizeConstraints:] automatically called when this happens.  And/or, set a symbolic breakpoint on LAYOUT_CONSTRAINTS_NOT_SATISFIABLE to catch this in the debugger.

Provide a `Preferences…` menu item

Issuehunt badges

Would be useful if we provide a default Preferences… menu that opens the preferences window. Can be programmatically created or subclassed in Interface Builder. Should also include localization.

If the app is a menu bar app, we should also call NSApp.activate(ignoringOtherApps: true) before showing the Preferences window.

There is a $80.00 open bounty on this issue. Add more on Issuehunt.

tab transition on High Sierra fails

On High Sierra, I get this when I switch tabs:

*** Assertion failure in -[Preferences.PreferencesTabViewController transitionFromViewController:toViewController:options:completionHandler:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1561.60.100/Controllers/NSViewController.m:925

Am investigating!

Slightly weird look on Dark theme, and Big Sur center default?

EDIT: I closed this again, after noticing that XCode has the same blue highlight, so it seems to be a Big Sur thing I wasn't aware of. Using a system icon already rendered the spacing much nicer.


An image speaks more than a thousand words, so let me post that first:

Screenshot 2020-12-09 at 16 39 20

The following is all about the toolbar (not the content below):

  • Notice the blue highlight (my macOS theme accent color is boring grey, so that's not it).
  • Notice the tight vertical spacing (am I supposed to use a specific PNG size? Needs to work on 10.15 too, so using a PNG).
  • Notice the center placement of the two icons (I thought they'd be left-aligned, or is that a Big Sur default?)

I kept it as minimal and slim as possible. This is with XCode 12.2, Big Sur, dark theme. Ignore the two settings icons, I was too lazy to find a different icon. Thanks!

Should the preferences window be excluded from the "Window" menu?

I noticed that in some apps, the preferences window doesn't show up in the Window menu. But there's of course no consistency on this.

Apple Feedback Assistant report: feedback-assistant/reports#111

Shows in Window menu

  • Xcode
  • Calendar (but in a different list than the main window, which is just weird)
  • Reminders
  • Safari
  • App Store
  • Mail
  • Contacts
  • Photos (but with a separator between it and the main window)
  • Keychain Access
  • Script Editor
  • Dictionary
  • Pixelmator
  • Fantastical
  • BusyCal (but with a separator between it and the main window)
  • Transmit
  • 1Password

Does not

  • Notes
  • Messages
  • Final Cut X
  • Music
  • TV
  • TextEdit
  • Grapher
  • Terminal
  • Preview
  • Sketch

Based on my findings, I'm leaning towards keeping it in the Window menu. But I'm looking for more opinions.

Add option to hide the toolbar when there's only one pane

Issuehunt badges

I personally don't really see the point of having a toolbar if there's only one preference pane and would like to hide it. I would still want to use this module though, so it's easy to add additional panes in the future.

So would be nice with a showToolbarForSinglePane option.

@DivineDominion Thoughts?

divinedominion earned $60.00 by resolving this issue!

Is it possible to add a flexible space between toolbar icons?

I have a need to add a 'Revert' item to my preferences toolbar. I would like this item to be placed on the right of the toolbar with a flexible space between it and the other items. The flexible space pushes the item all the way to the right. I don't see an obvious way to do this in your framework. Is it possible?

Thanks!

Preferences window title isn't localized

Issuehunt badges

I just completed the German localization for an app of mine. The app's development language is English.
When I start the app in Xcode with the language set to German, everything's localized correctly except for the Preferences window's title:

Screenshot 2019-03-27 at 10 56 06

Adding "Preferences" = "Einstellungen"; to my Localizable.strings did nothing. Is this a bug or am I missing something?

IssueHunt Summary

samusaranx samusaranx has been rewarded.

Sponsors (Total: $100.00)

Tips

Change the "Window" menu title?

When there are multiple tabs, the window menu would look like:

Screenshot 2020-06-08 at 22 19 57

Where General is the preferences window title. However, I feel it's not clear enough to the user that General mean the preferences window. Also, what if the main window has a name that is the same or close to the preferences tab name. That would be confusing.


When there's no tabs, the HIG says that the window title should be the app name and then Preferences, but that can look weird:

Screenshot 2020-06-08 at 22 18 38


The macOS Human Interface Guidelines doesn't say anything about this.

My preference would be to use Preferences — General for the title in the Window menu when there are multiple tabs, and Preferences when there's none.

Xcode uses this convention:
Screenshot 2020-06-08 at 22 22 42


Changing the title of only the "Window" menu items is possible with:

NSApp.changeWindowsItem(window, title: "Preferences — General", filename: false)

Thoughts?

Implement tab switching shortcuts?

What do you think about adding breowser-like tab switching shortcuts?

  • US layouts: +] and +[
  • Some European layouts: ++ and ++

By responding to Main Menu elements, this can even be customized.

SwiftUI unexpected switching animation

I used SwiftUI to build preference panels. However, when switching tabs, the view jumps with a weird animation. I feel this is because the SwiftUI view hasn't loaded when switching.

This only happens for the first time switching.

See:
CleanShot 2020-08-05 at 12 14 29

My current workaround is to disable the animation:

_ = PreferencesWindowController(
  preferencePanes: [...],
  style: .toolbarItems,
  animated: false, // <---
  hidesToolbarForSingleItem: true
)

Show using NSGridView in the example app

Issuehunt badges

NSGridView can be used in Interface Builder in Xcode 10.

We should show how to do a realistic preferences window. It doesn't have to be functional, but it should look so.

Something like:

Screen Shot 2019-04-03 at 21 43 39

It should have a General and Advanced pane with some controls.


IssueHunt Summary

waleeg waleeg has been rewarded.

Backers (Total: $80.00)

Submitted pull Requests


Tips


IssueHunt has been backed by the following sponsors. Become a sponsor

The preferences window always opens on the right side of the monitor

Issuehunt badges

Screen Shot 2019-04-07 at 18 25 47

Even if I move it and close the window, the next time it opens in the same position.

I think it should open in the center the first time, and then remember the user's position from there. I think we need to use saveFrame(usingName:).


IssueHunt Summary

mortennn mortennn has been rewarded.

Backers (Total: $60.00)

Submitted pull Requests


Tips


IssueHunt has been backed by the following sponsors. Become a sponsor

Rename project to `Settings`

Later this year.

  • While I'm doing that, also rename preferencePaneIdentifier to paneIdentifier and preferencePaneTitle to paneTitle. And the preferencePanes parameter to panes.

Use `NSToolbarItemGroup`

When we can eventually target macOS 10.15 in the far future, we could use NSToolbarItemGroup instead of placing a segmented control in the toolbar.

https://developer.apple.com/documentation/appkit/nstoolbaritemgroup/selectionmode/selectone
https://developer.apple.com/documentation/appkit/nstoolbaritem/3237224-isbordered
https://developer.apple.com/documentation/appkit/nstoolbaritem/3237225-title

All the new APIs:

Added NSToolbarItemGroupSelectionMode
Added NSToolbarItemGroupSelectionModeSelectOne
Added NSToolbarItemGroupSelectionModeSelectAny
Added NSToolbarItemGroupSelectionModeMomentary
Added NSToolbarItemGroupControlRepresentation
Added NSToolbarItemGroupControlRepresentationAutomatic
Added NSToolbarItemGroupControlRepresentationExpanded
Added NSToolbarItemGroupControlRepresentationCollapsed
Added +[NSToolbarItemGroup groupWithItemIdentifier:titles:selectionMode:labels:target:action:]
Added +[NSToolbarItemGroup groupWithItemIdentifier:images:selectionMode:labels:target:action:]
Added NSToolbarItemGroup.controlRepresentation
Added NSToolbarItemGroup.selectionMode
Added NSToolbarItemGroup.selectedIndex
Added -[NSToolbarItemGroup setSelected:atIndex:]
Added -[NSToolbarItemGroup isSelectedAtIndex:]

http://codeworkshop.net/objc-diff/sdkdiffs/macos/10.15/AppKit.html

Incorrect animation for first transition to preference pane with different width

When animating to a preference pane that has not been loaded before and has a larger width than the current pane the view is not correctly laid out initially, which causes an odd animation. The animation is correct in subsequent switches.

The example below is the example app with the width = 450 constraint removed from the Advanced tab, and the app set to open to the Advanced tab.

Screen Recording 2020-08-23 at 19 39 33 2020-08-23 19_49_52

I'm trying to figure out the exact scenarios that causes this. Starting on Accounts does not cause this same animation to happen so I'm not sure how the size change and animations are linked.

Provide common set of preferences out-of-the-box

Issuehunt badges

We could provide .general, .advanced, .updates and maybe .license:

Since almost everyone would have a General preference tab. I wonder if we should ship with a PreferenableGeneral type thing that has all these fields already filled out. My goal is to require the least amount of boilerplate possible to set up a preferences window. Thoughts?

We could do this for Advanced too. And eventually even ship some localizations for those, so users don't have to care about that either.

(Originally posted by @sindresorhus in #6)

There is a $60.00 open bounty on this issue. Add more on Issuehunt.

Dynamic content width?

Is it possible to use dynamic contentWidth for each view so that it can automatically cope with different localizations?

For example, a shorter contentWidth is better for Traditional Chinese localization:

image

Need a way to programmatically select tabs

With the following declaration, the ability to programmatically select a tab is hidden.
private let tabViewController = PreferencesTabViewController()

I suggest we make the following changes/additions in PreferencesWindowController ...

public func showWindow(tabIndex: Int = 0) {
	if !window!.isVisible {
		window?.center()
	}

    if tabIndex >= 0 && tabIndex < tabViewController.tabView.numberOfTabViewItems {
        tabViewController.tabView.selectTabViewItem(at: tabIndex)
    }

	showWindow(self)
	NSApp.activate(ignoringOtherApps: true)
}

public func selectTab(tabIndex: Int) {
    if tabIndex >= 0 && tabIndex < tabViewController.tabView.numberOfTabViewItems {
        tabViewController.tabView.selectTabViewItem(at: tabIndex)
    }
}

Small memory leak at init

Hi,

I was profiling my app for memory leaks, and it seems that Preferences is generating some during it's init:

image

Could you please have a look?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.