Creation of standing bar purposes on macOS in Swift

Howdy readers! I want to begin at this time by saying that this can be a macOS programming tutorial that I’ve wished to put in writing for a very long time now. Not solely as a result of this can be very attention-grabbing, but additionally as a result of we’re going to come throughout a distinct sort of purposes that we will create for the macOS working system. Nevertheless, a lot of the earlier macOS programming tutorials ought to come first as some prior information is required, however lastly we’re there! We're about to learn to create standing bar apps or in different phrases, apps that reside on the highest bar of macOS proper subsequent to the clock and different system or third get together purposes that all of us use and work together with each day.

The primary and most essential factor that differentiates apps from the standing bar from regular apps is the truth that they show an icon or title within the standing bar, in order that customers can shortly entry the performance of the app by merely clicking on it. In actual fact, such an utility shows a particular object known as a state factor that comprises a button, and that button comprises the title or icon.

When a standing merchandise is clicked, two issues often occur:

An inventory of choices is offered as a drop down menu so customers can select what to do. The choices displayed are at all times particular to the appliance and rely completely on the performance of the appliance. It is usually doable to have customized views among the many menu objects; on this method, customers can immediately discover what they’re on the lookout for just by clicking on the appliance standing merchandise. A pop-up window seems with an arrow pointing to the appliance merchandise within the standing bar. This pop-up window comprises a number of view controllers and is appropriate for managing consumer inputs whereas within the standing bar utility mode and with out having offered an precise window.

A rarer state of affairs is just to current the primary window of the appliance whenever you click on on the standing factor, simply as it might be offered throughout a standard launch. Although this isn’t so widespread, it’s an possibility permitting customers to carry up the appliance window proper in entrance of them by clicking merely on an icon within the prime bar.

More often than not, the options and performance of the standing bar utility are accessible with out presenting a window by default. In actual fact, that is what provides which means to the apps within the standing bar and that is typically what customers look forward to finding; to shortly use the options of the appliance with out having to handle the home windows. Nevertheless, this doesn’t imply that home windows and think about controllers can’t or shouldn’t be displayed in any respect! Quite the opposite, any obligatory window may be offered to supply extra choices, actions, consolation and adaptability to customers. However at all times remember the fact that customers look forward to finding key options or essential info by clicking on the app merchandise within the standing bar. For instance, in a standing bar utility that shows climate info, customers can completely configure their preferences in a separate window, however temperature or different important climate info can’t be "hidden" behind a menu possibility which can carry up one other window which in flip will include the climate particulars; it needs to be there by the point the app icon (or title) is clicked.

On this tutorial, we are going to concentrate on the primary two instances by going by two completely different demo apps and you’ll learn to create standing bar apps that work in a method or method ;one other; by displaying menu choices with a personalised view or by presenting a popover. We’ll talk about the required configuration that turns an occasional utility into a standing bar utility, we are going to see greatest practices and procedures, and usually nearly all the things it’s worthwhile to know whenever you begin creating your personal macOS bar purposes state

The standing bar utility to construct

The primary demo utility we are going to work with on this tutorial known as WorldTime and you could find it on this starter pack. Our objective is to begin from a standard macOS utility and convert it to a standing bar utility. When its standing merchandise is clicked, it is going to current a menu with a couple of choices (About window, Preferences and Exit), and a customized view with date and time info for a particular time zone.

As well as, we are going to enable to switch the popular time zone through a Preferences window. We’ll use consumer defaults as a technique to save (and skim later) the time zone chosen by the consumer. This pattern utility comprises all the required info one would wish to create related purposes that show a menu and presumably a customized view. Observe, nonetheless, that this kind of standing bar purposes will not be appropriate for dealing with consumer enter; if that’s your objective, it is best to learn the best way to create standing bar apps that show pop-up home windows later on this tutorial.

Getting began

Assuming you might have downloaded the starter pack, unzip and open the WorldTime venture in Xcode with the intention to comply with it right here. You will notice that preparations have already been made and that essential components of the code have already been carried out. In actual fact, what you'll discover already executed is 2 customized views, however we'll speak about them within the following sections. There are lacking components which might be extra related to our trigger right here, and we'll add them step-by-step. At this level, you might be free to run the demo utility if you want earlier than you begin; you’ll solely see the primary utility window exhibiting the appliance with out content material, and there might be no indication of the appliance within the standing bar.

The primary and maybe essentially the most essential step is to carry out the entire obligatory actions that can enable the appliance to run like a standing bar utility. The collection of actions described beneath may be executed in any order, however the best way we do it helps to higher perceive the entire course of.

The standing factor

As I already talked about within the introduction to this text, the icon or title of an utility that seems within the standing bar Belongs to a button contained in a particular object known as a standing bar merchandise. This object is an occasion of the NSStatusItem class and any utility that needs to run as a standing bar utility should initialize and configure such an object. The obvious place to have this initialization code is the AppDelegate class as a result of that is the start line for every utility, and certainly we are going to do that on this instance. Nevertheless, filling the AppDelegate with code apart from the appliance delegate strategies will not be a good suggestion as it’s simple to finish up with an unreasonably massive utility delegate file and complicated. For that reason, I’ll present you a greater strategy within the following instance the place we are going to learn to present a popover; an strategy that makes use of a standalone file and a category. For now and for academic functions of this tutorial, the usage of AppDelegate is completely acceptable.

So open the AppDelegate.swift file and declare the next property on the prime of the category:

var statusItem: NSStatusItem?

var statusItem : NSStatusItem ?

Now substitute the awakeFromNib () technique and add the next content material:

substitute awakeFromNib () operate

substitute func awakeFromNib ( )

The awakeFromNib () might be known as as a result of we have now a storyboard file from which the consumer interface is loaded. In case you desire a code answer solely, then all the present code in awakeFromNib () ought to go to the applicationDidFinishLaunching (_ 🙂 technique.

As you may see above, we first initialize an NSStatusItem object. What’s attention-grabbing right here is the argument of the initializer which defines the size of the merchandise within the standing bar. The variable size worth used above permits the factor to increase as a lot as obligatory in order that it could possibly absolutely show its title or icon. Alternatively, there may be additionally the worth squareLength which defines a sq. space for the icon or title of the factor. With this feature, the title of the factor outlined on the second line ("WorldTime") would seem truncated and solely the primary letters akin to the size of the sq. could be seen.

Our work right here will not be but completed; we are going to overview awakeFromNib () a number of instances so as to add extra code. It’s nonetheless essential that step one is now accomplished.

Replace of the Information.plist file

A standing bar utility can also be known as an agent utility, and initialization of an NSStatusItem object as proven above will not be sufficient to let the agent know ; utility we wish it to be an agent utility. To do that, a brand new entry should be made within the Information.plist file, so choose it to open it within the venture browser.

With the Information.plist file in entrance of you, choose the final line of the file, then go to the menu Editor> Add a component, or just click on on the small button extra immediately on the road. Within the new entry that might be added, set The applying is agent (UIElement) as the important thing and YES as the worth:

Keep in mind to press Cmd + S to avoid wasting the adjustments you simply made. At this level, you may re-run the appliance. Have a look at the standing bar the place you can find the standing factor of our app that’s exhibiting up for the primary time!

In fact, nothing occurs whenever you click on on it and the appliance window continues to be there. However don't fear, we nonetheless have a protracted technique to go till the app is about up appropriately.

Eradicating the appliance window

It's time to eliminate the appliance window, so open the Foremost.storyboard file. You can find within the storyboard the primary menu of the appliance (we are going to come again to that straight away), the window controller and the default view controller linked to the window controller. That is the default configuration made by Xcode when creating a brand new macOS utility.

To forestall the window from showing when the appliance is launched, choose the window controller and press the Delete key in your keyboard. Be certain that to take away solely the window controller and never the view controller; we are going to want it later.

Run the appliance once more; the app window will now not be proven and it's superior! Our app is beginning to seem like a standing bar app even when it does nothing but.

Utilizing the primary menu

Although the appliance window is now not proven as a result of there isn’t a window controller, there may be additionally one other factor that we don't wish to hold when creation of an agent utility. That is the primary menu. The straightforward answer is to delete it from the storyboard as we did with the window controller.

Nevertheless, this isn’t completely fascinating right here and let me clarify why. Our objective is to create a standing bar app that presents a menu with choices, in addition to a customized view with time and date info when the merchandise is utility standing is clicked. So, we’d like a menu it doesn’t matter what. If we take away the primary menu completely, we must create one from scratch, both within the storyboard or programmatically within the AppDelegate class. Why, on this case, not delete the menu objects which we don't actually need from the primary menu, and hold solely the primary menu merchandise (titled "WorldTime") that we will customise as wanted? As well as, we are going to get some options totally free, reminiscent of quitting the app or exhibiting the About window. Small particulars, however they may help save time. Nevertheless, I’ve to repeat that it’s not improper to fully delete the primary menu and create a brand new one from scratch.

Observe: It’s advisable to learn this earlier put up on the best way to work with menus on macOS purposes when you have not already executed so. It would enable you really feel extra comfy with the menus right here.

Following the strategy of not fully eradicating the primary menu, open the doc define, increase the Foremost Menu object and:

Choose the File menu merchandise. Press and maintain the Shift key. Click on the Assist menu merchandise to pick all objects from File to Assist. Faucet Delete on the keyboard.

Now click on on the remaining "WorldTime" menu merchandise. to increase it. Delete all of the sub-elements, however be sure you hold the next parts with separators between them:

About WorldTimePreferences… Give up WorldTime

Within the Xcode toolbar, click on the plus button to open the article library. Search for the time period "menu". From the listed objects, drag and drop a menu merchandise object as the primary merchandise within the WorldTime menu.

When you didn’t hold sufficient menu dividers when deleting pointless objects, repeat the above steps and drag and drop a menu separator between the merchandise menu you simply added and the About WorldTime possibility. In the long run, it is best to have the next menu:

Connection of the menu and the standing factor

At this level, we have now a menu nearly prepared to make use of. Now we have to join it to the state factor that we created beforehand within the AppDelegate class.

Change for a second to the AppDelegate.swift file. Go to the highest of the category and declare the next two IBOutlet properties:

Menu @IBOutlet low var: NSMenu?
@IBOutlet low var firstMenuItem: NSMenuItem?

@ IBOutlet low var menu : NSMenu ?

@ IBOutlet low var firstMenuItem : NSMenuItem ?

Let's return to the Foremost.storyboard file the place we have now to attach the WorldItem menu and the brand new menu merchandise that we added beforehand to the IBOutlet properties that we have now simply declared. Comply with these steps:

Choose the Software Delegate object within the doc define. Press and maintain the Ctrl key. Drag and drop it onto the Foremost Menu object. Within the small pop-up window, choose the property of exit from menu to determine connection.

Repeat the identical course of however this time goal the Object menu merchandise:

Choose the Software Delegate object within the doc define. Press and maintain the Ctrl key. Drag and drop it onto the Ingredient menu merchandise. Within the small pop-up window , choose the output property firstMenuItem.

We will now entry the primary menu and its first merchandise programmatically. For now, overlook concerning the first menu merchandise, we'll come again to that in a second. We’ll solely concentrate on the primary menu.

Open the AppDelegate.swift file once more the place we are going to outline the primary menu of the appliance because the menu which ought to seem when the standing merchandise is clicked. Go to the awakeFromNib () technique and add the next traces:

substitute awakeFromNib () operate

substitute func awakeFromNib ( ) .

if depart menu ] = menu

}

What we’re doing right here is assigning the menu property that’s linked to the primary menu to the menu property of the statusItem object. That is all we have to specify the menu that the standing merchandise ought to show when opened. In case you create your personal menus within the storyboard file or programmatically, that is the place you’ll find yourself; to assign your menu to the menu property of the standing merchandise object.

Now re-run the appliance. You will notice that whenever you click on on the WorldTime merchandise within the standing bar, the menu will seem! As well as, the About and Exit menu objects will work. Congratulations!

You might discover it annoying that the primary menu merchandise merely says "Merchandise" or that the Preferences possibility is grayed out. Don’t worry. Quickly we are going to substitute the "Merchandise" menu merchandise with a customized view that can show date and time info for a time zone, and a bit later we are going to create a preferences window that might be linked to the respective menu merchandise in order that it wins' now not be disabled.

Alternative of the title of the factor by a picture

Presently, the standing factor of our utility shows the title "WorldTime", nevertheless it doesn’t look so stunning and it’s definitely not in concord with the remainder of the weather within the standing bar. So let's substitute this title with a picture and make our app even cooler!

When you open the asset catalog, you can find a picture known as "clock". That is the picture that we are going to use with our standing factor. The picture measurement is 18x18px and 36x36px at 1x and 2x respectively.

In AppDelegate, go to the awakeFromNib () technique and remark out or delete this line:

// Remark out the subsequent line.
// statusItem? .button? .title = "WorldTime"

// Remark the next line.

// statusItem? .Button? .Title = "WorldTime"

Then add the next three traces:

let itemImage = NSImage (named: "clock")
itemImage? .isTemplate = true
statusItem? .button? .picture = itemImage

let itemImage = NSImage ( named : "clock" )

itemImage ? . isTemplate = true

statusItem ? . button ? . picture ] = itemImage

First, we load the picture from the catalog of belongings into the itemImage object. By setting isTemplate to true, we be certain that this picture is used as a template and that the system will course of it appropriately in order that it seems to be like some other icon within the standing bar. In case you might be utilizing a coloured picture, observe that this setting will take away its coloration, so omit it if you wish to hold your icons as they had been initially. In the long run, we assign the picture object to the picture property of the button of the factor.

Run the appliance once more and you will notice that our standing factor makes use of the picture we simply outlined as an alternative of the title we had beforehand.

The personalised view

Up to now so good since we managed to transform our macOS utility to an agent utility with a standing bar merchandise that makes use of a picture and shows a menu. Nevertheless, there isn’t a actual performance but, so let's put together the customized view which can show the time and date info on a specific time zone.

This tradition view is the DateTimeView which may be discovered carried out within the DateTimeView.swift file. Additionally, you will discover its counterpart, the DateTimeView.xib which comprises the view's consumer interface (UI). In actual fact, the consumer interface is sort of easy as it’s separated by a bunch of labels that are already linked to the respective IBOutlet properties within the DateTimeView class.

Earlier than persevering with with the implementation of our demo utility, let's focus a bit on the DateTimeView class and on a couple of key factors regarding its performance. Let's begin with the init (body 🙂 initializer, which hundreds the consumer interface from the XIB file utilizing the load technique (fromNIBNamed :). This technique is outlined within the LoadableView protocol and has a default implementation in protocol extension within the LoadableView.swift file. The LoadableView protocol comprises two strategies: one for loading the contents of an XIB file and one which provides one view as a subview to a different whereas additionally defining the required format constraints (we’re going to ; use later).

Observe : The LoadableView protocol was offered in a earlier tutorial. Have a look to search out out extra, in addition to to create customized views and use Cocoa controls on macOS.

The guts of this class is the showDateAndTimeInfo () technique which makes use of a date formatting object to transform the present date, time and time zone to string values ​​that are crammed in for the corresponding labels. What's attention-grabbing right here is that the date and time info displayed pertains to a time zone identifier that the consumer can choose through the Preferences window (we are going to create it later). This most popular time zone ID is learn in Consumer Preferences and is saved within the favoriteTimezoneID property. If such knowledge doesn’t exist within the consumer preferences (as a result of the appliance is used for the primary time and the consumer has not chosen a consumer) most popular time zone identifier), then the FavoriteTimezoneID stays zero and the present time zone is used to show the information for.

Observe : A time zone identifier is a string worth that signifies a area within the kind: "Europe / London" or "Asia / Hong_Kong". All obtainable time zone identifiers are contained within the knownTimeZoneIdentifiers property of the TimeZone class. You will notice that this property might be used later once we put together the Preferences window. Study extra right here.

The showDateAndTimeInfo () technique will not be known as by the beforehand talked about initialization technique, however it’s invoked from a timer initialized in one other technique known as startTimer (). This timer runs repeatedly each second every time DateTimeView turns into seen, in order that the displayed time indication is up to date in actual time. The timer stops within the stopTimer () technique.

With these few phrases in thoughts concerning the DateTimeView class, there are two issues to do. The primary is to initialize an occasion of this class and show it instead of the "Merchandise" menu merchandise. The second is to name the startTimer () and stopTimer () strategies when the appliance menu opens and closes respectively.

Displaying the personalised view

It might look like a difficult process to show the DateTimeView view within the menu of the standing merchandise, however as you will notice right here, it truly is very easy. Open the AppDelegate.swift file and go to the highest of the category. There, declare the next property:

var dateTimeView: DateTimeView?

var dateTimeView : DateTimeView ?

La propriété dateTimeView conservera une occasion de la classe DateTimeView. Il doit s'agir d'une propriété de classe, automobile nous aurons besoin d'accéder à cette occasion dans plusieurs méthodes.

Maintenant, passez à nouveau à la méthode awakeFromNib (). Ajoutez les lignes suivantes à la fin:

remplacer la fonction awakeFromNib ()

    si let merchandise = firstMenuItem
        dateTimeView = DateTimeView (cadre: NSRect (x: zero,zero, y: zero,zero, largeur: 250,zero, hauteur: 170,zero))
        merchandise.view = dateTimeView
    

remplacer func awakeFromNib ( ) .

si laissez article ] = firstMenuItem

}

Dans un premier temps, nous initialisons une occasion DateTimeView en lui fournissant la taille souhaitée (250x170px). L'occasion initialisée est affectée à la propriété dateTimeView. La dernière ligne est celle qui fait apparaître la vue dans le premier élément de menu; nous attribuons simplement l'occasion de vue personnalisée à la propriété view de l'élément de menu.

Exécutez l'utility et cliquez pour ouvrir l'élément d'état de l'utility. La vue personnalisée apparaîtra comme le premier élément du menu!

Cependant, aucune valeur réelle n'y est affichée. Why? Parce que la méthode startTimer () dans DateTimeView n'a jamais été appelée et que les valeurs de date et d'heure n'ont jamais été remplies dans les étiquettes correspondantes. Corrigeons cela.

DĂ©marrage et arrĂŞt de la minuterie

Comme je l'ai expliqué précédemment, la méthode startTimer () initialise et déclenche une minuterie qui s'exécute toutes les secondes, et à chaque répétition, elle appelle la méthode showDateAndTimeInfo () de DateTimeView. Le showDateAndTimeInfo () est celui qui lit la date actuelle, et il crée et remplit les valeurs sur les étiquettes appropriées.

Ce que nous voulons, c'est de faire fonctionner le chronomètre à chaque fois que la vue apparaît, ou en d'autres termes de la démarrer lorsque le menu de l'élément d'état de l'utility s'ouvre. Respectivement, lorsque le menu se ferme, nous voulons que la minuterie cesse de fonctionner.

Il existe des méthodes déléguées que nous pouvons utiliser pour savoir quand le menu s'ouvre et se ferme. Il est cependant nécessaire de définir la classe AppDelegate en tant que délégué du menu en premier. Pour ce faire, recherchez la situation suivante dans la méthode awakeFromNib ():

si laisser menu = menu
    statusItem? .menu = menu

if let menu = menu

statusItem ? . menu = menu

À l'intérieur de l'instruction if let, ajoutez la ligne suivante qui permet à l'AppDelegate de recevoir des messages du délégué de menu de l'élément:

si laisser menu = menu
    statusItem? .menu = menu

    // Ajouter cette ligne …
    menu.delegate = self

if let menu = menu

statusItem ? . menu = menu

// Ajouter cette ligne …

menu . délégué = auto

Maintenant, allez après la fermeture d'AppDelegate (quelques lignes après sa fermeture bouclée) et ajoutez cette extension:

extension AppDelegate: NSMenuDelegate

extension AppDelegate: NSMenuDelegate

 

The primary delegate technique that we’ll implement right here known as when the menu is about to open:

func menuWillOpen(_ menu: NSMenu)

func menuWillOpen(_ menu: NSMenu)

 

In it we’ll name the startTimer() technique by the DateTimeView occasion that we hold within the dateTimeView property:

func menuWillOpen(_ menu: NSMenu)

func menuWillOpen(_ menu: NSMenu)

The opposite delegate technique we have to name is proven proper beneath. In accordance to what we simply did, we are going to name the stopTimer() technique in it so the timer that’s operating to be stopped when the menu will get closed.

func menuDidClose(_ menu: NSMenu)
    dateTimeView?.stopTimer()

func menuDidClose(_ menu: NSMenu)

Right here’s the extension as one piece:

extension AppDelegate: NSMenuDelegate

extension AppDelegate: NSMenuDelegate

When you run the app once more now you will notice that as an alternative of the default label titles there are the precise date and time values! Nice!

What you see is the date and time matching to your native timezone. Proper subsequent we’ll concentrate on displaying the preferences window the place we’ll make it doable to decide on one other timezone identifier, and due to this fact show the date and time particulars for any chosen timezone.

Presenting Preferences

The preferences that our standing bar app will current are going to exist in a standalone window that can seem on the display screen each time the respective possibility is chosen. The preferences performance and the consumer interface exist already within the starter WorldTime venture, and you could find them within the PreferencesView.swift and PreferencesView.xib respectively. Equally to the DateTimeView we met beforehand, PreferencesView is a view object too and it additionally conforms to the customized LoadableView protocol with a view to load the consumer interface from the XIB file.

Preferences view consists by a popup button and two push buttons:

The popup button is populated with all of the obtainable timezone identifiers which might be taken as a group of String values utilizing the knownTimeZoneIdentifiers class property of the TimeZone class. You could find this within the PreferencesView.swift file within the populateTimezoneIDs() technique.

The 2 push buttons are used to avoid wasting the chosen timezone identifier, and to cancel setting a desire. When the Apply button is clicked, the chosen desire is saved to the Consumer Preferences dictionary of the app. Although I’m not a fan of the Consumer Preferences dictionary, utilizing it’s simply fantastic for the needs of this tutorial.

What you would possibly discover attention-grabbing as a normal information on macOS programming and past the standing bar apps is how the preferences window is being dismissed. Check out the dismissPreferences(_:) IBAction technique; there’s only one line:

self.window?.performClose(self)

self.window?.performClose(self)

Maintain the above as a fast approach to dismiss home windows programmatically. self refers back to the view object, so for those who use the above in a view controller and never in a view subclass be sure that to interchange self with the view property of the view controller:

// Exchange "self" with "view" when utilizing the above in view controller subclasses.
view.window?.performClose(self)

// Exchange "self" with "view" when utilizing the above in view controller subclasses.

view.window?.performClose(self)

Usually talking, remember the fact that you may entry the window object of a view controller by its view property as proven above.

Getting ready The Preferences View Controller

Having mentioned the above, it’s about time to utilize the PreferencesView and ultimately current the Preferences window. Let’s begin from the Foremost.storyboard file, the place, for those who bear in mind, we had deleted the default window controller however not the view controller object. We stored it as a result of we’re going to make use of it now.

Choose the View Controller object (the one with round blue and white icon) both within the Doc Define, or within the canvas immediately, after which open the Identification inspector. Within the Storyboard ID discipline set the preferencesID worth. We try this so we will consult with the view controller programmatically and cargo it from the storyboard. We’ll use the storyboard ID shortly.

Then present the Attributes inspector and set the Choose Timezone as the worth of the Title discipline. That is what the Preferences window will show as its title.

Lastly, click on to the view controller’s view after which present the Measurement inspector. Set the view’s width and top to 350px and 130px respectively.

Open the ViewController.swift file now. There are two issues to do right here: The primary one is to load and add the PreferencesView as a subview to the view controller’s view. The second is to carry out some configuration to the window that can include the view controller so it’s at all times displayed centered and it disables the resize and reduce buttons.

Ranging from the latter, it’s fairly possible that the next traces are going to be proved helpful to you in different purposes as properly, so hold them too as a normal information on macOS programming. Right here’s what they do so as of look:

Disable the resize button of the window.Disable the reduce button of the window.Drive the window to middle itself on the display screen.

Copy and paste the next within the ViewController class:

override func viewWillAppear()
    tremendous.viewWillAppear()

    view.window?.styleMask.take away(.resizable)
    view.window?.styleMask.take away(.miniaturizable)
    view.window?.middle()

override func viewWillAppear()

Observe: In keeping with the documentation of the middle() technique: “The window is positioned precisely within the middle horizontally and considerably above middle vertically”. Study extra right here.

Discover that we’re overriding the viewWillAppear() technique with a view to carry out any UI duties; at that time it’s sure that the view has been initialized and any configuration to it or its window will really apply. In addition to that, see as soon as once more that we entry the window object by the view property of the view controller.

Let’s initialize and add the PreferenceView now. Add the subsequent traces to the viewWillAppear() technique:

override func viewWillAppear()

override func viewWillAppear()

    ...

 

    let preferencesView = PreferencesView(body: self.view.bounds)

    preferencesView.add(toView: self.view)

At first we initialize the PreferencesView by making its body equal to the view controller’s view bounds. Then we add it to the view as a subview. The add(toView:) technique is a part of the LoadableView protocol, so open the respective file to see its implementation. Not solely it provides a view as a subview to the one given as an argument, nevertheless it additionally units the required format constraints.

That’s all we needed to do within the ViewController class, because the PreferencesView class is already carried out. Let’s go now to essentially the most attention-grabbing half, the best way to present the Preferences window from our app’s standing merchandise’s menu.

Presenting The Preferences View Controller

Open the AppDelegate.swift file and within the AppDelegate class add the next IBAction technique definition:

@IBAction func showPreferences(_ sender: Any)

@IBAction func showPreferences(_ sender: Any)

 

Now, head to the Foremost.storyboard file the place we’ll join the Preferences menu choice to the above technique. Within the Doc Define, click on to pick the Preferences… menu merchandise. Then, by having the Ctrl key pressed, drag and drop in direction of the Delegate object. Within the small black window that exhibits up, choose the showPreferences: possibility.

Again within the AppDelegate.swift file once more and within the showPreferences(_:) IBAction technique particularly, the very first thing we have to do is to load the ViewController from the storyboard. That is the place we’re going to make use of the storyboard ID that we specified earlier. The way in which to instantiate a view controller from a XIB file programmatically is proven proper subsequent; it’s a way we have now already seen in earlier macOS tutorials too:

@IBAction func showPreferences(_ sender: Any)
    let storyboard = NSStoryboard(title: "Foremost", bundle: nil)
    guard let vc = storyboard.instantiateController(withIdentifier: .init(stringLiteral: "preferencesID")) as? ViewController else

@IBAction func showPreferences(_ sender: Any)

    let storyboard = NSStoryboard(title: "Foremost", bundle: nil)

    guard let vc = storyboard.instantiateController(withIdentifier: .init(stringLiteral: "preferencesID")) as? ViewController else

With the view controller occasion obtainable we have to create a window that can “host” it. As soon as we accomplish that, we will show it. Right here it’s:

@IBAction func showPreferences(_ sender: Any)

@IBAction func showPreferences(_ sender: Any)

The primary line above creates a brand new NSWindow occasion and units the instantiated view controller as its content material view controller. There are different initializers that can be utilized with a view to initialize a window object, however on this case that is essentially the most handy one.

The second line not solely presents the window, nevertheless it makes it the first window (the system provides the main focus to it) and it orders it in entrance, which means above all different home windows on display screen. The few bits of configuration we beforehand did within the viewWillAppear() technique of the ViewController class will apply to the window occasion we simply created.

So, that’s it! Now you know the way to current a view controller from a standing bar app. Run the demo app and click on on the Preferences menu possibility. When you adopted step-by-step up till right here, you then’ll see the Preferences window and it is possible for you to to pick a distinct timezone.

The subsequent a part of the tutorial focuses on the best way to create a standing bar app that shows a popover as an alternative of a menu, and it exhibits a greater configuration and implementation strategy which doesn’t add pointless content material to the App Delegate (aside possibly from a single property). If what you learnt thus far covers you, then it’s okay to cease studying right here. Then again, for those who’re nonetheless hungry to know extra, then simply hold studying!

The Converter Demo App

The second app that we’ll work on to exhibit the best way to create a standing bar app that makes use of a popover known as Converter. Its objective is to transform levels to radians or radians to levels. You possibly can see the way it seems to be like within the following picture:

As you may see, it comprises two textual content fields for getting into the levels and radians values and 4 buttons. Ranging from left to proper, the Convert button triggers the precise conversion. Observe that if the levels textual content discipline has a worth, then that is what’s being transformed. If levels is empty however the radians textual content discipline has a worth, then that is what’s transformed into levels.

Subsequent, it’s the Clear button which merely clears the textual content fields from any values. Most attention-grabbing is the third button known as About, which shows a customized About view controller contained within the popover:

Lastly, the Give up button is clearly there for… quitting the appliance.

An extra objective other than simply making ready the app so it shows the popover when the standing merchandise will get clicked, is to additionally see the best way to change the content material view controller of the popover. That’s why the About view controller exists for.

Begin Getting ready The Converter App

To start, obtain the Converter starter venture and unzip it. When you accomplish that, open it in Xcode. The vast majority of the implementation relating to the view controllers that we are going to current has already been executed. What’s unnoticed of it’s any performance that has to do with the popover.

The 2 view controllers that we are going to use are the ConverterViewController and the AboutViewController, and the respective recordsdata include their implementations. Discover that the default ViewController.swift file that Xcode creates on any new venture doesn’t exist as a result of we don’t want it, so it’s already been deleted. Already created additionally, you will discover the consumer interfaces within the Foremost.storyboard file.

You can find the primary actions we are going to make right here acquainted, as we’ve already seen them within the WorldTime demo utility. Open the Foremost.storyboard file and delete each the window controller and the view controller that’s linked to it.

Relating to the primary menu it’s important to choices: Both to completely delete it, or to go away it as is as a result of it’s not going to look anyway. As a touch, when you might have controls like our textual content fields the place you wish to present system default functionalities utilizing keyboard shortcuts (ex. Undo, Copy, Paste, and so forth), then it’s really useful to maintain the primary menu, or a minimum of the Edit menu, since you’ll get all of them totally free. When you select to delete, then you’ll have to implement the keyboard shortcut actions you wish to present.

Open now the Information.plist file the place we’ll add the required entry that can mark our app as an agent app. That is additionally what makes the primary menu from not being displayed in any respect. Choose the final row within the file, and go to the Editor > Add Merchandise menu, or click on on the small plus button proven on the row. Set the Software is agent (UIElement) as the important thing, and YES as the worth to the brand new entry. Save your file by urgent Cmd+S.

There’s nothing new in our steps thus far, as a result of the above are obligatory actions for any standing bar utility. Now, on condition that we received’t show a menu however a popover when the standing merchandise will get clicked, it’s about time to begin exploring new issues.

The Standing Merchandise Supervisor Class

Beforehand within the WorldTime demo utility we used the AppDelegate class with a view to initialize and configure the standing merchandise, in addition to to deal with all actions and delegate strategies. However as I’ve mentioned fairly a couple of instances already, this isn’t going to be the case right here. We’re going to hold the AppDelegate empty, and our complete work relating to the standing merchandise will happen in a separate class which we’ll name StatusItemManager.

To be able to try this, we’d like a brand new file. Open the File > New > File… menu possibility in Xcode, or just press Cmd+N to begin creating a brand new file. Be certain that to pick macOS and the Swift File because the template:

Title the brand new file StatusItemManager and get completed with its creation. After getting it in entrance of you in Xcode substitute this line:

with this:

That’s obligatory so we will have entry to Cocoa controls, such because the view controllers that our app might be displaying. Now, outline the next class and make it a subclass of the NSObject:

class StatusItemManager: NSObject

class StatusItemManager: NSObject

 

Creating The Popover And The Standing Merchandise

With the StatusItemManager class able to be carried out, add the next three properties:

var statusItem: NSStatusItem?

var popover: NSPopover?

var converterVC: ConverterViewController?

var statusItem: NSStatusItem?

 

var popover: NSPopover?

 

var converterVC: ConverterViewController?

The primary one is the standing merchandise object which we’re going to initialize and configure proper subsequent. The second property is the popover which we’ll additionally initialize right here. Lastly, the third property will hold the ConverterViewController occasion as soon as it’s loaded from the storyboard. We’ll take care of it shortly.

Let’s implement now the next technique which initializes the standing merchandise and units a picture to its button (you’ll discover the “levels” picture within the Belongings catalog):

fileprivate func initStatusItem()
    statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
    let itemImage = NSImage(named: "levels")
    itemImage?.isTemplate = true
    statusItem?.button?.picture = itemImage

fileprivate func initStatusItem()

    statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

    let itemImage = NSImage(named: "levels")

    itemImage?.isTemplate = true

    statusItem?.button?.picture = itemImage

What we simply did is one thing we had additionally executed within the AppDelegate class within the WorldTime demo app. Nevertheless, this isn’t sufficient. We should particularly “inform” to merchandise object what it ought to do when it’s clicked, or in different phrases to specify a way that needs to be known as when customers click on on it. There was no want to take action within the WorldTime app as a result of we had assigned a menu to the merchandise, and displaying that menu was the default, computerized motion outlined by the system.

So, full the tactic implementation with the next two traces:

fileprivate func initStatusItem()

    statusItem?.button?.goal = self
    statusItem?.button?.motion = #selector(showConverterVC)

fileprivate func initStatusItem()

    ...

 

    statusItem?.button?.goal = self

    statusItem?.button?.motion = #selector(showConverterVC)

The primary line units the occasion of the StatusItemManager class as the article the place the goal technique might be present in. The second line specifies the motion technique that needs to be known as when the standing merchandise is clicked. That is the showConverterVC technique which doesn’t exist but. In the meanwhile, let’s simply outline it and we’ll implement it shortly:

@objc fileprivate func showConverterVC()

@objc fileprivate func showConverterVC()

 

The initStatusItem() technique is now full. It initializes the standing merchandise object, it units the picture that might be displayed within the standing bar, and it specifies the tactic to name when it’s clicked. Now, let’s initialize the popover object in a brand new technique:

fileprivate func initPopover()

fileprivate func initPopover()

Together with the popover initialization, we also needs to specify when it ought to turn out to be hidden, or to rephrase that, to specify its habits. Many of the instances the fascinating habits is to cover the popover when customers click on wherever outdoors of it. That is what we wish for the demo utility right here as properly, and with a view to obtain that we’d like yet another line within the above technique:

fileprivate func initPopover()
    popover = NSPopover()

    // Specify the popover's habits.
    popover?.habits = .transient

fileprivate func initPopover()

The transient worth makes the popover to shut when customers click on wherever else on the display screen however not on the popover’s space. If you wish to hold the popover seen and make it disappear solely by some motion in your app, then you should utilize the applicationDefined worth.

Observe: Try the documentation for extra details about the habits property of the popover right here and right here. When you use the applicationDefined worth because the popover’s habits, don’t overlook to manually shut it when customers carry out a related motion by calling the shut() technique by the popover property.

Initializing The StatusItemManager

I’ll present you two methods to initialize an object of the StatusItemManager class; programmatically and utilizing the storyboard file. I’ll begin with the programmatic strategy, and for that we’ll must implement the next initializer in our class:

override init()
    tremendous.init()

    initStatusItem()
    initPopover()

override init()

    tremendous.init()

 

    initStatusItem()

    initPopover()

As you may see, we name the 2 strategies we carried out proper above with a view to initialize the standing merchandise and the popover.

Now, change to the AppDelegate.swift file and declare the next property within the AppDelegate class:

var statusItemManager: StatusItemManager!

var statusItemManager: StatusItemManager!

Within the applicationDidFinishLaunching(_:) technique initialize it:

func applicationDidFinishLaunching(_ aNotification: Notification)
    statusItemManager = StatusItemManager()

func applicationDidFinishLaunching(_ aNotification: Notification)

    statusItemManager = StatusItemManager()

It’s essential to declare the statusItemManager as a category property as a result of we might want to entry it out of the AppDelegate as you will notice later. When you run the app at this level you’ll see solely the standing merchandise’s picture, however nothing will occur whenever you’ll faucet on it; do not forget that the motion technique known as showConverterVC() is outlined however not carried out but.

To be able to apply the second strategy for initializing a StatusItemManager object it’s important to remark out or delete the additions we simply made to the AppDelegate class. As an alternative of them, declare the next IBoutlet property solely (we’re nonetheless within the AppDelegate.swift file):

@IBOutlet weak var statusItemManager: StatusItemManager!

@IBOutlet weak var statusItemManager: StatusItemManager!

Now open the Foremost.storyboard file and within the Doc Define increase the Software Scene object.

Then:

Click on on the Plus button in Xcode’s toolbar to open the objects library.Kind the phrase “object” to seek for an object.Choose the row titled “Object” with the blue dice in it.Drag and drop it within the record of objects beneath the Software Scene, beneath the First Responder.

With the brand new object chosen, open the Identification inspector and set the StatusItemManager as the worth of the Class discipline beneath the Customized Class part.

Each time the app will run any more this object might be a StatusItemManager occasion. On prime of that, we marked the StatusItemManager class as a NSObject subclass precisely only for that; to handle to set the StatusItemManager customized sort as the article’s sort. Objects in Interface Builder should inherit from the NSObject class.

There’s one final step to do within the storyboard and that’s to attach the brand new object to the statusItemManager IBOutlet property of the AppDelegate. That’s obligatory so we will entry it programmatically later.

Choose the AppDelegate object and by urgent the Ctrl key, drag and drop to the StatusItemManager object. Within the small popup window that can seem, choose the statusItemManager outlet.

Lastly, return to the StatusItemManager.swift file. It’s required to override the awakeFromNib() which might be known as by the article we simply added to the storyboard. That is the place the place we’ll name the 2 strategies that initialize the standing merchandise and the popover:

override func awakeFromNib()
    tremendous.awakeFromNib()

    initStatusItem()
    initPopover()

override func awakeFromNib()

    tremendous.awakeFromNib()

 

    initStatusItem()

    initPopover()

Presenting The Converter View Controller

Only a few moments earlier we outlined the showConverterVC() technique within the statusItemManager class, and we set it because the motion technique that might be known as each time the standing merchandise of the app is clicked. There are three issues that we wish to obtain on this technique:

To instantiate a ConverterViewController object and assign it within the converterVC property in case this hasn’t occurred already.To set the converter view controller because the content material view controller of the popover.To indicate the popover.

We’ll begin by ensuring that the next should not nil:

@objc func showConverterVC()

@objc func showConverterVC()

    guard let popover = popover, let button = statusItem?.button else

The above ensures that the tactic execution received’t proceed if for any purpose the popover or the standing merchandise button haven’t been initialized. You’ll perceive fairly quickly why we’d like the merchandise’s button.

Subsequent, we’ll examine if the worth of the converterVC property is nil or not. Clearly, this property goes to be nil earlier than the primary time the popover will present up as a result of there isn’t a occasion of the converter view controller. Nevertheless, in subsequent appearances of the popover there’s no purpose to instantiate repeatedly new objects of the identical view controller; that’s why we’ll hold it within the converterVC property.

@objc func showConverterVC()

@objc func showConverterVC()

Now we have already seen the above approach for instantiating a view controller from the storyboard whereas we had been making the WorldTime demo utility, and we met it in earlier tutorials too. As soon as we load and initialize an occasion of the ConverterViewController class from the storyboard, we assign it within the converterVC property. An extra purpose we hold the view controller in that property is the About view controller that our app can even show; we are going to enable customers to return to the purpose that that they had left off when the About view controller appeared.

The subsequent step is to assign the converter view controller because the content material view controller of the popover:

@objc func showConverterVC()

@objc func showConverterVC()

Lastly, let’s present the popover:

@objc func showConverterVC()

@objc func showConverterVC()

    ...

 

    popover.present(relativeTo: button.bounds, of: button, preferredEdge: .minY)

You possibly can see that this technique requires a body and a view to be supplied with a view to show the popover correctly, in addition to the sting of the view that the popover will level to. We wish the popover to level to our merchandise (extra particularly to the underside facet of the merchandise’s button), that’s why we offer the button because the anchor view. The minY edge means the facet with the bottom Y worth, which in macOS is the underside facet.

The showConverterVC() technique is now full. Run the app and click on to the standing merchandise; you’ll see the popover exhibiting up for the primary time and presenting the converter view controller. You’re able to make actual conversions!

Presenting The About View Controller

All of the earlier dialogue concerning the Converter demo utility is all it’s worthwhile to know in case you’re planning to indicate just one view controller within the popover. Nevertheless, there might be instances the place you’ll wish to current multiple view controllers to the popover, and that is what we’re going to see on this half.

Observe: In case you wish to current view controllers in standalone home windows and never within the popover, then see how we did it within the WorldTime app. The identical approach to load the view controller, create a window and current it with the view controller applies in standing bar apps with popovers too.

So, our objective right here is to vary the content material view controller of the popover and to indicate the customized About view controller. To get began, we have to add a brand new technique within the StatusItemManager class which can load and initialize an AboutViewController occasion from the storyboard. That occasion might be set because the content material view controller of the popover.

What I simply mentioned doesn’t embrace something new; we have now already seen all obligatory steps. Right here’s the brand new technique:

func showAbout()

func showAbout()

    guard let popover = popover else

 

    let storyboard = NSStoryboard(title: "Foremost", bundle: nil)

    guard let vc = storyboard.instantiateController(withIdentifier: .init(stringLiteral: "aboutID")) as? AboutViewController else

 

    popover.contentViewController = vc

At first we ensure that the popover has been initialized in any other case there’s no purpose to proceed. Then, we instantiate an AboutViewController object and at last we assign it to the contentViewController property of the popover.

This technique must be known as by some means. We wish to current the AboutViewController when the About button is clicked within the ConverterViewController, so open the ConverterViewController.swift file and go to the showAbout(_:) IBAction technique.

Presently that technique is empty, however the lacking logic is what we’re nearly so as to add. What we wish to do right here is fairly easy: We have to entry the operating occasion of the StatusItemManager and name the showAbout() technique we carried out there. However how will we try this?

When you bear in mind, the AppDelegate class comprises the statusItemManager property, both this can be a regular class property or an IBOutlet property. It doesn’t actually matter the way you selected to initialize the StatusItemManager occasion (see the Initializing The StatusItemManager half), so long as the AppDelegate comprises that property.

Having that in thoughts, we’ll first entry the app delegate, after which by it we’ll entry the statusItemManager which we’ll ultimately use to name the showAbout() technique. Within the showAbout(_:) IBAction technique now of the ConverterViewController add the next:

@IBAction func showAbout(_ sender: Any)

@IBAction func showAbout(_ sender: Any)

    guard let appDelegate = NSApplication.shared.delegate as? AppDelegate, let itemManager = appDelegate.statusItemManager else

    itemManager.showAbout()

Accessing the app delegate occasion of the app is finished utilizing this:

NSApplication.shared.delegate as? AppDelegate

NSApplication.shared.delegate as? AppDelegate

This produces an non-obligatory worth, so utilizing the guard assertion (or an if let assertion alternatively) is just about obligatory so we’re protected. Additionally, the statusItemManager is an non-obligatory worth too, so the:

let itemManager = appDelegate.statusItemManager

let itemManager = appDelegate.statusItemManager

assertion ensures that the property we wish to use will not be nil.

By having unwrapped the statusItemManager property to the itemManager fixed, we name the showAbout() of the StatusItemManager class and due to this fact current the About view controller.

Run the app now and click on on the About button; you will notice the About view controller seems within the popover. Furthermore, the popover will adapt to the view controller’s view measurement which is smaller than the converter’s view:

Dismissing The About VC And Returning To Converter

Although we efficiently managed to current the About view controller, we can’t return to the converter as a result of the Again button doesn’t work but. There are some lacking items that can enable us to dismiss this view controller and present the earlier one once more.

Go to the StatusItemManager.swift file the place we’ll add the next technique:

func hideAbout()
    guard let popover = popover else
    popover.contentViewController?.dismiss(nil)
    showConverterVC()

func hideAbout()

    guard let popover = popover else

    popover.contentViewController?.dismiss(nil)

    showConverterVC()

As soon as we make the required examine that the popover will not be nil by unwrapping it within the guard assertion, we dismiss its content material view controller; that’s the About view controller. Then, we name the showConverterVC() technique which can assign the beforehand loaded occasion of the ConverterViewController to the popover’s content material and it’ll ultimately current it once more.

Open the AboutViewController.swift file now and head to the dismissAbout(_:) IBAction technique. Equally to what we beforehand did, we’ll first entry the app delegate occasion of the app, and thru it we’ll use the statusItemManager property with a view to name the hideAbout() technique. Replace the IBAction technique as proven subsequent:

@IBAction func dismissAbout(_ sender: Any)
    guard let appDelegate = NSApplication.shared.delegate as? AppDelegate, let itemManager = appDelegate.statusItemManager else
    itemManager.hideAbout()

@IBAction func dismissAbout(_ sender: Any)

When you run the app now the Again button within the About view controller might be completely working!

A Small Visible Enchancment

The Converter standing bar app is prepared, however nonetheless there’s a small enchancment we will make on it. When you begin switching between the Converter and the About view controllers, you’ll see that the transition from one to a different will not be fairly nice to the attention. The popover is resizing and the contained controls within the view controllers are shifting accordingly whereas every view controller continues to be seen, and that leaves us with a foul style and the sensation that a harsh motion is going on.

We will make this smoother by doing one thing actually easy: To cover the view of the view controller that’s about to vanish, earlier than it will get disappeared.

Let’s begin with the showAbout() technique within the StatusItemManager.swift file. Proper after the guard assertion and earlier than the opposite technique contents add the next line:

func showAbout()
    guard let popover = popover else

    // Add this line to cover the Converter view earlier than the popover will get resized.
    popover.contentViewController?.view.isHidden = true

func showAbout()

Subsequent, within the hideAbout() technique and as soon as once more proper after the guard assertion, add the very same command which can make the About view to turn out to be hidden earlier than it will get dismissed and the popover will get resized:

func hideAbout()
    guard let popover = popover else

    // Add this line.
    popover.contentViewController?.view.isHidden = true

func hideAbout()

Lastly, don’t overlook to make the Converter’s view seen once more on the finish of the identical technique:

func hideAbout()

func hideAbout()

    ...

 

    popover.contentViewController?.view.isHidden = false

This line might be executed after the popover can have offered the converter view controller once more.

Run yet another time and you will notice that switching between the view controllers is smoother and never any extra laborious to the attention.

Conclusion

Coming to the tip of this put up lastly, I hope you discovered the 2 pattern purposes and the at this time matter attention-grabbing and enticing. Although this ended up being a protracted tutorial, what you learn here’s what it’s worthwhile to make standing bar apps of any of the 2 sorts mentioned.

Earlier than you begin creating such an app, first think about what you wish to present to your customers. When you solely purpose to current simply choices, or choices mixed with some form of informational views (aka customized views that don’t require consumer enter) then select the menu-style app. In any other case go for the popover answer, particularly for those who require consumer enter. In any case, essentially the most profitable apps mix parts of each, in addition to different information on macOS programming.

When you haven’t learn the earlier tutorials on the macOS programming collection then I like to recommend you to take action, there are many attention-grabbing stuff to study. And with that I depart you; take your time to learn and play with your personal standing bar apps, and brainstorm in your subsequent superior macOS venture. Qui sait? It could be a standing bar app!

For reference, you could find the whole initiatives right here and right here.

Credit: Icons by Icons8