Instantiating Views from Mac Storyboards

May 30th, 2017

Filed under: Cocoa, Mac Development | Be the first to comment!

This is another post I’m writing as a reference to myself in case I need to deal with this subject in the future. There may be better ways to instantiate views than what I describe here. I’m sharing this information because there’s not much information available on Mac storyboards.

Reading the following articles may help you follow along:

Introduction

When developing user interfaces for Mac apps, you can normally lay out the whole interface in Interface Builder at design time. But sometime you need to add user interface elements at run time. Many PDF viewers let people add sticky notes to PDF documents. The developer of a PDF viewer would create a text view or custom view for the sticky note in Interface Builder but wait until the user decides to add a sticky note to add the view to the PDF document.

Instantiating a view from a storyboard involves the following high level steps:

  1. Create a subclass of NSViewController for your view.
  2. Add a new scene to the storyboard for your view controller.
  3. Set the class of the new scene’s view controller to your subclass.
  4. Add an IBAction to the view controller that will create the view.

Creating a View Controller Subclass

Storyboard scenes need a view controller. If you’re going to instantiate a view from a storyboard, you need to create a view controller for the view.

To add a view controller to your project, choose File > New > File in Xcode. Select Cocoa Class from the list of file templates. Name your class and make it a subclass of NSViewController. When you’re done, the class should look similar to the following:

class StickyNoteViewController: NSViewController {
    @IBOutlet var textView: NSTextView?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
    }

}

I decided to stick with the sticky note theme and use a text view as the sticky note. Your class won’t have the @IBOutlet variable. Add an outlet for your view so you can connect it in Interface Builder.

Add a New Scene

Open your storyboard. Drag a view controller from the object library to the storyboard canvas to create a new scene.

Add your view to the view controller in the new scene. Configure the view using the attributes inspector. If you created an outlet for the view in your view controller subclass, connect the view to that outlet.

Set the View Controller Class

Now that you’ve added the scene, set the view controller class to your subclass. Select the view controller in Interface Builder and open the identity inspector.

CustomClassAndStoryboardID

Enter the name of your view controller subclass in the Class combo box.

While you are in the identity inspector, enter a name for the view controller in the Storyboard ID text field. You will need this ID when you load the view controller from the storyboard.

Add an IBAction to Create the View

Now it’s time to instantiate the view. Create an IBAction in the view controller where you’re going to create your view. In the IBAction you’ll instantiate the view.

@IBAction func addStickyNote(_ sender: AnyObject) {
    if let board = storyboard {
        let stickyNoteController = board.instantiateController(withIdentifier: "stickyNote")
            as! StickyNoteViewController
        addChildViewController(stickyNoteController)
        view.addSubview(stickyNoteController.view)
    }
}

What the code does is load the NSViewController subclass from the storyboard, add the view controller as a child view controller, and add the view as a subview.

Don’t forget to add a menu item or a control and connect it to this IBAction.

Facebooktwittergoogle_plusredditmail

RWDevCon 2016 Vault

April 26th, 2017

Filed under: iOS Development, Mac Development | Be the first to comment!

I saw the site raywenderlich.com made the videos of their RWDevCon 2016 conference freely available.

RWDevCon 2016 Vault

There are 24 videos to watch along with materials to download so you can follow along with the tutorials in the videos. The videos are a year old and use Swift 2 so you’ll have to convert the code to Swift 3 if you’re using Xcode 8. But the RWDevCon 2016 Vault looks like a great resource for people wanting to learn iOS development, Xcode tips, and Instruments tips.

Facebooktwittergoogle_plusredditmail

Xcode 8.3: Instruments Display Settings Moved

April 18th, 2017

Filed under: Instruments | Be the first to comment!

Prior to Xcode 8.3 the configuration section of the Instruments trace document window had an area for display settings, as you can see in the following image:

InstrumentsCallTreeCheckboxes

Starting with Xcode 8.3 the display settings area has been removed from the configuration section. You can access settings you configure before starting a trace by choosing File > Recording Options in Instruments. The following image shows the configuration options for the Allocations instrument:

InstrumentsRecordSettings

The remaining display settings are at the bottom of the window. The following image shows the bottom of the window for the Allocations instrument:

InstrumentsDisplaySettingsXcode8 3

Click the Call Tree button to access the Call Tree series of checkboxes.

CallTreeCheckboxes

Facebooktwittergoogle_plusredditmail

Xcode 8.3: Cocoa Projects Use Storyboards

April 3rd, 2017

Filed under: Xcode | Be the first to comment!

In Xcode 8.3 Apple removed the Use Storyboards checkbox for new Cocoa application projects. New projects use storyboards. Keep a copy of Xcode 8.2 to create new projects if you prefer using xib files.

If you want to use xib files instead of storyboards in Xcode 8.3, you’ll have to remove the storyboard from the project and add a xib file to the project. You will also have to tell Xcode to use your xib file as the main interface file for the project.

  1. Select your project from the project navigator to open the project editor.
  2. Select the application target from the left side of the project editor.
  3. Click the General button at the top of the project editor.
  4. Enter the name of the xib file in the Main Interface combo box. Don’t enter the .xib extension.

SetMainInterface

Additional information can be found in the following thread on Apple’s developer forums:

Did Xcode 8.3 update eliminate the basic AppDelegate project template?

Facebooktwittergoogle_plusredditmail

Creating a Simple Mac Application Using Cocoa, Swift, and Storyboards

March 31st, 2017

Filed under: Cocoa, Mac Development | Be the first to comment!

I’ve noticed there aren’t many articles or tutorials online about writing Mac apps in Swift. To help fill the void I’m writing this tutorial that guides you through the creation of a simple Cocoa app in Swift.

The app converts temperatures from Celsius to Fahrenheit. Enter a temperature in Celsius, click a Convert button, and the app shows the temperature in Fahrenheit. I can’t think of a simpler app to introduce Cocoa programming.

The screenshots in this article are from Xcode 8.3. If you are using a different version of Xcode, some screens may look different.

Create the Project

Open Xcode and choose File > New > Project. The New Project Assistant opens. Select Cocoa Application, which is in the macOS section.

NewProjectStep1

Click the Next button to move to the next step.

NewProjectStep2

Enter a name for the project in the Product Name text field. I chose the name TemperatureConverter.

Select None from the Team menu. Teams are used to submit apps to the App Store. This project isn’t going on the App Store.

The organization name appears in the copyright notice of new source code files Xcode creates. If you don’t have an organization name, enter your name in the Organization Name text field.

The organization identifier is a way to uniquely identify yourself and the app you’re creating for things like preference files. There should be no spaces in the organization identifier. Something like com.YourOrganizationName should work fine for this project.

Choose Swift from the Language menu.

Starting with Xcode 8.3 Cocoa application projects use storyboards by default. If you’re using an earlier version of Xcode, you should see a Use Storyboards checkbox. Select the Use Storyboards checkbox.

Deselect the Create Document-Based Application and Use Core Data checkboxes. You can also deselect the Include Unit Tests and Include UI Tests checkboxes if you want.

Click the Next button to move to the last step of the project creation process. Choose a location to save your project. If you want to put your project under version control, select the Create Git repository checkbox. Click the Create button.

When you create the project, the left side of the project window shows all the files in the project. The two files you will be working with in this tutorial are ViewController.swift and Main.storyboard. ViewController.swift contains the source code. Main.storyboard contains the user interface.

Creating the User Interface

To create the user interface for this project, you must open Main.storyboard. Select Main.storyboard from the left side of the project window to open the storyboard. The initial storyboard Apple provides for a Cocoa application looks similar to the following screenshot:

StoryboardStartingPoint

Xcode provides a menu bar, a window controller for an empty window, and a view controller for the window’s content view. To build the user interface for this project, you’re going to add the following items to the view controller:

  • A text field to enter the Celsius temperature
  • A label to show the Fahrenheit temperature
  • A button to perform the temperature conversion
  • Explanatory labels for the text field and Fahrenheit temperature label

The user interface elements are in Xcode’s object library. Choose View > Utilities > Show Object Library to open the object library, which is in the lower right corner of the project window.

ObjectLibrary

There is a search field at the bottom of the object library you can use to filter user interface elements. Type Label to find the label. Type Text Field to find the text field. Type Button to find a button.

Drag a user interface element from the object library to the view controller. Double-click on a label or button to change its text.

When you are finished the view controller should look similar to the following:

UserInterface

I used a push button for the Convert button.

Create the Outlets

Select the file ViewController.swift from the left side of the project window. What you need to do is add outlets for the Celsius text field and the Fahrenheit label so you can access them in your code.

@IBOutlet weak var celsiusTextField: NSTextField!
@IBOutlet weak var fahrenheitLabel: NSTextField!

Let’s go through the outlet declarations piece by piece.

  • @IBOutlet tells the compiler the variable is an outlet so you can make a connection to it in Interface Builder, which is the part of Xcode you use to design user interfaces.
  • weak says the outlet is a weak reference. If you remove the word weak the outlet becomes a strong reference. Weak and strong references deal with Cocoa’s memory management. A deeper explanation is beyond the scope of this tutorial.
  • var says the outlet is a variable and not a constant. Outlets must be variables.
  • After the var is the name of the variable.
  • NSTextField tells the compiler the type of the outlet. The outlets in this tutorial are text fields. Labels are text fields whose values can’t be edited.

The exclamation point after NSTextField requires more explanation. The exclamation point tells the compiler the outlet is an implicitly unwrapped optional. An optional value may or may not exist. Declaring the two outlets doesn’t mean the text field and label exist in the storyboard. The app won’t know until it launches the storyboard and loads the window and its contents.

An implicitly unwrapped optional states the variable is guaranteed to exist. If an implicitly unwrapped optional does not exist and your app accesses it, the app crashes. In your Swift code you should avoid using implicitly unwrapped optionals because they can cause your app to crash. Implicitly unwrapped optionals are OK to use for outlets because the storyboard is going to load. If the text field and label don’t exist, the app isn’t going to work so you might as well crash.

Connect the Outlets

Now you need to connect the text field and label in the storyboard to the outlets you created. If you don’t make these connections, your app will crash when it runs.

Open Xcode’s assistant editor so you can have the files ViewController.swift and Main.storyboard open at the same time. Choose View > Assistant Editor > Show Assistant Editor to open the assistant editor. Open ViewController.swift in one editor and Main.storyboard in the other editor.

Select the Celsius text field in the storyboard. Hold down the Control key and drag it to the celsiusTextField variable in the source code file. When you reach the variable, it should highlight and there should be an option to connect the outlet. Release the mouse button when it says Connect Outlet.

Making connections in Xcode can be frustrating. Sometimes the Connect Outlet option does not appear. Keep trying and eventually you will be able to make the connection.

After connecting the Celsius text field, connect the label. Select the label. It should be the one with the value 0, not the one that says Fahrenheit Temperature. Control-drag the label to the fahrenheitLabel variable. Choose Connect Outlet. When you connect the outlets, the left side of the editor has a connection symbol for the outlets, as you can see in lines 13 and 14.

ConnectionMarks

Write the Code

Now it’s time to start writing code. Open the file ViewController.swift and add the following two functions:

@IBAction func convertToFahrenheit(_ sender: AnyObject) {
    if let celsiusTemperature = Float(celsiusTextField.stringValue) {
        let fahrenheitTemperature = convert(celsius: celsiusTemperature)
        fahrenheitLabel.stringValue = fahrenheitTemperature.description
    }
}

func convert(celsius: Float) -> Float {
    return ((celsius * 1.8) + 32.0)
}

Let’s look at the signature of the convertToFahrenheit function.

  • @IBAction tells the compiler the function is an Interface Builder action so you can connect the Convert button to it.
  • func is the keyword to declare a Swift function.
  • The function takes an argument named sender, which has type AnyObject. All Interface Builder actions take a sender as an argument. The _ character before sender means you don’t have to type sender: when calling convertToFahrenheit the way I typed celsius: when calling the convert function.

What complicates the convertToFahrenheit function is the fact that text fields and labels store strings. You cannot do calculations with strings because strings might not have numeric values. To convert the temperature to Fahrenheit, you need to convert the value of the Celsius text field to a floating-point number. That’s what the first line of code does. The if let statement allows you to safely handle the situation where there’s no number in the text field. If someone enters XYZ in the Celsius text field, the function returns without doing anything.

The second line calls the function to convert the Celsius temperature to Fahrenheit. The third line sets the Fahrenheit label’s text to the Fahrenheit temperature.

The convert function takes the Celsius temperature and returns the Fahrenheit temperature using the following formula:

F = (1.8 * C) + 32

Connect the Convert Button

Now the last step is to connect the Convert button so that the conversion occurs when you click the button. Open Main.storyboard. You should see the object list on the left side of the editor.

ObjectList

Select the Convert button. Control-drag to the First Responder object in the view controller scene. You won’t be able to connect to the First Responder object in the application scene. When you make the connection, a HUD opens with a long list of actions to choose from. Select convertToFahrenheit. Now when someone clicks the Convert button, it calls the convertToFahrenheit function.

Turn Off Code Signing

Xcode projects are initially set to use code signing so you can submit your apps to the App Store. Code signing requires you to be a member of Apple’s paid developer program. I can’t assume everyone reading this article is a member of Apple’s paid developer program. That’s why I had you choose None from the Team menu when you created the project. Because there is no team you have to turn off code signing for the project to get the project to build.

To turn off code signing, perform the following steps:

  1. Select the project from the project navigator on the left side of the project window to open the project editor.
  2. Select the application target from the list of targets on the left side of the project editor.
  3. Click the Build Settings button at the top of the project editor.
  4. Set the Code Signing Identity build setting to Don’t Code Sign.

TurnOffCodeSigning

Build the Project

In the upper left corner of the project window is the Run button, which looks like the Play button on a DVD player. Click the Run button to build your project and run it.

If you run into problems building the project, make sure there are no typing mistakes in your code. Making a tiny mistake typing the name of a variable or function is enough to prevent the compiler from building your project.

If the app crashes when you run it, make sure you connected the outlets. My Finding Where Your App Crashes in Xcode article can help you locate the cause of the crash.

If clicking the Convert button does nothing, make sure you connected the button to the IBAction and make sure you entered a numeric value in the Celsius text field.

If all else fails, I have the project on GitHub for you to download.

Where to Go From Here?

You could build upon this project by making the conversion from Fahrenheit to Celsius.

To learn more about Cocoa programming, you best source is Apple’s documentation, which you can access in Xcode by choosing Help > Documentation and API Reference. I wish I could recommend a book on Mac development, but when I searched Amazon for Cocoa programming books, the last book was published in April 2015 and used Swift 1.

Facebooktwittergoogle_plusredditmail