Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all 4370 articles
Browse latest View live

Getting Started With OS X and Swift Tutorial: Part 1/3

$
0
0
Get started with OS X development!

Get started with OS X development!

Update note: This tutorial was updated to Swift by Joe Howard. Original tutorial by Ernesto García.

It’s a good time to be an iOS developer. Not only can you release your apps to the iTunes App Store, but you also have the foundational skills to become a Mac developer, since iOS development and Mac development are quite similar!

If you’re an iOS developer and you’re curious about learning the basics of becoming a Mac developer so you can start migrating your iOS apps to the desktop, this tutorial is for you.

In this tutorial, you’re going to build your first Mac application, specifically a Mac version of the app we created in the How To Create A Simple iPhone App tutorial.

If you’ve followed that tutorial, you will be familiar with most of the steps in this one, and you will be able to see the main differences between iOS and Mac programming. By the end, you’ll have a great introduction to developing with OS X and Swift!

If you haven’t followed it, don’t worry. It’s not required in order to read and understand this one – we’ll guide you along the way step by step.

While making this app, you’ll learn the following topics:

  • How to create a Mac App in Xcode
  • Learn the basic structure of a Mac app
  • Learn the main differences between OS X and iOS
  • How to use Table Views – including adding and deleting rows
  • How to use a text field, a button and an image view
  • How to select an image from your hard drive, or capture a picture from your computer’s camera
  • How to handle window resizing using Auto Layout
  • How to persist data between app sessions

This tutorial is for beginner Mac developers, but it assumes that you are familiar with Swift programming and with Xcode. Knowledge of iOS programming is recommended to follow this tutorial, but not mandatory.

In this first part of a three-part series, we’ll cover how to load your model with a list of bugs and display them in a table view.

Getting Started

Creating a Mac project is very similar to creating an iOS project – it still uses Xcode, just a different template!

So start by going to File\New Project… in Xcode, and in the window that pops up, select “Application” in the “OS X” section. Then click Next.

New Project

On the next page, you will enter the application information. Type ScaryBugsMac in the product name field, add your organization name, and select a unique organization identifier. Apple recommends using a reverse domain format. Leave the rest of the text fields blank.

Finally, make sure that Swift is selected as the language, that none of the checkmarks are checked, and leave the document extension field blank. When you’re done, click Next.

New Project

Now Xcode will ask you for a location to save the project. Choose a folder on your computer and click “Create”.

The project is ready, and you should have a Mac application with a single empty window. Let’s check out how it looks. Find the “Run” button, which is located in the left side of the toolbar at the top of Xcode. Click it and Xcode will begin to build the app.

When Xcode finishes building the application, you should see the main window of your application.

Empty Window

This shows you three things: first, you chose the correct template and it works (yay!); second, it is a good blank starting point to build upon; and third, there are some big (and somewhat obvious) differences from developing for iOS:

  • The window doesn’t have to be a particular fixed size, such as the iPhone or iPad’s screen size – it can be fully resizable!
  • Mac apps can have more than one window, and you can minimize them, arrange them however you like, etc.

Let’s do something with this window, and make it show some information about bugs. Just like in iOS, the first thing to do is to create a new View Controller. In this view, you will define the user interface of the main app.

To create a new View Controller, go to File\New\File… , and in the window that pops up, choose OS X\Source\Cocoa Class, and click Next.

Create MasterViewController

Name the class MasterViewController, and type NSViewController for “Subclass of”. Make sure that the option “Also create XIB file for user interface” is selected. Click Next.

Create MasterViewController

In the final popup, click Create again. Now your new view controller is created and your Project Navigator should look similar to this:

Project Navigator

Now that you’ve created the view controller, it’s time to place the UI items on it. In the Project Navigator, click on MasterViewController.xib. That will load the visual representation of the view controller you just created in Interface Builder.

Interface Builder lets you build your user interfaces in a visual way. You just need to drag a component onto your view and position or resize it according to your application’s needs.

The first thing your app needs do is to show a list with the Bugs. For that, you are going to need a table view. In OS X, the control is called NSTableView (similar to UITableView in iOS).

If you’re familiar with iOS programming, you may be able to see a pattern here. Many user interface classes that are in UIKit were originally derived from classes that already existed in OS X’s AppKit. Many of them just changed the NS prefix used in OS X to the UI prefix used in iOS.

So, as a rule of thumb, if you are wondering if some iOS control you know and love may already exist on the Mac, you can try and look for the same class with NS. You’ll be surprised how many you’ll find – NSScrollView, NSLabel, NSButton and more! Note that the APIs for these controls might be quite a bit different from the iOS variants. In many cases though, Apple has been moving to make the OS X versions more like the iOS versions.

The user interface controls are located in the bottom right part of the screen. Make sure the third tab is selected (which is the tab with the UI controls) and find the NSTableView control. (You can scroll down the controls list until you find it, or you can type NSTableView in the panel’s search field).

Add Bugs NSTableView

Drag the table view from the panel onto the view and position it near the top left corner. Don’t worry now about the table size, you will take care of that later.

smats-addbugstableview2

Now you have a view with a table on it, but you still haven’t added the view controller to the main window so it won’t show up. Open AppDelegate.swift to get started with that.

Now you are going to create a property for the view controller. In Swift, the implicit namespace and scope for classes is the app project, so there is no need to import MasterViewController in the Application delegate.

Add the following code just below the declaration of the window property:

var masterViewController: MasterViewController!

Now the Application Delegate has a MasterViewController property, but the view won’t be shown on the application’s screen yet. To do that you need to instantiate the variable to create a new view, and after that, you need to add the newly created view to the main window of the application.

The Application delegate has a method applicationDidFinishLaunching that is called by the operating system when the application first launches. That is the point where you should add all the initialization code that is meant to run only once when the application starts.

Note: If you are familiar with iOS programming, this method is the OS X equivalent to application(_:didFinishLaunchingWithOptions:).

Insert this code inside applicationDidFinishLaunching:

masterViewController = MasterViewController(nibName: "MasterViewController", bundle: nil)
 
window.contentView.addSubview(masterViewController.view)
masterViewController.view.frame = (window.contentView as NSView).bounds

First, you instantiate a new MasterViewController using the objects in the interface builder file of the same name. Once it’s created, you add it to the main window and set its frame to take up the entire content view.

The windows in OS X (NSWindow class) are always created with a default view, called contentView, which is automatically resized to fill the window. If you want to add your own views to a window, you will always need to add them to the contentView using addSubview.

The last line just sets the size of your view to match the initial size of the window. Comparing this again with iOS programming, it’s a bit different. In iOS you would set the window’s rootViewController, or rely on a storyboard to load the initial app view controller. But rootViewController does not exist in OS X, so instead you add your view to the window’s contentView.

Now, if you build and run, you will see that the main window shows your view with your table view. Nice – now you’re starting to get somewhere!

Empty NSTableView

A Scary Data Model: Organization

So far you have a window with a nice table view on it, but it doesn’t display anything. You want it to show some information about your scary bugs – but wait, you don’t have any data to display either!

In the next steps, you are going to create a data model for the application, but before that, you are going to see a way to keep things organized in the Project Navigator.

Note: This is an optional section that shows you how to organize files into groups. If you’re already familiar with groups in the project navigator, feel free to skip to the next section.

This your current organization in the Project Navigator section of Xcode:

Project Navigator

The default template creates a group with the application’s name, and a sub-group for the supporting files (plist, resources, etc). When your project grows up, and you have to deal with lots of files, it becomes more and more difficult to find the files you need.

In this section we’re going to show a way to organize your files. This organization is quite subjective, so feel free to change it to any organization you feel comfortable with.

First, you are going to create a group to store your user interface files, and you’re going to name it “GUI”. To create it, control-click (or click the mouse right button) the ScaryBugsMac group.

In the menu that pops up, choose “New Group”. The group created is automatically selected and you can type the new name “GUI”.

Now, drag the user interface files to that group ( AppDelegate.swift, MasterViewController.swift/.xib and MainMenu.xib). After dragging them, your Project Navigator should look like this:

Project Navigator: GUI

Now create a second group inside ScaryBugsMac, and name it “Model”. In the next steps we’re going to create the data model files for your application, and you will add those files to this group.

This is the way the Project Navigator looks after adding it.

Project Navigator: Model

Here’s how the data model will be organized:

  • ScaryBugData: Contains bug name and rating.
  • ScaryBugDoc: Contains full size image, thumbnail image, ScaryBugData.

The reason you’re setting things up like that is it will make things easier later on in this tutorial when you start saving data to the disk.

A Scary Data Model: Implementation

Note: If you’ve followed the How To Create A Simple iPhone App on iOS 5 Tutorial, you will find that this section is (almost) identical to that. One of the good things about Mac/iOS programming is that they share most of the SDK, obviously, except the UI classes and some OS specific parts.

So, when you’re creating the model and classes that don’t need user interface, you will find that most of your code will likely just work on Mac, or it will work with some minor changes.

For instance, in this case, changing the ScaryBug model classes from iOS to Mac only required one change. UIImage does not exist in OS X, so you just needed to change it to OS X’s image class, NSImage. And that was it (along with updating the language to Swift)!

The first step is to create the model classes, beginning with ScaryBugData.

In the project navigator, Control-Click the Model Group you just created, and in the menu click “New File…”. Select the OS X\Source\Cocoa Class template, and click Next.

smats-newcocoaclass

Name the class ScaryBugData, and enter NSObject for “Subclass of”. Click Next.

smats-createscarybugdata

In the final popup, click Create again. If all went well, your Project Navigator should now look similar to this:

smats-projectnavigator5-ScaryBugData

Open ScaryBugData.swift and replace its contents with the following:

import Foundation
 
class ScaryBugData: NSObject {
  var title: String
  var rating: Double
 
  override init() {
    self.title = String()
    self.rating = 0.0
  }
 
  init(title: String, rating: Double) {
    self.title = title
    self.rating = rating
  }
}

This is pretty simple stuff – you’re just declaring an object with two properties – a string for the name of the bug, and a double for how scary you rated it. In Swift, the String and Double types are both structs, and structs are value types, so explicit memory management attributes (e.g. strong) are not needed.

You also define an initializer for the class, so you can set the title and rating when you create the bug. Again, extremely simple stuff here. You create your initializer to fill in your instance variables from the passed-in parameters. Note there is no need for deinit, since the ScaryBugData properties are value types, and no need to synthesize your properties due to auto-synthesis.

Ok, that’s it for ScaryBugData. Now follow the same steps you did above to create another subclass of NSObject, this time named ScaryBugDoc.

Replace the code in ScaryBugDoc.swift with the following:

import Foundation
import AppKit
 
class ScaryBugDoc: NSObject {
  var data: ScaryBugData
  var thumbImage: NSImage?
  var fullImage: NSImage?
 
  override init() {
    self.data = ScaryBugData()
  }
 
  init(title: String, rating: Double, thumbImage: NSImage?, fullImage: NSImage?) {
    self.data = ScaryBugData(title: title, rating: rating)
    self.thumbImage = thumbImage
    self.fullImage = fullImage
  }
}

Here you are creating some properties and two initializers. Of note here is that thumbImage and fullImage are optional NSImage properties, so unlike normal variables, they don’t require an initial value or an assigned value in the default initializer.

That’s it – your data model is complete! At this point you should compile and run your application to check that everything runs fine. You should expect to see the same empty list, because you haven’t yet connected the data model with the UI.

Now you have a data model, but you don’t have any data. You need to create a list of ScaryBugDoc objects, and you will store the list in an array.

Open MasterViewController.swift and add the following property to the class:

var bugs = [ScaryBugDoc]()

This will be the property that we’ll use to keep track of your list of bugs. Now to fill it with some starter data!

Scary bug pictures and sample data

At this point, MasterViewController is ready to receive a list of Bugs, but then again, you don’t have any data yet.

Before adding the data, you’re going to need some scary pictures! You can download these pictures from the How To Create A Simple iPhone App on iOS 5 Tutorial or find your own favorite scary bugs on the Internet :]

Once you’ve downloaded the files or gotten your own, open Images.xcassets and drag all the bug images from the Finder into the list of images or the right, under the pre-existing AppIcon image:

smats-projectnavigator6-imagesxcassets

Now, let’s finally create the sample data. Open MasterViewController.swift and add the following method to the class:

func setupSampleBugs() {
  let bug1 = ScaryBugDoc(title: "Potato Bug", rating: 4.0,
    thumbImage:NSImage(named: "potatoBugThumb"), fullImage: NSImage(named: "potatoBug"))
  let bug2 = ScaryBugDoc(title: "House Centipede", rating: 3.0,
    thumbImage:NSImage(named: "centipedeThumb"), fullImage: NSImage(named: "centipede"))
  let bug3 = ScaryBugDoc(title: "Wolf Spider", rating: 5.0,
    thumbImage:NSImage(named: "wolfSpiderThumb"), fullImage: NSImage(named: "wolfSpider"))
  let bug4 = ScaryBugDoc(title: "Lady Bug", rating: 1.0,
    thumbImage:NSImage(named: "ladybugThumb"), fullImage: NSImage(named: "ladybug"))
 
  bugs = [bug1, bug2, bug3, bug4]
}

Here you just use the ScaryBugDoc initializer to create four sample bugs, passing in the title, rating, and images for each. You then add them all to the bugs array.

In order to create the sample data, open AppDelegate.swift and find applicationDidFinishLaunching. Add the following above the line with the call to addSubview:

masterViewController.setupSampleBugs()

And finally you have some data! Build and run your app, and make sure all works well without errors.

You still won’t see anything in the user interface, but at this point the view controller has the data it needs, and you’re able to begin the work in the user interface to finally show your Scary Bugs List.

A Different Kind of Bug List

In order to display your bug list, you need to set up the table view to get the list from your model.

In OS X, the table view control is called NSTableView. It’s similar to UITableView in that it displays lists of data, but one major difference is that in NSTableView each row can have multiple columns!

NSTableView has cells for each row. However, there have been some recent changes as to how these work:

  • Before OS X 10.7 Lion, table view cells were a special class derived from the NSCell class. They were not based on views, and it was the responsibility of the developer to handle drawing and even mouse events (gah!)
  • From OS X 10.7 on, there’s a new type of table view: the View-Based table view. This table view works in a very similar way to UITableView. The cells are a special type of view, and working with them is very similar to the way it works in iOS – which is much easier!

In this tutorial you are going to use the new View Based Table View. We’ll cover the basics here, but if you want to learn more about NSTableView, you can read the Table View Programming Guide, which does a great job explaining how table views work.

Open MasterViewController.xib and select your table view. Be aware that the table view is embedded in a clip view, which is embedded in a scroll view, so first time you click it, you will end up selecting the scroll view. Clicking a second time will select the clip view, then a third click will select the actual table view.

Another way to select the table view is to open the “Objects” Panel on the left side of Interface Builder, and select it by clicking the “Table View” object (you may need to expand the “Clip View” object first).

Once you have it selected, the first thing you need to do is make sure that the table view is “View Based”, because older versions of Interface Builder create “Cell Based” table views by default. Xcode 6 and above will likely set the table view to View Based by default.

To check, make sure that you have selected the “Attributes Inspector” tab on the properties panel on the right of the screen. And then, in “Content Mode”, make sure “View Based” is selected; if not, select it. Also, since your list does not need multiple columns, change the Columns property to one.

In order to customize the list a little bit, check the property “Alternating Rows”, which will draw the rows in alternating white/gray colors, and uncheck the “Headers” property. This will remove the heading of the table, because you don’t need it for the tutorial.

After removing the extra column, the remaining column may be narrower than the table view. In order to resize it, just click on the table column (using the Objects panel on the left, or by clicking four times on the table view) then resize it to the table’s full width.

TableView Properties

The next step is to configure the cell view that the table view will use. Your list needs to display the image of the Bug and its name. You need an image and a text field in your cell to show that information. Interface Builder has a preconfigured NSTableCellView that includes an image view and a text field, so you are going to use that one.

In the Object library panel on the bottom left side of the window, locate the “Image & Text Table Cell View”, and drop it on your table view.

Add TableView Cell

After doing that, your table now has two different types of cells. Remove the old cell type (the cell that does not have the gear icon on the left) by clicking on it and pressing the Delete key on your keyboard.

The last step is changing the height of the cell, because now it’s too small to show the bug image. You want to set its height to 32. Select the cell by clicking on it, and then open the “Size Inspector” tab in the Utilities panel on the right side of Xcode window. You can change the height of the cell to 32 in the Height panel. Another way to do it is dragging the bottom border of the cell until you get to the desired height.

After setting the cell height, the image and the text fields are a bit misaligned. To fix that, click on them and move them until they are centered in the cell. Also, set the width of the text field so it takes up the width of the column. You can also resize the image view and play with the text field font to fit it to your needs.

Now the table view design should look like this:

smats-tableviewcellproperties

Now you need to set the column identifier. This is a name you give to every column of the table view, so that when you want to perform some actions or you receive some notification from a column, you are able to identify the column.

This may not be strictly necessary in this tutorial, because you only have one column, but it’s a good practice to do it so that you don’t find any issues when you want to create a multicolumn table view in other projects.

To do that, select the table column (remember, either click the table view four times to select it, or use the Objects panel on the left), then open the “Identity Inspector” tab in the Utilities panel.

There, change the Identifier from “Automatic” to “BugColumn”.

smats-bugcolumn

That’s it for the table UI configuration. Now, you need to connect the table view with the view controller, so that they are aware of the existence of each other.

Like in iOS, the table view has two properties that are used to let the table and its controller communicate: the data source and the delegate. Basically, the data source is the class that will tell the table view what data it needs to show. The delegate is the object that controls how the data is displayed, and also will receive notifications from the table view, like for instance, when a cell is selected.

Usually (but not always) the delegate and the data source are the same object. In this project, the data source and the delegate will be your MasterViewController. Connecting the table view with its data source and delegate can be done programmatically, but for this tutorial you are going to do the connection in Interface Builder.

Select your table view, and in the Utilities Panel choose the “Connections Inspector” (the one with an arrow pointing to the right). There, in the “Outlets” section, you will see the delegate and the data source.

To connect the delegate, click on the circle on the right of the delegate, and drag it to the “File’s Owner” (for MasterViewController), located on the “PlaceHolders” panels on the left side.

smats-connectdelegate

Just with that you told the table view that its delegate is the MasterViewController. When you instantiate your view controller in your app, that connection will be automatically setup for you.

Now, repeat the same procedure for the dataSource outlet. After doing so, you will see both connections pointing to the File’s Owner, like this:

smats-connectdatasource

That’s it, now you’re ready to add the necessary code in your view controller to show your Bug list. Open MasterViewController.swift and paste the following code at the very end of the file:

// MARK: - NSTableViewDataSource
extension MasterViewController: NSTableViewDataSource {
  func numberOfRowsInTableView(aTableView: NSTableView!) -> Int {
    return self.bugs.count
  }
 
  func tableView(tableView: NSTableView!, viewForTableColumn tableColumn: NSTableColumn!, row: Int) -> NSView! {
    // 1
    var cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn.identifier, owner: self) as NSTableCellView
 
    // 2
    if tableColumn.identifier == "BugColumn" {
      // 3
      let bugDoc = self.bugs[row]
      cellView.imageView!.image = bugDoc.thumbImage
      cellView.textField!.stringValue = bugDoc.data.title
      return cellView
    }
 
    return cellView
  }
}
 
// MARK: - NSTableViewDelegate
extension MasterViewController: NSTableViewDelegate {
}

Here, you’re adding two extensions to MasterViewController to declare its protocol conformance to both NSTableViewDelegate and NSTableViewDataSource. In order to show data in a table view, you need to implement at least two data source methods.

The first method numberOfRowsInTableView is called by the OS to ask the data source, “How many rows do I need to show?” You respond by simply giving the count of the list of bugs in your array.

With that method, the table view knows how many rows to display, but still does not know anything about which cells to display in every row, and what information those cells should have.

That is done in tableView(_:viewForTableColumn:row:). This method will be called by the OS for every row and column of the table view, and there you have to create the proper cell, and fill it with the information you need.

Here’s what’s going on in that method, step by step:

  1. The first thing you do is get a cellView by calling makeViewWithIdentifier. This method will create (or reuse) the proper cell for a column based on the identifier you set up in Interface Builder.
  2. Once you have the cell, it’s time to fill it with information. You have to do it differently depending on the column. That’s why you check for the “BugColumn” identifier. If the column identifier is “BugColumn”, then you set the image and text from the ScaryBugDoc. In this case, this table only has one type of column so this check isn’t absolutely needed. However it’s good to know how to handle the multicolumn case, since you might need that for your own apps.
  3. The last step is setting the cell information. Based on the row, you get from your bugs array the proper ScaryBugDoc, and then fill the image with the thumbImage and the text field with the name of the bug (“title”).

That’s all you need to display information in a table view. It’s just a matter of defining the properties and connections in Interface Builder and implementing only two methods in your view controller.

Now, it’s time to build and run the application. If everything went fine, you should see the table view with all the Scary Bugs in your list!

Part 1 app

Where to go from here?

Here’s the current project download with all of the code you’ve developed so far in this tutorial series.

Next in the Series, you will learn how to add a detail view, and how to add/edit/delete bugs from your list. You will also cover how to polish the user interface and how to handle window resizing to make your app look great at any size!

Getting Started With OS X and Swift Tutorial: Part 1/3 is a post from: Ray Wenderlich

The post Getting Started With OS X and Swift Tutorial: Part 1/3 appeared first on Ray Wenderlich.


Getting Started With OS X and Swift Tutorial: Part 2/3

$
0
0
It's time to show these Scary Bugs in a Mac Application! :]

It’s time to show these Scary Bugs in a Mac Application! :]

Update note: This tutorial was updated for Swift by Joe Howard. Original tutorial by Tutorial Team Member Ernesto García.

This tutorial is the second part of a three part series on how to create a simple app with OS X and Swift.

In the first part of the series, you created a Mac application that showed a list of Scary Bugs.

In this second part, you’ll learn how to add a details section to show the complete information about a bug: its name, scariness rating, and a larger (and much more frightening!) picture of the bugs.

You will also lean how to change that information, rate bugs, and even change their pictures!

Let’s get started!

Download some stuff

In order to complete this tutorial, you’ll need some pictures and some code files. So download these extra project resources and uncompress them.

Note: In order to Rate the bugs from “not scary” to “totally scary”, you’re going use an open source rating control component called EDStarRating, which is included in the package.

In this tutorial we’re not going to explain how it’s built, but we’re going to teach you how to use a custom view like this one in your projects. The package also includes an NSImage category that you’ll use to create a thumbnail of the large bug image, and also three types of faces to show the rating.

For more information on EDStarRating, you can check out its github page.

As you did in the first part of the series, you’re going to create some groups to keep things organized.

First, create a new group called “Art”. Drag the three shocked face images into the new group – these are the images you’ll be using for the “stars” in the rating view for some humorous flair. :] Make sure “Copy items if needed” is checked, and that “ScaryBugsMac” is selected in the “Add to targets” section.

Create another group named “Views” in Xcode, then select and drag both EDStarRating.h and EDStarRating.m to that group. Again, make sure “Copy items if needed” is checked, and that “ScaryBugsMac” is selected in the “Add to targets” section. This is the 5-star rating view.

smats-importfile

Click Finish. On the next screen, be sure to select “Yes” when asked “Would you like to configure an Objective-C bridging header?” The bridging header is used by Xcode to reference Objective-C classes from Swift code.

smats.bridingheader

Repeat for NSImage+Extras.h and NSImage+Extras.m, except drag them to a new group named “Helpers”. This is some helper code you’ll need to resize images a bit later on. Again, make sure that all the required checks are selected (Copy items if needed and Add To targets).

Next, open the bridging header file ScaryBugsMac-Bridging-Header.h and add the following import statements:

#import "EDStarRating.h"
#import "NSImage+Extras.h"

Bridging headers and mixing Objective-C and Swift in the same project works just as it does in iOS.

Note: For more on mixing Objective-C and Swift, check out Chapter 8, “Swift and Cocoa” in our book Swift by Tutorials.

This is how your project should look like after adding those files and moving the bridging header to the Supporting Files group:

smats-project-imports

Creating the Details Section

Now it’s time to change the user interface to include the controls that will show the ScaryBugs detail information.

In iOS, a typical Master/Detail application creates two views, and when a row of the table is tapped, the interface changes to a different view to display the required information.

In OS X, the paradigm is different. Since you don’t have the size restrictions of the iPhone screen, you can add the detail information in the same view. You just need to resize it to be a bit bigger. It’s similar to the way iPad’s Master/Detail applications behave.

Open MasterViewController.xib. Select the view, and increase its height and its width. You don’t need to worry now about the exact size. Just big enough to fit in the new controls you’ll be adding to the right.
Something like this:

smats-resizemasterview

The next step is adding the required controls. You need to show the following detail information: the bug’s name, rating and image.

For the Bug Name you are going to use an NSTextField control, which can show and edit text. For the Rating, you will use the EDStarRating control. And for the image, you will use an NSImageView.

Besides that, you will add two labels with titles, just to let the user know the meaning of every field.

To do this, you just need to do what you previously did with the table view. Find the required controls in the Controls Panel at the bottom right of the screen, and drag them to our view.

Drag one Text Field (for the name), two Labels (for the titles), and one Image View (this one is named ‘Image Well’ in the controls panel).

The EDStarRating control is a custom control, so it’s not included in the panel. To use it, you need to find in the controls panel a control named “Custom View”. Drag it onto the main view. Later you will configure it so that it shows the rating.

Now it’s time to arrange the controls. Place them on the empty space on the right side of the view. Vertically, from top to bottom, arrange them this way:

  • First, a Label, which will be used as a title for the name. Below it, place the text field.
  • Below the text field, the second label (which will become the title for the rating).
  • Below that label, place the custom view (that will later become our rating control).
  • And finally, place the image view below the custom view.

Try to arrange them so that the left edges of the controls are aligned. After that, your view should look similar to this:

smats-emptydetail

In the previous step, you added a custom view for the EDStarRating control. You now need to tell Interface Builder that you want the view class to be EDStarRating instead of the base class NSView.

How do you do that? Quite easy. Click on the custom view to select it. In the Utilities panel, switch over to the Identity Inspector by clicking on the third tab.

In that tab, change the name of the Class to EDStarRating.

smats-edstarrating

Now you need to change the text of the labels. Select the first label, and switch over to the Attributes Inspector by clicking on the fourth tab. There, change the Title to “Name”.

Note you can also change the text of a label by double-clicking on it, and changing the text on the label directly.

Repeat that step for the second label, and change its title to “Rating”

The last step is to configure the main window so that the new controls are visible when running the app. Later in the series, you’ll us auto layout to ensure correct view resizing.

Select the top-level view (it’s also called “Custom View” in the document outline) and note its width and height in the Size Inspector (the one with the ruler icon).

ScaryBugsMac-Static-Window-Source

Then switch to MainMenu.xib, select the ScaryBugsMac window, and set the window width and height to the same values in the Size Inspector. Also, select the Minimum Size checkbox.

static_window_sizing_dest

Ok, now it’s time to build and run the application. If everything went well, the app should run and you should see a window similar to this:

smats-app2-1

You can see that all the controls are there, but the Rating control is missing. Don’t worry about it – the control is there, but since you haven’t configured it yet it doesn’t show anything.

The view currently has all the controls you need to show the details. Now you need to add outlets to the view controller so that you can later access those controls from our code.

To do this, return to Xcode and open MasterViewController.xib. Bring up the Assistant Editor (second button under the “Editor” section in the top toolbar), and make sure the assistant is showing MasterViewController.swift.

Select the table view (remember that you may need to click twice or just select the table in the Controls panel on the left side). When you’re sure that the table view and not the scroll view is selected, control-drag from the table view into MasterViewController.swift, right after the opening curly brace of the class:

smats-bugsTableView

A popup will appear allowing you to hook the NSTableView up to an outlet in your class. Name it bugsTableView, make sure the Storage is set to Weak, and click Connect.

smats-bugsTableView2

With that simple action, you’ve added a property to the view controller, and it’s internally hooked up to the table view in Interface Builder.

Now, you need to repeat those steps for the text field and the image view (but not for the labels – the text there isn’t going to change, so you don’t need to add those outlets).

You just need to do the same steps as before. Select the text field, and control-drag from it into MasterViewController.swift. Call the outlet bugTitleView.

Repeat the same action for the image view, and call the outlet bugImageView. And for the last one, the bug rating in the custom view, create the outlet for the rating view and call the property bugRating.

After creating those outlets, you should have the following list in MasterViewController.swift:

@IBOutlet weak var bugsTableView: NSTableView!
@IBOutlet weak var bugTitleView: NSTextField!
@IBOutlet weak var bugImageView: NSImageView!
@IBOutlet weak var bugRating: EDStarRating!

You will notice there is no warning or error for referencing EDStarRating – its import has already been added to the bridging header and so the class is visible to Swift files.

Showing the details

Now it’s time to show some information in those new controls you just added. When the user clicks on any row of our table view, you need to get the selected bug information, and show that information in the detail section.

This involves three steps:

  1. You need to know which row is selected. The table view tells its delegate that a row is selected by calling the method tableViewSelectionDidChange. So you need to implement that method in MasterViewController to receive the notification.
  2. You need to get the selected Scary Bug from the bugs array.
  3. Finally, you will use the selected Scary Bug object to show the information of that bug in the details section.

Open MasterViewController.swift and add the following method to the class:

func selectedBugDoc() -> ScaryBugDoc? {
  let selectedRow = self.bugsTableView.selectedRow;
  if selectedRow >= 0 && selectedRow < self.bugs.count {
    return self.bugs[selectedRow]
  }
  return nil
}

This method first gets the table view’s selected row. After some sanity checks, you just get the ScaryBugDoc at that position by indexing into the bugs array by subscript.

Next, add the following method:

func updateDetailInfo(doc: ScaryBugDoc?) {
  var title = ""
  var image: NSImage?
  var rating = 0.0
 
  if let scaryBugDoc = doc {
    title = scaryBugDoc.data.title
    image = scaryBugDoc.fullImage
    rating = scaryBugDoc.data.rating
  }
 
  self.bugTitleView.stringValue = title
  self.bugImageView.image = image
  self.bugRating.rating = Float(rating)
}

This method is very simple. It just reads the title, the rating and the image from the ScaryBugDoc, and passes that information to the controls. In order to change the Bug title, you set the stringValue property of bugTitleView. To change the image, you set the image property the bugImageView control. Then you set the rating by setting the rating property on the bugRating control.

That’s it! After making these simple additions, you are now able to show the details for the selected bug.

Next, add the following method to the NSTableViewDelegate extension near the end of the file:

func tableViewSelectionDidChange(notification: NSNotification!) {
  let selectedDoc = selectedBugDoc()
  updateDetailInfo(selectedDoc)        
}

All you’re doing here is calling those two helper methods to get the selected bug and then update the display whenever the user selects a new table row.

The final step you need to do is to configure the Rating control. You need to set some properties for it to work the way you need – and you need to do this before the view is first shown on the screen.

New in OS X 10.10 Yosemite is the iOS-style view controller life cycle methods viewWillAppear, viewDidLoad, and friends. The classic point to set up your view from code in OS X is from loadView(), which is backwards-compatible.

You can override this method and add any initial configuration you may need. It’s important to call through to the superclass implementation at the beginning of the method, or the view will not be created!

Add the following method:

override func loadView() {
  super.loadView()
 
  self.bugRating.starImage = NSImage(named: "star.png")
  self.bugRating.starHighlightedImage = NSImage(named: "shockedface2_full.png")
  self.bugRating.starImage = NSImage(named: "shockedface2_empty.png")
 
  self.bugRating.delegate = self
 
  self.bugRating.maxRating = 5
  self.bugRating.horizontalMargin = 12
  self.bugRating.editable = true
  self.bugRating.displayMode = UInt(EDStarRatingDisplayFull)
 
  self.bugRating.rating = Float(0.0)
}

Here, you’re setting up the rating control with its needed defaults: the images to show as the user selects a rating, the delegate, and the view parameters.

You’ll notice an error in Xcode since you haven’t declared the class as implementing the EDStarRatingProtocol yet. Add the extension at the end of MasterViewController.swift to fix the error:

// MARK: - EDStarRatingProtocol
 
extension MasterViewController: EDStarRatingProtocol {
 
}

You’ll implement the EDStarRatingProtocol methods later in the tutorial.

Now, build and run the application. You should see the window with the funny faces in the rating control, because you’ve just configured it successfully.

Also, now if you click on any row, the information and the image of the selected bug appears in the details section!

smats-ladybug1

Awesome, now it’s starting to look like a real app! :]

At this point, you can select a bug in the list and see its details. You can also change the text and the rating in the details section; however, after you make a change, none of the changes are reflected in the bug list and model! To make this app fully functional, you’ll want to be able to add new bugs or delete existing bugs, so this next section covers how to add those editing capabilities to your app!

Adding and deleting bugs

Now it’s time to implement the editing functionalities. First you’ll need to add two buttons: one to add a new row, and another one to delete a selected row.

Open MasterViewController.xib and find “Gradient Button” in the controls panel. Drag two of those buttons below your table view. Resize the table view if needed.

Select one of the buttons, and open the Attributes Inspector (fourth tab in the Utilities Panel). This will be the “Add row” button. For these buttons, you’ll use system images rather than set a title.

In the attributes inspector, locate the Title property, and delete all the text. It should be empty so that the button does not show any text. Now, in the Image property you are going to select the image with a “+” sign.

That image is named “NSAddTemplate”. You can type the name or you can click on the combobox and scroll down until you find it.

After that, repeat the same process with the other button. Delete its title, and set the image property to “NSRemoveTemplate”, which is the image with a “-” sign.

smats-addbutton

After switching the buttons to use the images, you might want to resize them so they’re a bit smaller.

Now you have two buttons, but they don’t do anything, because you haven’t hooked them up with the view controller.

You need to create an action for every button. This is done almost in the same way that you did when you created the properties for the controls, and is just like you would do in iOS.

Again, bring up the Assistant Editor and make sure it’s showing MasterViewController.swift.

Add a new empty extension into MainViewController for button actions, like this:

// MARK: - IBActions
 
extension MasterViewController {
 
}

The action extension is not strictly necessary, but provides a convenient way to help organize your Swift view controller code as you did for each protocol.

Next, select the “Add” button, and control-drag from the button into the new extension in MasterViewController.swift,

smats-addBug1

A popup will appear allowing you to create a new action for that button. Make sure you select Action as the connection type, name the action addBug, and click Connect.

smats-addBug2

After that, a new method addBug(_:) is created in the view controller. Every time the user clicks the Add Bug button, the system will call that method.

Repeat the process for the delete button, and name the action deleteBug.

It’s time to add code in those methods to add and delete the bugs. Open MasterViewController.swift and add the following code inside addBug:

// 1. Create a new ScaryBugDoc object with a default name
let newDoc = ScaryBugDoc(title: "New Bug", rating: 0.0, thumbImage: nil, fullImage: nil)
 
// 2. Add the new bug object to our model (insert into the array)
self.bugs.append(newDoc)
let newRowIndex = self.bugs.count - 1
 
// 3. Insert new row in the table view
self.bugsTableView.insertRowsAtIndexes(NSIndexSet(index: newRowIndex), withAnimation: NSTableViewAnimationOptions.EffectGap)
 
// 4. Select the new bug and scroll to make sure it's visible
self.bugsTableView.selectRowIndexes(NSIndexSet(index: newRowIndex), byExtendingSelection:false)
self.bugsTableView.scrollRowToVisible(newRowIndex)

Let’s see what we’re doing here.

To set up the model side, you first create a new ScaryBugDoc object and add it to your bugs array.

On the view side, you insert a new row in the table view for that bug. After that, the OS will automatically call the method tableView(_:viewForTableColumn:row:) and the cell will be updated with the Bug information.

The last two lines are just cosmetic additions. You select the newly created row and you make the table view scroll to that row so that the newly created row is visible.

And now for the Delete button, paste the following code inside the deleteBug():

// 1. Get selected doc
if let selectedDoc = selectedBugDoc() {
  // 2. Remove the bug from the model
  self.bugs.removeAtIndex(self.bugsTableView.selectedRow)
 
  // 3. Remove the selected row from the table view
  self.bugsTableView.removeRowsAtIndexes(NSIndexSet(index:self.bugsTableView.selectedRow),
    withAnimation: NSTableViewAnimationOptions.SlideRight)
 
  // 4. Clear detail info
  updateDetailInfo(nil)
}

In this method you use optional binding to first call selectedDoc(), which you wrote earlier to get the currently selected bug – ah, the benefits of reusable code!

That method can return nil if there is no selection, so only if there is a selected bug, you remove that bug from your array. In the last line you just update the detail section with a nil value. That will actually clear all the information of the selected bug.

And that’s it – build and run! If everything went fine, now when you click on the Add Bug button, a new line is added.

You can also delete a bug by selecting a row, and clicking the delete button.

smats-app2-2-addBug

Editing bug information

At this point, you can add and remove Scary Bugs from the list. Now is time to edit existing bugs.

You can make three changes to a bug: change its name, change its rating, and change its image.

First, let’s see how you can change the name. When you select a bug, its name is set in the text field located in the details section.

Right now you can change it, but the changes are not stored in the model, so those changes are lost. You need to update the model every time the user changes the text of the selected bug.

In order to achieve that, first you need to know when the text has been changed. You don’t want to receive a notification every time the user changes a character, but when the user has finished editing.

This happens when the user is typing and presses ENTER, or when the user changes from the text field to another control. The text field sends an action when that happens, in the same way that buttons send an action when clicked.

So, the way to implement it is the same. Open MasterViewController.xib, bring up the Assistant Editor, and make sure it’s displaying MasterViewController.swift.

Select the text field, and control-drag from it into MasterViewController.swift right before addBug():

smats-titleChange1

A popup will appear allowing you to create a new action for that text field. Name the action bugTitleDidEndEdit.

smats-titlechange2

That method will be called by the OS when the user finishes editing the text. Back in MasterViewController.swift, add the following helper method to the class:

func reloadSelectedBugRow() {
  let indexSet = NSIndexSet(index: self.bugsTableView.selectedRow)
  let columnSet = NSIndexSet(index: 0)
  self.bugsTableView.reloadDataForRowIndexes(indexSet, columnIndexes: columnSet)
}

This will trigger a reload of a single row to refresh its data. When you change the model, you’ll need to call this method to have the table view row update itself.

Next, find bugTitleDidEndEdit and add the method implementation as follows:

if let selectedDoc = selectedBugDoc() {
  selectedDoc.data.title = self.bugTitleView.stringValue
  reloadSelectedBugRow()
}

First, you get the selected bug by calling selectedBugDoc(). Then, you get the new text from the text field, and change the bug title in the document.

The last step is to change the title in the table view in reloadSelectedBugRow(). For that, you just tell the table view to reload the row for the current bug. This will cause tableView(_:viewForTableColumn:row:) to be called, which will then reload the table view cell appropriately.

Note: It is better to update a cell in by reloading it as shown here rather than trying to directly manipulate the cell’s content outside of tableView(_:viewForTableColumn:row:).

Build and run the application. Now, if you select a bug and edit its name (remember to press enter), the name will be changed in the table view!

If you change the selection, and go back to it, the new text is still there, because now you’ve stored it in the model object.

smats-app2-3-newname

Now it’s time to change the rating. EDStarRating works in a similar way as the table view does. You need to define its delegate, and the OS will call a method to inform us that the rating has changed.

You’ve already configured all that in the loadView method in a previous step, so you just need to add the method that will be called.

Open MasterViewController.swift and add this method inside the EDStarRatingProtocol extension:

func starsSelectionChanged(control: EDStarRating!, rating: Float) {
  if let selectedDoc = selectedBugDoc() {
    selectedDoc.data.rating = Double(self.bugRating.rating)
  }
}

Here you are doing the same as before: you get the selected doc, and update it with the new value.

Build and run the application. Now notice that the rating value is stored in the model and every time you change the rating for a Bug, that value is kept, even if you select other bugs and select the first bug again later.

smats-app2-4-newRating

So, there is only one thing left to do – allow the user to change the bug’s image!

Getting a picture

To get a new picture from the user, you’re going to add a new button. When the user clicks it, you will present a window allowing the user to choose a new image.

Open MasterViewController.xib. Find the “Push Button” control in the object library, and drag it to the view, just below the image view.

Change the button title to “Change Picture”:

smats-changePictureButton

Now, you need to add an action for it. Repeat the same steps you followed for the “Add” and “Delete” buttons, and name the action changePicture.

That action will be called every time you click the button. The next step is to show the user a window to change the bug’s picture.

For that, you are going to use an OS X specific control, called IKPictureTaker. This allows you to choose a picture from your computer or even use the webcam to take a picture, and just with a single line of code. You can learn more about this control in Apple’s ImageKit Programming Guide.

Once the user has selected the picture, the control will notify your view controller that a picture is available via a delegate callback.

Select MasterViewController.swift and add this import at the top of the file:

import Quartz

The image picker is part of the Quartz framework, so you need to import that first.

Now, add this code inside changePicture:

if let selectedDoc = selectedBugDoc() {
  IKPictureTaker().beginPictureTakerSheetForWindow(self.view.window,
    withDelegate: self,
    didEndSelector: "pictureTakerDidEnd:returnCode:contextInfo:",
    contextInfo: nil)
}

In this piece of code, you first check if you have a bug selected. If you find a bug, then you show the picture taker control so that the user can choose a picture.

With this single line of code, ImageKit will show a window to select the new image. In the same method you tell the picture taker control that the view controller (self) is going to be its delegate.

When it’s finished, the picture taker will call the delegate method pictureTakerDidEnd(_:returnCode:contextInfo:). In that method, you will collect the new image, and set it to the bug.

Add this method next:

func pictureTakerDidEnd(picker: IKPictureTaker, returnCode: NSInteger, contextInfo: UnsafePointer<Void>) {
  let image = picker.outputImage()
 
  if image != nil && returnCode == NSOKButton {
    self.bugImageView.image = image
    if let selectedDoc = selectedBugDoc() {
      selectedDoc.fullImage = image
      selectedDoc.thumbImage = image.imageByScalingAndCroppingForSize(CGSize(width: 44, height: 44))
      reloadSelectedBugRow()
    }
  }
}

When this is invoked, it means that the picture taker control has finished its work. But the user may have cancelled the operation, and you won’t have any image available.

For that, you check that the control returned OK (NSOKButton) and that you have a new image available.

You should be familiar by now with the rest of the code. First you get the selected Bug, and then the cell in the table view for the selected row. The only difference here is that in the model and the cell you have to update the image.

Build and run the app. If you select a bug and click Change Picture, you will be able to choose an image on your computer or even capture a new picture with your computer’s camera. That image will be associated to the selected bug.

So, now your application is almost ready. You can see the bug’s details, add or delete bugs, change their names, rate them and even change their pictures!!

smats-app2-5-angrybug

Where to Go from Here?

Here is a sample project with all of the code you’ve developed so far in this tutorial series.

In the Final Part of the series, you’ll learn how to polish the application and take care of the small details that make an app look better and more professional.

Stay tuned, and feel free to join in the discussion in the comments below if you have any questions!

Attribution: Angry Bug Image is licenced under Attribution-Share Alike 3.0 Unported and it was created by the user FoeNyx.

Getting Started With OS X and Swift Tutorial: Part 2/3 is a post from: Ray Wenderlich

The post Getting Started With OS X and Swift Tutorial: Part 2/3 appeared first on Ray Wenderlich.

Getting Started With OS X and Swift Tutorial: Part 3/3

$
0
0
It's time to show these Scary Bugs in a Mac Application! :]

It’s time to show these Scary Bugs in a Mac Application! :]

Update note: This tutorial was updated for Swift by Joe Howard. Original tutorial by Tutorial Team Member Ernesto García.

Welcome back to the third and final part of the How to Create a Simple Mac App tutorial series!

In the first part of the series, you created a Mac app that showed a list of Scary Bugs.

In the second part of the series, you learned how to show the details for the bugs, as well as how to add, delete and modify bugs.

In this third and final part of the tutorial, you’ll wrap up your OS X and Swift introduction by polishing your app and providing a better user experience.

By the time you’re done with this tutorial, you will have created a complete OS X app – and hopefully you’ll be inspired to create some Mac apps of your own! :]

If you didn’t follow along with the previous parts, here’s an archive of the final project from part 2 that you can open in Xcode and follow along from here!

What’s wrong with this app?

Everything works great so far. You can see a bunch of scary bugs, add or delete bugs, and you can even change any information on a bug.

It’s functionally complete. But as is, you are not providing a very good user experience.

For example, if you resize the window and make it very large, look what happens to the poor controls!

smats-app3-1-theproblem

They are not resized, and they are totally misaligned, making the app look very ugly and unprofessional.

It’s even worse if you make your window small:

smats-app3-2-theproblem2

Ack! In this case you cannot even see all the information you need. It’s clear that you need to set a minimum window size to make the app usable.

Another issue is that bug data doesn’t persist between app sessions. Any bug data added or modified by the user of your app is lost every time the app is restarted. You’ll add bug data persistence later on in this part of the series.

So, let’s fix the resizing issues by setting a minimum window size, after first adding a few more UI elements.

Open MasterViewController.xib. Resize the view and arrange the controls in a way that you feel looks good and such that you can see the information you need with the minimum size, perhaps like this:

smats-mastersize

In the sample screenshot, the controls are also arranged so that all the buttons are aligned vertically, and all the controls in the detail view are aligned horizontally and have the same width (except the Change Picture button).

Now let’s add a little detail to make it look better, and to make the separation between the list and the detail section clearer. Select Vertical Line in the Objects Library, and drag it onto the view. Place it between the list and the detail controls, just in the middle of the empty space.

smats-verticalline

That looks much better!

Reset

Also, it would be nice to have a button to reset bug data to the original sample data supplied in the app, after the user has made a few changes. Drag a push button to below the table view, change it’s title to Reset All, and, similar to what you did in Part 2, control-drag from the button to MasterViewController.swift in the Assistant Editor to add an action named resetData:

smats-resetbutton

Add the following code to resetData():

setupSampleBugs()
updateDetailInfo(nil)
bugsTableView.reloadData()

The method here just calls setupSampleBugs() to restore the sample app data. updateDetailInfo with the nil parameter will clear out the details fields, and then you just reload the table view.

Build and run the application, then add, delete or modify some bug data. Then, click the Reset button to ensure it is functioning correctly.

Resizing

After these changes, look in the size inspector and record the size of the main Custom View in MasterViewController.xib. In my case, it was 540×400 pixels, but yours may vary. This is OK, just write down what it is for you.

smats-customviewsize

That’s going to be the minimum size for your application window. Now, open MainMenu.xib, and then select the window of the app. In the size inspector check the Constraint box for Minimum Size and change the width and the height fields to the values you wrote down earlier.

smats-windowminsize

Build and run the application, and try resizing the window:

smats-windowminsize2

You’ll see you can still resize the window, but it won’t get any smaller than the minimum size you defined. With this change, your bug information is always properly visible, w00t!

Now it’s time to handle the resizing, which requires a bit of thought. Your window has two different parts: the table view, and the details section. They should behave differently when you resize the window.

First you need to ensure that the MasterViewController view itself resizes correctly when the app window is resized. Remember, the window and the view controller’s view are two separate things! You’ll do that using the Auto Layout Visual Format Language. Open to AppDelegate.swift, and add the following to the end of applicationDidFinishLaunching():

// 3. Set constraints on masterViewController.view
masterViewController.view.translatesAutoresizingMaskIntoConstraints = false
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:|[subView]|",
  options: NSLayoutFormatOptions(0),
  metrics: nil,
  views: ["subView" : masterViewController.view])
let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|[subView]|",
  options: NSLayoutFormatOptions(0),
  metrics: nil,
  views: ["subView" : masterViewController.view])
 
NSLayoutConstraint.activateConstraints(verticalConstraints + horizontalConstraints)

Adding those constraints programmatically ensures that the MasterViewController view will resize when the window is resized. For both the vertical and horizontal dimensions, the master view controller will be right up against the bounds of the window.

Inside the view, you want the table view to grow vertically when the window is resized, but you want its width to stay constant. However, you want the detail side to expand as the window grows larger. You’ll handle the resizing by using auto layout in interface builder to constrain the views.

Open MasterViewController.xib and select the table view. Click the Pin constraint popup in the bottom right of the Interface Builder editor window, and add top, left, and bottom constraints and a width constraint, being sure to to set the bottom constraint against the custom view in the drop down menu, then tap “Add 4 Constraints”:

smats-constraints-tableview

Note that your constraint values might be different from those pictured here since the exact spacing might be different.

Next, select the Reset button, and add a top constraint to the table view and a left constraint to the main view:

smats-constraints-reset

Next select the separator line that you added, and set a top and bottom constraint to the main view, and a left constraint to the table view, being sure to select the “Bordered Scroll View – Table View” in the left constraint drop down:

smats-constraints-verticalline

Now you need to configure the constraints for the “Add” and “Delete” buttons. You don’t want these buttons to change their size, but you need to keep their distance to the bottom of the table view constant. Add top, left, width, and height constraints for each button individually, for example, for the add button:

smats-constraints-addbutton

Repeat for the delete button.

Build and run the application, and try resizing the window. Great success!

smats-app3-3-masterresize

Now you can resize the window, and the table view grows vertically to fit it. The buttons also change their position to always stay below the table view.

But the details section is not looking good yet. In the details section, you need the controls to resize horizontally when the window’s width increases. The Auto Layout constraints for the detail views can be set in a similar manner to the table view. Add the following constraints to the details views in Interface Builder, in the following order:

  • Top and left constraints for the Name label
  • Top, left, and right constraints for the bugTitleView text field
  • Top and left constraints for the Rating label
  • Top, left, right, and height constraints for the bugRating custom view text field
  • Top, left, bottom, and right constraints for the bugImageView
  • Move the Change Picture button so that its right edge aligns with the right edge of the bugImageView, then add right and bottom constraints to the button

You may notice some auto layout warnings on bugImageView after you set its constraints, but the warnings will be resolved when the constrains are set on the Change Picture button.

After the above list is complete, the constraints on the detail views will look as follows:

smats-constraints-detailviews

Compile and run the application, and try resizing again.

smats-app3-4-resize

If you resize the app, you will see the controls grow to fit the available space, and change their position accordingly. Now it’s looking much better!

You can experiment with the different Scaling settings on the bugImageView. Select the Image Well in Interface Builder, and see what happens when you run the app and resize the window when choosing various options for the scaling attribute, e.g. “Proportionally Up or Down” or “Axes Independently”.

Note: If you feel that the app does not look good when you make it very large, you can also set the maximum size of the window the same way you changed the minimum. Just go to the Size Inspector panel of the main window, and set the maximum size option.

Attention to details

Now the app is working fine, and it’s able to adapt to window size changes. The user interface looks better and more professional than the previous one.

There are still a few small details that can make the user experience a little better. For example – build and run the application, and without selecting anything, click the “Delete” or the “Change Picture” buttons. You can click them, but nothing happens, right?

Since you’re the developer of the app, you know that those buttons don’t do anything when there is no selection. But the user might not know that, so the following situation could occur:

smats-onestar

This is the kind of situation you should avoid by enabling and disabling the button as required so that your users have a better experience. These small details add up, and fixing them will make your app look polished.

Here’s what should happen whenever the selection changes:

  • If a row is selected, you need to enable the “Delete” button, the “Change picture” button, the text field and the rating view.
  • If the table does not have any selection, you just need to disable them, so that the user cannot interact with those controls.

In Interface Builder, you’re going to set the buttons and text field to disabled by default. Open MasterViewController.xib. Select the “Delete” button, and open the Attributes Inspector. Scroll down until you find the property “Enabled” and uncheck it.

smats-deletebutton

Repeat this for the Change Picture button, and the text field.

This way, those controls are disabled by default when the application starts. You’ll need to re-enable them when the user selects a row in the table view.

In order to enable those controls, you need to add outlets in the view controller. Let’s do it first with the Delete button.

Bring up the Assistant Editor and make sure it’s displaying MasterViewController.swift.

Select the “Delete” button, and control-drag from the button into MasterViewController.swift.

smats-deletebutton2

A popup will appear allowing you to hook the NSButton up to a property in your class. Make sure the connection property in that popup is “Outlet”, name it deleteButton, and click Connect.

smats-deletebutton3

Repeat the same operation for the Change Picture button, and name it changePictureButton.

Open MasterViewController.swift, and add the following code in tableViewSelectionDidChange(_:), just below the line updateDetailInfo(selectedDoc):

// Enable/disable buttons based on the selection
let buttonsEnabled = (selectedDoc != nil)
deleteButton.enabled = buttonsEnabled
changePictureButton.enabled = buttonsEnabled
bugRating.editable = buttonsEnabled
bugTitleView.enabled = buttonsEnabled

In this code, you determine if the controls need to be enabled or not based on the user selection. If the selectedDoc is nil, it means that no rows are selected so the controls should be disabled.

Likewise, if the user selects a bug, they should be enabled again.

There is one more step. The rating view is enabled by default, and you also want it to be disabled when the application starts. Since it’s a custom view, it cannot be enabled/disabled in Interface Builder.

You need to disable it programmatically when the application starts. Find loadView() and change the line:

self.bugRating.editable = true

to this:

self.bugRating.editable = false

With this change, you set the control as not editable by default. The control is shown, but the rating cannot be changed unless the user selects a bug.

Build and run the application.

smats-app3-5-disabledbuttons

When the application starts, you can see that the controls are disabled. When you select a bug, they’re all enabled.

And when the row is deselected or deleted, they become disabled, because there are no bugs selected.

Note: You could have also solved this problem by making the all the detail views hidden until you select a bug. It’s up to your personal preference and what works best for your app.

Saving the Bugs

Your app is functioning great, handling window resizing correctly, and giving a consistent user interface to the user. However, if the user adds, removes, or edits bug data in the app and then quits, those changes are lost between sessions, and the user has to start all over again.

In order to resolve this, changes made to the bug data must be persisted to disk. There are a number of techniques to persist data, and we’ll consider a few before settling on an efficient method for this simple app.

One option would be to save data for each bug to a file on disk. When the app starts, all the bug files would be read to create the bug array. Another option would be to store bug data in the cloud, using CloudKit or a third-party API.

If the app were to hold a large amount of bug data, you would need to consider something like Core Data to persist the bug information.

Since this app is relatively simple and only contains data for a small number of bugs, you’ll take a more basic approach. Like iOS, Mac apps have access to NSUserDefaults, so you’ll store the bug data there.

Before doing so, you have to make the bug model classes conform to the NSCoding protocol. Start with ScaryBugData.swift by adding the following extension to the end of the file:

// MARK: - NSCoding
 
extension ScaryBugData: NSCoding {
  func encodeWithCoder(coder: NSCoder) {
    coder.encodeObject(self.title, forKey: "title")
    coder.encodeObject(Double(self.rating), forKey: "rating")
  }
}

Here, you’re setting the protocol conformance and implementing encodeWithCoder, which writes the important data of the object out into a coder object.

You also need a matching initializer. However, you cannot put required initializers in a class extension so add it to the main class definition instead:

required convenience init(coder decoder: NSCoder) {
  self.init()
  self.title = decoder.decodeObjectForKey("title") as String
  self.rating = decoder.decodeObjectForKey("rating") as Double
}

init(coder:) will do the opposite of encodeWithCoder, and read in the object from a coder.

Similarly, add the NSCoding protocol to ScaryBugDoc.swift with the following extension:

// MARK: - NSCoding
extension ScaryBugDoc: NSCoding {
  func encodeWithCoder(coder: NSCoder) {
    coder.encodeObject(self.data, forKey: "data")
    coder.encodeObject(self.thumbImage, forKey: "thumbImage")
    coder.encodeObject(self.fullImage, forKey: "fullImage")
  }
}

Then add the required initializer to the main class definition:

required convenience init(coder decoder: NSCoder) {
  self.init()
  self.data = decoder.decodeObjectForKey("data") as ScaryBugData
  self.thumbImage = decoder.decodeObjectForKey("thumbImage") as NSImage?
  self.fullImage = decoder.decodeObjectForKey("fullImage") as NSImage?
}

So you’ve setup the model objects to be encoded and decoded. Now you need to implement saving them to NSUserDefaults. Start by adding the following helper method to MasterViewController.swift:

func saveBugs() {
  let data = NSKeyedArchiver.archivedDataWithRootObject(self.bugs)
  NSUserDefaults.standardUserDefaults().setObject(data, forKey: "bugs")
  NSUserDefaults.standardUserDefaults().synchronize()
}

This method creates an NSData object from the bugs array and then saves that object into NSUserDefaults. NSKeyedArchiver can handle the model objects in the bugs array, since they conform to NSCoding.

Switch to AppDelegate.swift, and add the following to applicationWillTerminate():

masterViewController.saveBugs()

Prior to the application terminating, MasterViewController will save the bug array to NSUserDefaults.

Loading the Bugs

Now that you have a means of saving bug data, you must read the data in when the app starts. Still in AppDelegate.swift, find applicationDidFinishLaunching and the following line of code inside it:

masterViewController.setupSampleBugs()

Replace that line with the following:

if let data = NSUserDefaults.standardUserDefaults().objectForKey("bugs") as? NSData {
  masterViewController.bugs = NSKeyedUnarchiver.unarchiveObjectWithData(data) as [ScaryBugDoc]
} else {
  masterViewController.setupSampleBugs()
}

You first check to see if bug data exists in NSUserDefaults. If so, even if it’s an empty array (hey, maybe the user hates bugs!) the restore that data into the bugs array. Otherwise, load the the sample bug data instead.

Build and run the application, add/edit/delete bugs, and then quit the app using Cmd-Q. Restart the app and you’ll see that all the changes to bugs have been saved across restarts! :]

Note: If you doesn’t quit the app gracefully, then the call to saveBugs() may not happen — you’ll need to hit Command-Q rather than kill the app from Xcode. To counteract this problem, you can add more tactical calls to saveBugs() in other spots in MasterViewController – say whenever there’s a new bug or a change to an existing one.

Where To Go From Here?

Here is the final project with all of the code you’ve developed in this tutorial series.

At this point I’d recommend reading Apple’s Mac App Programming Guide, or having a look at some of the samples provided by Apple.

You can also try to add different kinds of controls or new functionality to the application. For instance, how about writing the model to a file, and asking the user where to store it using a file save panel? Or maybe add the ability to search a bug in your list, using a search field control?

I hope you enjoyed making this Mac version of the ScaryBugs app using Swift! If you have any questions or comments or would like to see more Mac tutorials on this site in the future, please join the forum discussion below!

Getting Started With OS X and Swift Tutorial: Part 3/3 is a post from: Ray Wenderlich

The post Getting Started With OS X and Swift Tutorial: Part 3/3 appeared first on Ray Wenderlich.

RWDevCon: Inspiration Talks Announced!

$
0
0

UpdatedSchedule

As you probably know, the team and I are running an iOS conference next February focused on hands-on, high quality tutorials: RWDevCon.

What you might not know is in the afternoon, after a hard day’s work on tutorials, we’re going to switch things over to a more relaxed pace and cover what we call “inspiration talks.”

Inspiration talks are short 18-minute non-technical talks with the goal of giving you a new idea, some battle-won advice, and leaving you excited and energized.

I’ve been reviewing the speaker’s inspiration talk text in the past few weeks, and they’ve come up with some amazing material so far!

Today, we are happy to announce the topics and descriptions for all of the inspiration talks in the conference. You can find them on the official conference schedule here:

At this point there’s just 3 weeks until the conference – we can’t wait to see some of you in DC soon!

RWDevCon: Inspiration Talks Announced! is a post from: Ray Wenderlich

The post RWDevCon: Inspiration Talks Announced! appeared first on Ray Wenderlich.

Video Tutorial: Swift Scroll View School Part 15: Pull to Refresh I

Introduction to OS X Tutorial: Core Controls and Swift Part 1/2

$
0
0
Get started with OS X core controls!

Get started with OS X core controls!

Update note: This tutorial was updated for OS X 10.10 Yosemite and Swift by Michael Briscoe. Original post by tutorial team member Ernesto García.

If you’re an iOS developer and you’re interested in learning about Mac development, you’re in luck – with your iOS skills, you’ll find it quite easy to learn!

Many of the Cocoa classes and design patterns you know and love like strings, dictionaries and delegates have direct equivalents in Mac development. You’ll feel right at home!

However, one big difference with Mac development are there are different controls. Gone are UIButton and UITextField – instead there are similar (but slightly different) Mac variants.

This tutorial will introduce you to some of the more common user interface controls of OS X — the foundation upon which most Mac apps are built. You’ll learn about these controls, as well as the methods and properties you’ll need to understand in order to get up and running as a developer! :]

In this tutorial, you’ll be creating a simple Mac application like the popular game Mad Libs. Mad Libs is a word game where you can insert different words in a block of text in order to create a story — which often has hilarious results!

Once you’ve completed both parts of this tutorial, you’ll have a fundamental understanding of the following controls:

  • Labels and Text Fields
  • Combo Boxes
  • Popup Buttons
  • Text Views
  • Sliders
  • Date Pickers
  • Buttons
  • Radio Buttons
  • Check Buttons
  • Image Views

Before you go through this tutorial, you should go through our How to Make a Simple Mac App Tutorial to learn the basics of Mac development.

The best way to learn any new programming platform is to dive right in and get started — so without further ado, here’s your introduction to Core Controls in Mac OS X! :]

Getting Started

Launch the latest version of Xcode 6, and choose File\New\Project. When the Choose a template dialog appears, select OS X\Application\Cocoa Application, which is the template you use to create an app with a GUI on the Mac. Then click Next.

New OSX Application

In the project options dialog, enter MadLibs as the product name. Also enter a unique company identifier and make sure to choose Swift as the language:

New Project Settings

Click Next. Finally, choose a location where you’d like to save your new project, and click Create.

Once Xcode has finished generating the project files, you’ll be presented with the main Xcode window. Go ahead and click Run in the toolbar. If all goes well, you should see the following:

You’ve built a working application — without any coding at all. The window is empty right now, but you’re going to fill it up with some controls and make it look amazing! :]

Click on MainMenu.xib in the project explorer, and you’ll see a window named “MadLibs” which already contains a view. You’re going to create a view controller that will be the owner of this view.

From the File menu choose New\File, or alternately you can use the keyboard shortcut Command + N. When the Choose a template dialog appears, select OS X\Source\Cocoa Class, as shown below:

Cocoa Class

Click Next to move on to the the file options dialog. Enter RootViewController as the Class and make it a subclass of NSViewController. Make sure Also create XIB file for user interface is NOT checked, and Swift is the language, then click Next.

Create rootViewcontroller

Finally, click Create to save the new view controller.

Note: the reason that you did not check Also create XIB file for user interface is because this view controller is going to be responsible for the Main Window’s view, which is already made for you in MainWindow.xib.

Now you need to create an instance of your new view controller and associate the Main Window’s view with the view controller. Here, you have two choices — you can do it directly in code, or you can do it the quick and easy way: by using the Interface Builder within Xcode! :]

Note: If you would like to learn how to create an instance of a view controller in code, please have a look at the How to Make a Simple Mac App tutorial.

Select MainMenu.xib in the project explorer to open it in Interface Builder. In the lower right-hand corner of the window, you’ll find the the Object Library palette. In the Object Library palette, find View Controller and drag it to the Objects palette on the left-hand side of the window, like so:

Drag ViewController

By default, this view controller object is an instance of NSViewController, but you’ll need this to be an instance of your RootViewController class instead — you will change that right now.

To do this, click on the newly created view controller object, go to the utilities palette and then select the identity inspector tab. Rename the class to RootViewController, as in the screenshot below:

RootviewController Classname

In order to make your newly created view controller the owner of the window’s content view, you’ll need to connect its view outlet to the main window’s content view.

To do that, select the main window in the Objects palette to display it on the screen, and then select your view controller object. Then, go back to the utilities palette and select the connections inspector tab. From the Outlets section, click and drag the view outlet to the main window. This sequence of events is illustrated in the screenshot below:

Connect rootViewcontroller

And that’s it! You’ve now configured your app to instantiate an instance of your RootViewController class, and you’ve made it the owner of the main window’s content view. And you still haven’t written a single line of code! :]

If you’ve followed the above instructions, then everything should be hooked up properly. But before you go any further, take a minute to ensure that everything’s working by following the steps below! :]

You’re going to override awakeFromNib in your RootViewController class, since this method is part of an object’s life cycle when it’s instantiated from a XIB file. If everything has been connected correctly, then this method will be called when the application runs, and the view controller should have a view associated with it.

Open RootViewController.swift and add the following:

override func awakeFromNib() {
  println("View controller instance with view: \(self.view)")
}

This will simply output some debug code to the console to indicate that (a) this code is called, and (b) that the associated view is valid.

Buld and run your app. In the Output pane of the Debug area, you should a message similar to the following:

View controller instance with view: <NSView: 0x60000012cd00>

If you see the line above (the NSView hex address will be different), then you’ve verified that both the view and the view controller have been instantiated, and the two are connected properly!

Now that the basic framework has been laid down, you can now move on to the main focus of this tutorial — adding controls to your app! :]

Each of the remaining steps in this tutorial will focus on a single, different control. You’ll learn the basics of each control, and then implement each one in the Mad Libs app to try it out.

NSControl – The Building Block of Controls

NSControl is the foundation upon which all other controls are built. NSControl provides three features which are pretty fundamental for user interfaces: drawing controls on the screen, responding to user events, and sending action messages.

As NSControl is an abstract superclass, it’s entirely possible that you’ll never need to use it directly within your own apps. All of the common controls are descendants of NSControl, and therefore inherit the properties and methods defined in NSControl.

The most common methods used for a control are getting and setting the value of that control, as well as enabling or disabling the control itself. Have a look at the details behind these methods below:

Setting The Control’s Value

If you need to display information using a control, you’ll usually do this by changing the control’s value. Depending on your needs, the value can be a string, a number or even an object. In most circumstances you’ll use a value which matches the type of information being displayed, but NSControl allows you to go beyond this and set several different value types!

The methods for getting and setting a control’s value are:

// getting & setting a string
let myString = myControl.stringValue
myControl.stringValue = myString
 
// getting & setting an integer
let myInteger = myControl.integerValue
myControl.integerValue = myInteger
 
// getting & setting a float
let myFloat = myControl.floatValue
myControl.floatValue = myFloat
 
// getting & setting a double
let myDouble = myControl.doubleValue
myControl.doubleValue = myDouble
 
// getting & setting an object
let myObject: AnyObject! = myControl.objectValue
myControl.objectValue = myObject

You can see how the different setters and getters fit with the type-safety of Swift.

Enabling & Disabling a Control

Enabling or disabling a control based on the current state of an app is a very common UI task. When a control is disabled, it will not respond to mouse and keyboard events, and will usually update its graphical representation to provide some visual cues that it is disabled, such as drawing itself in a lighter “greyed out” color.

The methods for enabling and disabling a control are:

// disable a control
myControl.enabled = false
 
// enable a control
myControl.enabled = true
 
// get a control's enabled state
let isEnabled = myControl.enabled

Okay, that seems pretty easy — and the great thing is that these methods are common to all controls; they’ll all work the same way for any control you use in your UI.

Now it’s time to take a look at the more common user interface controls of OS X! :]

Field of Dreams – NSTextField

One of the most common controls in any UI is a field that can be used to display or edit text. Almost any modern application will need to display some sort of text, such as a RSS reader, a Twitter client, or an address book, and almost all applications require some sort of text enty to log in, change settings, or to provide other inputs to the app.

The control responsible for this functionality in OS X is NSTextField.

NSTextField is used for both displaying and editing text. You’ll notice this is quite different than iOS, where UILabel is used to display text, and UITextField to edit it. In OS X, these two controls are combined into one, and the behaviour of the control is modified via setting the control’s properties.

If you want a text field to be a label, you simply set it as non-editable. To make the text field editable — yup, you simply set the control to be editable! These properties can either be modified programmatically or from Interface Builder.

To make your coding life just a little easier, Interface Builder actually provides several pre-configured controls for displaying and editing text which are all based on NSTextField. These pre-configured controls can be found in the Object Library, and are described in the graphic below:

NSTextField objects

So now that you’ve learned about the function of NSTextField, you can now add one to your Mad Libs application! :]

Living in the Past — A Past Tense Verb

In the Mad Libs application you’ll be building, you will add various UI controls which will allow the user to blindly construct a funny sentence, without knowing the final result. Once the user has finished, your app will combine all the different parts and display the result, hopefully with some comedic value. The more creative the user is, the more fun they’ll have! :]

The first control you’ll add is a text field where the user can enter a verb to be added to the sentence, as well as a label to inform the user what the text field is for.

Select MainMenu.xib in the project explorer to open it in Interface Builder, and then select the Main Window. In the Object Library palette, locate the Label control and drag it onto the window. Double-click the label to edit the default text, and change it to Past Tense Verb:.

Next, locate the Text Field control and drag it onto the window, placing it to the right of the label.

You should now have something similar to this:

Added PastTense textfield

Later on, you’re going to need to interact with the text field, and to do this you’ll need an outlet on the view controller.

To create this property and connect it so that you can work with it, open the assistant editor — making sure that RootViewController.swift is selected — and Ctrl-Drag the text field to the class interface to create a new property, like shown below:

drag verb

In the popup window that appears, name the Outlet pastTenseVerbTextField, and click Connect, as shown in the image below:

pasttense popup

And that’s it! You now have an NSTextField property in your view controller that is connected to the text field in the main window.

You know, it would be great to display some sample default text when the app launches, in order to give the user an idea of what to put in the field. Since everyone loves to eat, and food related Mad Libs are always the most entertaining, the word ate would be a tasty choice here! :]

A good place to put this is inside awakeFromNib. To change the text field’s default value, simply set the stringValue property you learned about earlier.

Add the following code to the end of awakeFromNib:

// Set the default text for the pastTenseVerbTextField property
pastTenseVerbTextField.stringValue = "ate"

Build and run your app. You should see the two controls you’ve just added, and the text field should display the word ate, as below:

Okay, that takes care of a single input with a default value. But what if you want to provide a list of values that the user can select from?

Combo Boxes to the rescue! :]

The Value Combo – NSComboBox

A combo box is interesting — and quite handy — as it allows the user to choose one value from an array of options, as well as enter their own text.

It looks similar to a text field in which the user can type freely, but it also contains a button that allows the user to display a list of selectable items. You can find a solid example of this in OS X’s Date & Time preferences panel:

Here, the user can select from a predefined list, or enter their own server name, if they wish.

The control responsible for this in OS X is NSComboBox.

NSComboBox has two distinct components: the text field where the user can type, and the list of options which appear when the embedded button is clicked. You’re able to control the data in both parts separately.

To get or set the value in the text field, simply use the stringValue property covered earlier. Hooray for keeping things simple and consistent! :]

Providing options for the list is a little more involved, but still relatively straightforward. You can call methods directly on the control to add elements in a manner similar to NSMutableArray, or you can use a data source — anyone familiar with iOS programming and UITableViewDataSource will feel right at home!

Method 1 – Calling Methods Directly On The Control

NSComboBox contains an internal list of items, and exposes several methods that allow you to manipulate this list, as follows:

// Add an object to the list
myComboBox.addItemWithObjectValue(anObject)
 
// Add an array of objects to the list
myComboBox.addItemsWithObjectValues([objectOne, objectTwo, objectThree])
 
// Remove all objects from the list
myComboBox.removeAllItems()
 
// Remove an object from the list at a specific index
myComboBox.removeItemAtIndex(2)
 
// Get the index of the currently selected object
let selectedIndex = myComboBox.indexOfSelectedItem
 
// Select an object at a specific index
myComboBox.selectItemAtIndex(1)

That’s relatively straightforward, but what if you don’t want your options hardcoded in the app — such as a dynamic list that is stored outside of the app? That’s when using a datasource comes in really handy! :]

Method 2 – Using A Data Source

When using a data source to populate the combo box, the control will query the data source for the items it needs to display as well, as any necessary metadata, such as the number of items in the list.

To use a data source, you’ll need to implement the NSComboBoxDataSource protocol in your class. From there, it’s a two-step process to configure the combo box to use the data source.

First, set the control’s dataSource property, passing an instance of the class implementing the protocol; usually “self”. Then set the control’s usesDataSource property to “true”.

myComboBox.dataSource = self
myComboBox.usesDataSource = true

In order to conform to the protocol, you’ll need to implement the following two methods:

// Returns the number of items that the data source manages for the combo box
func numberOfItemsInComboBox(aComboBox: NSComboBox!) -> Int {
  // anArray is an NSArray variable containing the objects
  return anArray.count
}
 
// Returns the object that corresponds to the item at the specified index in the combo box
func comboBox(aComboBox: NSComboBox!, objectValueForItemAtIndex index: Int) -> AnyObject! {
  return anArray[index]
}

Finally, whenever you need to reload the data in a combo box which is using a data source, simply call reloadData().

Which Method To Use?

If your list of items is relatively small and you don’t expect it to change that often, adding items once to the internal list is probably the best choice. But if your list of items is large or dynamic, it can often be more efficient to handle it yourself using a data source. For this tutorial you’ll be using method 1.

Now that you’ve covered the fundamentals of the combo box, move on to implement one in your app! :]

The Singles Bar — A Singular Noun

In this section you’ll add a combo box to your Mad Libs application, to allow the user to enter a singular noun. They can either choose from a list or enter their own.

To do this, first add a Label to the app so that the user knows what the control is for. Select the MainMenu.xib in the project explorer to open it in Interface Builder, and then select the Main Window.

In the Object Library palette, locate the Label control and drag it onto the window. Double-click it to edit it’s default text, changing it to Singular Noun:.

Next, add the combo box that will provide the users with the list of choices. Locate the Combo Box control and drag it onto the window, placing it to the right of the label.

Your window should now look like this:

Added combobox

Now you need to add an NSComboBox outlet to the view controller so you can populate the default items, and later access it via code to read the user’s selection.

To do this, use the same technique you used for the text field: select the assistant editor (making sure RootViewController.swift is selected) and Ctrl-Drag the combo box to the RootViewController.swift to create a new property, just like in the screenshot below:

Combo dragged

In the popup window that appears, name the property singularNounCombo.

Combo popup

Now the NSComboBox property is connected to the combo box control! Now, if you only had some data to populate the list! :] Time to add some.

Open RootViewController.swift in the code editor and add the following property declaration directly below the singularNounCombo property:

var singularNouns: [String]!

Inside awakeFromNib, add this to the bottom:

// Setup the combo box with singular nouns
singularNouns = ["dog", "muppet", "ninja", "fat dude"]
singularNounCombo.removeAllItems()
singularNounCombo.addItemsWithObjectValues(singularNouns)
singularNounCombo.selectItemAtIndex(2)

The first line initializes an array with four strings. The second line removes any items added by default. Next items are added to the combo box using one of the methods covered earlier. Finally, the combo box is asked to select the item at index 2 so that “ninja” is the default value.

Build and run the application to see your combo box in action!

Great — it looks as though everything is working just right. If you click on the combo box, you can then view and select any of the other items.

Now what if you want to present the user with a list of choices, but not allow them to enter their own text? Read on — there’s a control for that as well! :]

Pop Goes the Weasel — NSPopupButton

The pop up button allows the user to choose from an array of options, but without giving the user the option of entering their own value in the control. The control responsible for this in OS X is NSPopupButton.

Pop up buttons are incredibly common in OS X, and you can find them in almost every application — including the one that you’re using right now: Xcode! :] You’re using the pop up button to set many of the properties on the controls you’re using in this tutorial, as in the screenshot below:

Filling the Spaces — Adding Items To Pop Up Buttons

As you might expect, adding items to NSPopUpButton is similar to adding items to NSComboBox — except that NSPopUpButton doesn’t support using a data source for the content of the control. NSPopUpButton maintains an internal list of items and exposes several methods allowing you to manipulate it:

// Add an item to the list
myPopUpbutton.addItemWithTitle("Pop up buttons rock")
 
// Add an array of items to the list
myPopUpbutton.addItemsWithTitles(["Item 1", "Item 2", "Item 3"])
 
// Remove all items from the list
myPopUpbutton.removeAllItems()
 
// Get the index of the currently selected item
let selectedIndex = myPopUpbutton.indexOfSelectedItem
 
// Select an item at a specific index
myPopUpbutton.selectItemAtIndex(1)

Pretty straightforward, isn’t it? That’s the beauty of the core controls — there are a lot of similarities between them in terms of the methods used to manipulate the controls.

Time to implement a pop up button in your app! :]

The More the Merrier — A Plural Noun

You’ll now add a pop up button to your Mad Libs application which will allow the user to choose between different plural nouns to populate your comical sentence.

Select MainMenu.xib in the project explorer to open it in Interface Builder, and then select the Main Window. In the Object Library palette, locate the Label control and drag it onto the window just beneath the Singular Noun label.

Note: Alternatively, a shortcut is to hold down Option and drag an existing label to duplicate it. This is handy so you can keep the same size and properties of an existing label.

Double-click the control to edit its default text, and change it to Plural Noun:. Next, locate the Pop Up Button control and drag it onto the window, placing it to the right of the label.

Your window should now look like this:

Added popup

Now you need to add an NSPopUpButton outlet to the view controller. You’ll use the same technique as before, which should be fairly familiar by now: open the assistant editor, make sure RootViewController.swift is selected, and then Ctrl-Drag the pop up button to the RootViewController.swift to create a new property. This is illustrated in the screenshot below:

Drag popup

In the popup window that appears, name the property pluralNounPopup:

popup-popup

Okay, now the outlet is connected to the pop up button control. Now you just need some data to populate it!

Open RootViewController.swift in the code editor and add the following property declaration directly below the pluralNounPopup property.

var pluralNouns: [String]!

Add the following code to the bottom of awakeFromNib:

// Setup the pop up button with plural nouns
pluralNouns = ["tacos", "rainbows", "iPhones", "gold coins"]
pluralNounPopup.removeAllItems()
pluralNounPopup.addItemsWithTitles(pluralNouns)
pluralNounPopup.selectItemAtIndex(0)

The first line in the code above initializes an array with four strings. The second line removes any existing items from the pop up button. The third line adds the array of items to the pop up button using some of the methods covered earlier. Finally, the pop up button is instructed to select the first item in the list as the default.

Build and run the application to see the result:

Once the app has launched, note that the pop up button shows the initial item, tacos, and if you click on the pop up button, you’ll see all the other items in the list.

Okay, so you now have two controls that allow the user to select from lists, as well as a control that allows the user to enter a single line of text. But what if you need the user to enter more than a few words in a text field?

Read on to learn about text views! :]

Text is Next – NSTextView

Text views, unlike text fields, are usually the control of choice for displaying rich text. Some implementations even allow for more advanced features such as displaying inline images.

The control responsible for this on OS X is NSTextView.

A great example of an application using all of what NSTextView has to offer is TextEdit, as shown in the screenshot below:

NSTextView is so feature rich that to cover everything would warrant a tutorial of its own, so here you’ll just see a few of the basic features in order to get you up and running! (Did you just breathe a sigh of relief?) :]

Here are the basic methods you’ll need to work with text views:

// Get the text from a text view
let text = myTextView.string
 
// Set the text of a text view
myTextView.string = "Text views rock too!"
 
// Set the background color of a text view
myTextView.backgroundColor = NSColor.whiteColor()
 
// Set the text color of a text view
myTextView.textColor = NSColor.blackColor()

Relatively simple — nothing too shocking here! :]

The String’s the Thing — Attributed Strings

NSTextView has built-in support for NSAttributedString. If you pass an attributed string to a text view, the string will be displayed correctly using all the appropriate attributes such as font, font size, and font color.

Note: An attributed string is a special type of string where you can tag subsets of the string with different attributes – such as its font, it’s color, whether it’s bolded, and so on. To learn all about attributed strings, check out the Attributed Strings chapter in iOS 6 by Tutorials – most of the information in that chapter applies to Mac development as well.

NSTextView has a property called textStorage, whose type is NSTextStorage. NSTextStorage is a semi-concrete subclass of NSMutableAttributedString, which is a fancy way of saying “you can put formatted text in it, and change it later.”

In order to have NSTextView display an attributed string, you actually set the attributed string on the NSTextStorage instance returned by the textStorage property, as shown in the code below:

// Setting an attributed string on the NSTextStorage instance owned by our NSTextView
var myAttributedstring = NSMutableAttributedString(string: "Red Green")
myAttributedstring.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: NSMakeRange(0, 3))
myAttributedstring.addAttribute(NSForegroundColorAttributeName, value: NSColor.greenColor(), range: NSMakeRange(4, 5))
 
myTextView.textStorage.setAttributedString(myAttributedstring)

So that’s how to set the text view’s (attributed) string!

The Phrase that Pays – Adding a Text View

Looks like you have everything you need in order to add a text view to your Mad Libs application! This text view will allow the user to enter a multi-word phrase that will be used in the final rendered Mad Lib.

To add the text view to your application, open MainMenu.xib and then select the Main Window. In the Object Library palette, locate the Label control and drag it onto the window just beneath the Plural Noun label (or duplicate an existing label, as mentioned earlier).

Again, double-click it to edit the default text, changing it to Phrase:. Next, locate the Text View control and drag it onto the window, placing it beside the the label you just created.

Your window should now look like this:

Added textview

Now you need to add an NSTextView outlet to the view controller. Again, the same familiar technique is similar to before, but there’s a twist.

Important: Since text views are contained inside scroll views, it’s important you make sure you’ve actually selected the text view before creating the outlet. To do so, simply click twice on the text view or select it from the Object navigator on the left. You can check the text view has been selected by inspecting the Type in the popup window.

Once you’re sure you have the text view selected, as before select the assistant editor, make sure RootViewController.swift is selected, and Ctrl-Drag the text view to the RootViewController.swift. The result is shown below:

Drag text

In the popup window, name the property phraseTextView, as shown here:

Popup textview

Now that the NSTextView property is connected to the text view control, you can set a default text phrase in the control to again give the user an idea of what to enter here.

Add the following code to the end of awakeFromNib:

// Setup the default text to display in the text view
phraseTextView.string = "Me coding Mac Apps!!!"

Build and run the application to see the result! :]

Superb! The Mad Libs application is really starting to take shape now! :]

Pushing Your Buttons — NSButton

Buttons are controls designed to send a message to an app whenever they’re clicked by a user. An app associates an action with the button which is executed whenever the button is clicked.

The control responsible for this on OS X is NSButton.

On OS X there are many different styles of buttons which are viewable Interface Builder’s Object Library. They all work in much the same way, the only difference being their visual representation. The different types of Buttons are shown in the image below:

Variants of NSButton in Mac OS X

You should use the style of button that best suits your application’s design — refer to the OS X Human Interface Guidelines for advice and guidance on best design practices for your app.

Typically, when working with a button, you’ll simply need to associate an action with the button and set its title. However, there may be times when you need to disable the button, or change its appearance. The following methods allow you to perform these actions on your button:

// disable a button
myButton.enabled = false
 
// enable a button
myButton.enabled = true
 
// getting & setting a button's title
let theTitle = myButton.title
myButton.title = theTitle
 
// getting & setting a button's image
let theImage = myButton.image
myButton.image = theImage

Looks fairly simple — adding a button to your app in the next section should be a breeze!

Buttoning Things Down — Adding a Button

Find the Push Button in the Object Library palette and drag it onto the window, changing its title to Go!, like so:

Button added

Since you’re not going to manipulate the button in any way beyond letting the user click it, you don’t need to create an outlet for the button. However, you do need to create an action and associate it with the button, so that your app knows what to do when the button is pushed! :]

Just as before, Ctrl+Drag the button to the RootViewController.swift file and release just above awakeFromNib, as in the screenshot below:

Button dragged

In the popup window that appears, make sure that the connection is set to Action. Name the action goButton.

Button popup

The goButton method will be invoked by the app whenever the user clicks on the button. For now you’ll add some debug code — just to make sure everything’s working!

Add the following code to goButton::

// Past tense verb
let pastTenseVerb = pastTenseVerbTextField.stringValue
 
// Singular noun
let singularNoun = singularNounCombo.stringValue
 
// Plural noun
let pluralNoun = pluralNouns[pluralNounPopup.indexOfSelectedItem]
 
// Phrase
let phrase = phraseTextView.string ?? ""
 
// Create the mad lib sentence
println("A \(singularNoun) \(pastTenseVerb) \(pluralNoun) and said, \(phrase)!")

Note that for the text view, the string property is actually an optional so it could be nil. To guard against that case, you’re using the nil coalescing operator ?? so if string is nil, you’ll get the empty string "" instead.

That’s all the code you need for now — build and run your app! :] It should look like the screenshot below:

Button added 1

Every time you click the button, you should see a short and silly sentence appear in Xcode’s console.

Where to Go From Here?

Here’s the example project containing all the source code from this tutorial up to this point.

In the second part of this tutorial, you’ll learn about more of the core controls in OS X, including sliders, date pickers, radio buttons, check boxes and image views — each of which will be added to your Mad Libs application in order to complete it!

In the meantime, if you have any questions or comments about what you’ve done so far, join in the forum discussion below!

Introduction to OS X Tutorial: Core Controls and Swift Part 1/2 is a post from: Ray Wenderlich

The post Introduction to OS X Tutorial: Core Controls and Swift Part 1/2 appeared first on Ray Wenderlich.

Introduction to OS X Tutorial: Core Controls and Swift Part 2/2

$
0
0
More OS X core controls!

More OS X core controls!

Welcome back to the second and final part of the Core Controls in Mac OS X tutorial!

In the first part of this tutorial, you started building a Mad Libs style Mac OS X application, where the user enters various words and phrases in order to create a funny sentence.

Along the way, you learned about and implemented some of the core UI controls that are used in OS X applications — namely, Text Fields, Combo Boxes, Pop Up Buttons, Push Buttons and Text Views.

In this final part of the tutorial, you’ll finish off your application, and learn how to use the following controls:

  • Sliders
  • Date Pickers
  • Radio Buttons
  • Check Boxes
  • Image Views

After finishing this two-part tutorial you’ll have a solid understanding of the core controls available on OS X, and you’ll be able to go forth and build some amazing apps for the Mac! :]

This tutorial will pick up where we left off last time – if you don’t have it already, here’s the project where we left things off.

Without further ado, it’s time to get to work!

Slipping and Sliding — NSSlider

A slider is a control that lets the user choose from a predetermined range of values. A slider has a minimum and a maximum value, and by moving the control’s knob, the user can choose a value between those two limits. Sliders can be either linear or radial. What’s the difference between the two, you ask?

Linear sliders can be either vertical or horizontal, and they let you choose a value by moving the knob along the track. A really great example of linear sliders is in OS X’s Mouse preferences panel, as shown in the screenshot below:

Radial sliders are a little different — they are displayed as a small circle with a knob, which can be rotated a full 360 degrees. In order to select a value, you click and drag the knob to the required position. You can find a great example of radial sliders in Adobe Photoshop, where they’re used to define the angle of a gradient, as such:

The control responsible for this on OS X is an NSSlider.

All four types of sliders (horizontal, vertical, radial and slider cell) are in fact the same control, NSSlider. The only difference is how they’re displayed. Interface Builder has an object in the Object Library for each of the four types, as shown below:

sliders-ib

Slider Semantics

There are two common tasks you’re likely to perform when working with sliders: getting or setting the current value, and getting and setting the high and low limits of the slider’s range. These properties are outlined here:

// getting & setting an integer value
let theInteger = mySlider.integerValue
mySlider.integerValue = theInteger
 
// getting & setting a float value
let theFloat = mySlider.floatValue
mySlider.floatValue = theFloat
 
// getting & setting a double value
let theDouble = mySlider.doubleValue
mySlider.doubleValue = theDouble
 
// getting & setting the minimum value of the range
let theMinimumValue = mySlider.minValue
mySlider.minValue = theMinimumValue
 
// getting & setting the maximum value of the range
let theMaximumValue = mySlider.maxValue
mySlider.maxValue = theMaximumValue

Again, nothing too surprising here — if you’ve learned anything by now, it’s that implementing standard UI controls in your OS X application is a fairly straightforward exercise. Move on to the next section to include an NSSlider in your app!

Pick a Number, Any Number

You could have the user just type in a value into a text field, but it’s much more interactive – and intuitive – to let the user use a slider to enter a numeric value in your app.

To include a slider in your app, select MainMenu.xib in the project explorer to open it in Interface Builder, and then select the Main Window. In the Object Library palette, locate the Label control and drag it onto the window.

Double-click the control to edit its default text, changing it to Amount: [10]. Next, locate the Horizontal Slider control and drag it onto the window, placing it to the right of the label.

Click on the slider to select it, and set the Minimum value to 1, and the Maximum value to 10, using the attributes inspector. Change the Current value to 5 — this will be the default value of the slider when the user first runs the app.

Also, make sure that Continuous is checked. It’s important that this option is set, as it tells the slider to notify of any change in the slider’s value. If Continuous wasn’t set, the slider would send only a single notification when the knob was moved!

The screenshot below shows the setup of the Slider control:

added-slider

Now that the slider is in place, you’ll need to create two outlets; one for the slider, and one for the label. Wait, you may say — that’s a little different. Why are you adding a property for the label?

That’s so you can update the label’s text continuously to list the current amount whenever the value of the slider is changed; hence why you set the Continuous property on the slider. Aha! Makes sense now, doesn’t it? :]

Select the assistant editor and — just as before, making sure the RootViewController.swift file is selected — Ctrl-Drag the label to the RootViewController.swift to create a new outlet. Name it amountLabel.

Repeat the above process with the slider, naming the outlet amountSlider.

Since you want the slider to inform the app whenever its value is changed, you need to add an action to your application. You already created an action for your button in part 1; adding an action is very much like creating an outlet, so you’ll get a little more practice!

To create an action in your code, select the slider and Ctrl-Drag to RootViewController.swift anywhere within the class definition:

drag-action-slider

In the popup window, be sure to set the connection as an action rather than a outlet. Name it sliderChanged, like so:

slider-popup-action

Now that you’ve created the action, you need to add the code which will update the label whenever the action is called.

Add the following code inside the sliderChanged method:

let amount = amountSlider.integerValue
amountLabel.stringValue = "Amount: [\(amount)]"

A quick review of the code above shows that you first read the slider’s current value. Then you set the value of the label to a string containing the slider’s value.

Note: This example uses integerValue to get a nice round number, but if you need more precision you could use either floatValue or doubleValue for your slider.

Build and run the app. Try moving the slider back and forth to see the label update with the slider’s current value:

slider-filled

There’s one small problem — did you notice it? The label does not display the slider’s current default value when the app first launches! While it’s not a big problem, it makes the app look unfinished. The reason for this is that the label is only updating when the slider’s knob is moved.

Fear not — it’s relatively easy to fix! :]

Add the following code to the end of awakeFromNib:

// Update the amount slider
sliderChanged(self)

Now the app will call sliderChanged at launch — and that will cause the label to be updated, as the slider’s value is read even before the user touches the control. Neat!

Build and run your app — the label displays the value at first run, which is a small touch, but is one of those ‘fit and finish’ elements that make your app look polished.

What about more complicated values, such as calendar dates? Yup, OS X has those handled too! :]

Hot Date Tonight — NSDatePicker

Date Pickers are controls that display date and time values, as well as providing a method for the user to edit those values. Date Pickers can be configured to display a date, a time or both a date and time. The control responsible for this on OS X is NSDatePicker.

Date Pickers can be displayed in one of two styles: textual, where the date and time information is shown in text fields, and graphical, where the date is represented by a calendar and the time by a clock. You can find examples of all these styles in OS X’s Date & Time preferences panel, as in the screenshot below:

The most common tasks you’ll perform with a date picker are getting and setting the date or time value, and setting the minimum and maximum date or time values that are permitted in your control. The properties to do this are set out below!

// getting & setting the date/time value
let myDate = myDatePicker.dateValue
myDatePicker.dateValue = myDate
 
// getting & setting the minimum date of the range
let theMinimumDate = myDatePicker.minDate
myDatePicker.minDate = theMinimumDate
 
// getting & setting the maximum date of the range
let theMaximumDate = myDatePicker.maxDate
myDatePicker.maxDate = theMaximumDate

Again — the controls have a very simple getter and setter style interface to update these values. Now it’s time (pardon the pun!) to put this control to work! :]

I’m Late for a Very Important Date

Following the usual procedure, add a new Label to your window and change its title to Date:. Find the Date Picker control in the Object palette, and drag it onto the window, placing it to the right of the label, like so:

added-date

Create an outlet for the date picker, just as you’ve done for each of the previous controls. In the popup window, name the property datePicker.

Just like the other controls in your app, it’s nice to display a default value to the user when they first run your application. Picking today’s date as the default sounds like a good choice! :]

Add the following code to the end of awakeFromNib:

// Set the date picker to display the current date
datePicker.dateValue = NSDate()

Build and run the app! You should see your shiny new date picker displaying current date, like in the screenshot below:

date-filled

Everything working OK? Great! Next up: Radio Buttons!

Video Killed the Radio…Button — NSMatrix

Radio buttons are a special type of control that always appear in groups; they are typically displayed as a list of options with selectable buttons alongside. Their behaviour is also somewhat unique; any button within the group can be selected, but selecting one button will deselect all other buttons in the group. Only a single button, within the same group, can be selected at one time.

A good example of radio buttons that are used to present a set of options is the iTunes Back Up options, as shown below:

There’s a special class of control to handle this unique grouped behaviour: NSMatrix. With NSMatrix, you can define a group of radio buttons and it will automagically handle all of the events of that group for you.

For example, every time a radio button is clicked, the matrix control selects the clicked button, and deselects the rest of the buttons within that group. You only need to worry about getting and setting the proper values. How convenient! :]

NSMatrix allows you to group radio buttons in rows and columns. When working with radio buttons and NSMatrix, you’ll typically need to get the row of the selected button, or select one of the buttons from your code. You can perform those actions using the following methods and properties:

// Select a radio button at a specific row and column within the matrix
let row = 3
let col = 1
myMatrix.selectCellAtRow(row, column: col)
 
// Get the selected row of the matrix
let selectedRow = myMatrix.selectedRow
 
// Get the selected column of the matrix
let selectedCol = myMatrix.selectedColumn

Once again, a complicated control is reduced to some very simple methods. Read on to implement a radio button control in your app!

A Place to Call Home – Adding Radio Buttons

Add a new Label to your app (you should be getting pretty comfortable with this by now!), and change its title to Place:. Locate Radio Group in the Object Library palette, and drag it onto the window, just beside the label.

Select the group of radio buttons and open the attributes inspector in the utilities panel. Set Rows to 3, and Columns to 1. Change the title of the first radio button to read WWDC, the title of the second one to read 360iDev, and the third to read RWDevCon, as in the image below:

added-matrix

Now, create a new property for the radio buttons group — another action you should be quite familiar with by now! Ctrl-Drag the group into the RootViewController.swift source file, just below the existing properties. Be careful to select and drag from the group, and not an individual radio button!

matrix-dragged

In the popup window that appears, name the property placeRadioButton, as below:

matrix-name

Again, you’ll need to select a default value for your radio button control when the app launches. To make the RWDevCon radio button the default, you’ll need to know the row and column index of the radio button you want to set as default. As the index of both the rows and columns in the radio button control is zero based, that makes it row 1, column 0.

Add the following code to the end of awakeFromNib:

// Set the radio group's initial selection
placeRadioButton.selectCellAtRow(2, column: 0)

Build and run the application. You should see your radio button control on the window, ready to be selected, and the RWDevCon as the default:

madlibs-rwdevcon

Radio buttons are one way to toggle values in your app, but there’s another class of controls that perform a similar function — check boxes!

Ticking all the Boxes — NSButton

Check boxes are in fact the same as push buttons, but they warrant their own section because they’re used in a different manner. Typically, push buttons are used to send a message to an action when clicked; you don’t necessarily care about their state otherwise. With check boxes however, you need to know about their state, but most of the time, you don’t care if or when they’re clicked! :]

You typically use check boxes in an app to display the state of some boolean value. That state tends to influence the app in some way such as enabling a feature, or changing a property of an object.

You can find a good example of check boxes in the Reminders app. The check box informs both the app and the user if a task has been completed, and you can toggle the state of the task by clicking on the checkbox, as below:

Working with check boxes is relatively easy; most of the time you’ll only be concerned with getting and setting the state of the control. The state of the check box can be one of three states: NSOnState (feature on everywhere), NSOffState (feature off everywhere) and NSMixedState (feature on somewhere, but not everywhere).

Here’s how you can use it:

// Set the state to On
myCheckBox.state = NSOnState
 
// Set the state to Off
myCheckBox.state = NSOffState
 
// Get the state of a check box
let state = myCheckBox.state

Super simple! Time to add a checkbox to your app. You should be pretty familiar with adding controls and properties to your app by now — if you’re up for the challenge, see if you can figure out how to add the control without looking at the steps below! :]

Check and Double Check – Adding Checkboxes

Find the Check Box in the Object Library and drag it onto the window, changing it’s title to Yell!! as in the image below:

added-check

As you’ve done many times now, add an outlet for the check box. Open the assistant editor and Ctrl-Drag the check box to RootViewController.swift and in the popup window, name the property yellCheck, like so:

check-popup

For this tutorial, make the check box default to the off state when the app launches. To do that, add the following code to awakeFromNib:

// set check button state
yellCheck.state = NSOffState

Build and run the application! You should see the check box, and it’s state should be unchecked. Click it to see it in action:

button-filled

Okay! You’ve finally added all the controls you need to create your funny mad lib sentences. All you’re missing is a way to collect the value of each control, combine those values into a sentence, and display the sentence on-screen!

Pulling it All Together

First, you will need to add the controls to your app where the results will be displayed. You’re going to use two controls: a label to display the complete sentence, and an image view to display a picture, which should liven up the user interface!

Find the Wrapping Label in the Object Library palette and drag it onto the window, just below the Go!! button. Make it look a little more attractive by using the attributes inspector to change the border of the label to Frame, which is the first of the four buttons.

After that, remove the default text of the label by double-clicking it, selecting the text and hitting backspace, like below:

multiline-add

You’ll need to create an outlet to set the value of this new label to contain your new hilarious sentence! As before, Ctrl-Drag the label to the RootViewController.swift file, and in the popup window. name the property resultTextField.

Leave this control as it is for now; you’ll write the code that populates it in just a bit. The last control you’ll need — an Image View, to show your image on the screen — is up next.

Room with a View — NSImageView

An Image View is a simple and easy to use control that — surprise! — displays an image. Bet you didn’t expect that! :]

There are very few properties you need to interact with an Image View at runtime:

// Get the image from an image view
let myImage = myImageView.image
 
// Set the image of an image view
myImageView.image = myImage

At design time, you can configure the visual aspects: the border, scaling and alignment. Yes, these properties can be set in code as well, but it’s far easier to set them in Interface Builder at design time, as below:

imageview-props

Just a Pretty Face – Populating the Image Well

OK, time to add an image view to your application! In the Object Library palette, find the Image Well and drag it onto the window, just below the wrapping label. Feel free to resize the app window if necessary.

added-image

Create a new outlet for the image view in the same way you’ve done for all the previous controls: Ctrl-Drag the image view to the RootViewController.swift file, and in the popup window name the property imageView.

Build and run. Your app should now look like this:

image-filled

Phew! Your user interface is finally finished — the only thing that’s left to do is to create the code that will assemble your hilarious sentence and populate the image view that you added above!

Time To Make It Work

Now that you have all the controls you need to allow the user to select the various inputs to the application, you need to construct the sentence based on those inputs.

When the user clicks the Go! button, you’ll collect all the values from the different controls and combine them to construct the full sentence, and then display that sentence in the wrapping label you added previously.

Finally, in order to spice up your all-text interface, you will also display a picture in the image view that you added in the last section.

To do this, download the resources for this project, and add the single image to your project.

drag-face

When the options window appears, make sure the Destination: Copy items if needed option is checked, as shown below:

copyimage-options

Clicking Finish will add the image file to your project and make it available for your use!

face-added

It’s finally time to add the core of the application — the code which constructs the mad lib sentence! Since this needs to happen when the Go! button is clicked, you’ll add the necessary code to the goButton method.

Add the following code to goButton:

// Past tense verb
let pastTenseVerb = pastTenseVerbTextField.stringValue
 
// Singular noun
let singularNoun = singularNounCombo.stringValue
 
// Place
let cell: NSButtonCell = placeRadioButton.selectedCell() as NSButtonCell
let placeString = cell.title
 
// Amount
let amount = amountSlider.integerValue
 
// Plural noun
let pluralNoun = pluralNouns[pluralNounPopup.indexOfSelectedItem]
 
// Phrase
let phrase = phraseTextView.string
 
// Date
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.LongStyle
let date = dateFormatter.stringFromDate(datePicker.dateValue)
 
// Speak or SHOUT
var voice = "said"
if yellCheck.state == NSOnState {
  voice = "yelled"
}
 
// Create the mad lib sentence
let results = "On \(date), at \(placeString) a \(singularNoun) \(pastTenseVerb) \(amount) \(pluralNoun) and \(voice), \(phrase)!"
 
// Display the mad lib sentence
resultTextField.stringValue = results
 
// Load the rage face image
imageView.image = NSImage(named: "face.png")

That may seem like a lot of code, but don’t worry — it will be explained step by step! :]

let pastTenseVerb = pastTenseVerbTextField.stringValue

Here you’re getting the string value from the pastTenseVerbTextField by calling its stringValue property.

let singularNoun = singularNounCombo.stringValue

In this section of code, you get the string from the combo box by calling its stringValue property.

You might ask why you don’t just look up the selected row, and then retrieve the string associated with that row? Quite simply, it’s because the user can enter their own text into the combo box. So use the stringValue to get the current string, which could have been either selected or typed.

let cell: NSButtonCell = placeRadioButton.selectedCell() as NSButtonCell
let placeString = cell.title

Here you get the currently selected radio button by calling NSMatrix’s selectedCell method, and assigning the title string value to the placeString constant.

let amount = amountSlider.integerValue

Next, read the slider’s current value using its integerValue method. Remember that if you need more precision with this control, you could also use floatValue or doubleValue.

let pluralNoun = pluralNouns[pluralNounPopup.indexOfSelectedItem]

Here you get the plural noun, selected from the popup button. How is this done? Look up the appropriate plural noun in your pluralNouns array using array subscript syntax, by calling the popup indexOfSelectedItem property.

let phrase = phraseTextView.string ?? ""

Next up is the phrase the user typed. To acquire it, simply retrieve the string value of our text view by getting its string property. Again, you’re using nil coalescing since the property is an optional and could be nil.

let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.LongStyle
let date = dateFormatter.stringFromDate(datePicker.dateValue)

To get the date, call the date picker’s dateValue method. Then, convert the returned date to a human-readable string using an NSDateFormatter.

var voice = "said"
if yellCheck.state == NSOnState {
  voice = "yelled"
}

The final piece of the jigsaw: should you speak or shout? Simply get the checkbox state: if it’s NSOnState, assign yelled to the string variable. Otherwise, leave it as the default said.

let results = "On \(date), at \(placeString) a \(singularNoun) \(pastTenseVerb) \(amount) \(pluralNoun) and \(voice), \(phrase)!"

At this point, you’ve collected all the information you need to construct the mad lib sentence! This is where the magic happens. The results constant uses string interpolation, and the different values read from the various controls to build a string, based on the user’s input.

resultTextField.stringValue = results
 
imageView.image = NSImage(named: "face.png")

Finally — you can display the results of all your hard work! First, display the sentence in the results label, by setting its stringValue property. Then, add some pizazz to the app by displaying an image to the user, which is as easy as loading the image and setting the image property of the image view control.

That’s it! You’re done! Run and build the app, so you can construct some hilarious sentences for yourself!

final-result

Congratulations! You’ve finished building the Mad Libs application, and have learned a ton about the most common Mac OS X controls along the way.

Feel free to play with the controls, select different values, type funny nouns or verbs and see the results each time you click the Go! button, and see what funny stories you can create! :]

Where To Go From Here?

Here is the final project containing all the code from this tutorial.

In order to gain a deeper understanding of the controls provided by OS X, I recommend you have a read through the different programming guides available from Apple listed below, which contain a wealth of information about how to use the available controls.

In particular, I highly recommend you read the OS X Human Interface Guidelines. This guide explains the concepts and theories of user interfaces on OS X, and Apple’s expectations of how developers should use the controls and design their UI’s to provide a consistent and pleasurable experience. It’s essential reading for anyone intending to develop for the Mac platform, especially if they plan to distribute their applications via the Mac App Store.

Here are some useful links to reinforce or further explore the concepts you’ve learned in this tutorial:

I hope you enjoyed this tutorial, and as always if you have any questions or comments please join the forum discussion below!

Introduction to OS X Tutorial: Core Controls and Swift Part 2/2 is a post from: Ray Wenderlich

The post Introduction to OS X Tutorial: Core Controls and Swift Part 2/2 appeared first on Ray Wenderlich.

Video Tutorial: Swift Scroll View School Part 16: Pull to Refresh II


Video Tutorial: Swift Scroll View School Part 17: Pull to Refresh III

Video Tutorial: Swift Scroll View School Part 18: Sprite Kit Scrolling

Video Tutorial: Swift Scroll View School Part 19: Sprite Kit Zooming

Video Tutorial: Swift Scroll View School Part 20: Sprite Kit Level Selector I

Video Tutorial: Swift Scroll View School Part 21: Sprite Kit Level Selector II

Video Tutorial: Collection Views Part 0: Introduction

Video Tutorial: Collection Views Part 1: Getting Started


Apple Pay with Erik Kerber – Podcast S03 E01

$
0
0
Learn about Apple Pay with Erik Kerber!

Learn about Apple Pay with Erik Kerber!

Welcome to season 3 of the raywenderlich.com podcast!

In this brand new season, we’re switching things up a bit with the following format:

  • A 10-minute overview of the topic with Mic & Jake, followed by:
  • A 20-minute technical discussion with a special guest

Our hope is that this does a better job familiarizing you with each topic, and helps you get to know Mic & Jake a bit better. Let us know what you think! :]

In this episode, we talk with Tech Editor and Tutorial Team Author Erik Kerber, to discuss Apple Pay – how it works, and how you can integrate it into your own apps.

[Subscribe in iTunes] [RSS Feed]

Our Sponsor

Interested in sponsoring a podcast episode? We sell ads via Syndicate Ads, check it out!

Links and References

Contact Us

Where To Go From Here?

We hope you enjoyed this episode of our podcast. Stay tuned for a new episode next week! :]

Be sure to subscribe in iTunes to get access as soon as it comes out!

We’d love to hear what you think about the podcast, and any suggestions on what you’d like to hear in future episodes. Feel free to drop a comment here, or email us anytime at podcast@raywenderlich.com!

Apple Pay with Erik Kerber – Podcast S03 E01 is a post from: Ray Wenderlich

The post Apple Pay with Erik Kerber – Podcast S03 E01 appeared first on Ray Wenderlich.

Top 10 iOS Conferences in 2015

$
0
0
Check out the best iOS conferences in 2015!

Check out the best iOS conferences in 2015!

Each January, we write about the top 10 iOS conferences in the coming year, so everyone can start thinking about their conference plans.

I have to say, this has been the most challenging year to write this article yet. The number of iOS conferences has exploded lately, which makes choosing the top 10 very difficult.

Mike Lee says it best: “We have always had a great conference culture in our community, and lately there seems to be an embarrassment of riches on a world scale.”

So please remember that this article is just my own personal opinion based on what I’ve heard from team members and the community. If I miss out on a conference you really like, please add your comments to this post, so everyone else can learn about it too.

The Conferences

Without further ado, let’s dive in to the top 10 iOS conferences of 2015!

1) WWDC and AltConf

WWDC

The elephant in the room with iOS conferences is WWDC – Apple’s official developer conference.

Without a doubt going to WWDC is a must – if you can get a ticket, that is. Last year, Apple introduced a new lottery system to get tickets, so it’s now a matter of luck.

However, even if you don’t manage to get a ticket, you should consider going to San Francisco anyway. WWDC is the largest gathering of iOS developers in the entire year, so you can meet up with all your friends at various meetups and parties.

Even better, during the same time as WWDC there’s a great unofficial conference called AltConf, which is a great way to hear from some well known non-Apple speakers from across the community.

“WWDC is the conference that transcends all other iOS conferences. Developers from around the world make their pilgrimage to the the Moscone Center to not only hear what new devices, frameworks, and other Apple tech they’ll get this year, but to get rare face time with Apple designers and engineers. The conference is only half of the event, as during WWDC week there are countless meetups and parties for both those with and without tickets.” -Erik Kerber

“The theory behind AltConf is simple: it’s worth being in town for WWDC, even if you don’t have tickets, and if you’re already there, you might as well go to the conference next door. The reality of what AltConf has become is an important addition to the very controlled stream of information Apple provides. Some of the industry’s best speakers don’t work at Apple, and some of the industry’s most important topics aren’t being discussed by Hair Force One.” -Mike Lee

“For the first developer conference I’ve attended, WWDC smashed all my expectations and turned me into a blob of hype and excitement. Apple engineers in the labs are both friendly and helpful so I would highly recommend you go talk to them if you get a chance. AltConf, which takes place across the street from WWDC, featured some extremely high quality talks from several well known individuals. If you don’t win the WWDC lottery, you can attend AltConf and still catch some Apple engineers in coffee shops all around the city.” -Jack Wu

2) 360iDev

360idev conference

I’ve made no secret that 360iDev is one of my favorite iOS conferences. It has a little bit of everything – technical talks, design talks, business talks, and great parties – and has a wonderful community of attendees that come back year after year.

Attending 360iDev always makes you feel like you’re returning to home, and meeting up with friends you haven’t seen in ages. I look forward to many more years of 360iDev to come!

“360iDev should be attended by every iOS and Mac developer at least once. The organizers do a great job at securing a good swatch of speakers for extremely varied topics and a fun atmosphere. It’s definitely affordable and usually paired with a hotel making a stress-free experience. This past year had a few logistical hiccups with the new venue they chose but still was a blast. I’m definitely coming back.” -Aaron Douglas

“If I could only go to one conference this year it would be 360iDev in Denver. It’s a great mix of how to talks, introduction to new technologies, code and technology samples and business ideas and tips. The climate and weather are usually great and so are the other attendees. Don’t plan on doing any sight seeing, as this conference is packed with content.” -Tim Mitra

3) CocoaConf

cc

If 360iDev is the annual big gathering, CocoaConf is the periodic local meetup. CocoaConf’s unique trait is the conference travels across the US, having multiple locations across the year.

CocoaConf also has some fun unique traits like group discussions, game shows, geeky iOS songs, and more. Definitely recommended if one is near you!

“CocoaConf is a great affordable iOS and Mac circuit conference managed by Dave Klein and his family. This conference comes to at least eight locations throughout the year and is something every Mac and iOS developer should try to attend. Year to year some of the presenters haven’t changed but the content is fresh and the people are fun to be with.” -Aaron Douglas

“CocoaConf is a great traveling conference which allows you to learn a lot without having to travel too far from home. It’s a great way to meet other devs from your area and your region as well. I enjoyed attending for the first time in the spring so much that I asked if I could put a talk together, and I would up speaking in Boston and Atlanta this fall.” -Ellen Shapiro

4) NSConference

NSConference

NSConference is one of the oldest iOS conferences in Europe, and always draws top-notch speakers.

“NSConf – the best conference outside of WWDC for me. The conference attracts the best speakers and because of that, a top quality attendee base. The conference is really set up to meet new people and you find yourself sitting at a new table every day. Fantastic!” -Matt Galloway

“NSConference feels like a mini WWDC set in Leicester, the location is a concert, auditorium setting with round tables. The organizers encourage a lot of networking in between sessions and this helps if you like meeting and chatting with fellow developers. The talks have a wide-range of topics, from the outright weird keynote to the former Apple Developer Evangelist giving you marketing advice. And if you like alcohol, the evening drinks are fun to be at :-)” -Tope Abayomi

“You can’t call 360|iDev the most important indie conference in the world, because it shares that title with NSConference, and you’re not seeing the whole world of Apple unless you go to both. Everybody love NSConference, but I think what’s most amazing is that the organizers have been expanding and evolving the production every year, yet it somehow keeps getting better.” -Mike Lee

  • When: March 16-18, 2015
  • Where: Athena, Leicester, UK
  • Team members planning to attend: Kyle Richter
  • Websites: NSConference

5) iOSDevUK

iOSDevUK

iOSDevUK is a conference located in a university in Wales. It is extremely technical, and its remote location encourages attendees to bond and make good friendships. I’ve heard great things about this conference from many folks on the raywenderlich.com team and am attending myself this year.

“iOSDevUK shouldn’t really work, but somehow it totally does. Forget the ritzy hotels, grand keynotes, packed ballrooms, and big city nightlife – iOSDevUK is really just a bunch of loveable devs shacking up together on a quaint University campus near the scenic Welsh seaside… and it is phenomenal! If you truly want to connect with world-class speakers, chat with all the attendees, and be fully immersed in iOS for a couple of days, then you must attend this conference. You’ll feel like nothing matters in this world except for iOS :]” -Ricardo Rendon Cepeda

“The first thing you will enjoy about iOSDevUK is the location, if you enjoy remote landscapes and seeing the ocean. The conference itself is set in the University of Aberystwyth so it instantly gives you a scholarly feel. The talks are very technical so you do get to learn a lot. The dinner too is amazing.” -Tope Abayomi

“As pretty much everyone is want to remark – this conference is not the easiest to get to. However, for me, that just adds to it’s charm; there are few routes in to Aberystwyth, the most convenient form of transport being by train. Inevitably this leads to a gathering of attendees all heading for the same train which takes on a geek equivalent of the ‘Hogwarts Express’. The conference is held at Aber’s University campus which seems to get moved a bit further up the hill every year! Accommodation is available on site in student rooms which are basic but transport me back to Uni days. Late nights & one or two drinks serve only to reinforce that feeling.

Conference sessions are held in parallel in two lecture theatres, so there’s always a decision to make over which to attend. There are plenty of breaks for coffee & biscuits between sessions, giving great opportunities to catch up with old friends & to make new ones. This is one conference which has regular attendees year-on-year with good reason. Sessions cover a wide range of subjects & every one I attended taught me something new. Despite the walk back up the hill I have no hesitation in recommending iOSDevUK. For me it feels like being back at Uni & I come away each year feeling that I’ve learned something as well as having a great time.” -Mark Brindle

6) MDevCon

Mdevcon

MDevCon is a mobile conference in Amsterdam. It focuses on more than just iOS – if you also think Android and Google Glass development is cool, this is the conference for you!

“If you’re crossing an ocean for NSConference, there’s a good chance you’re stopping in Amsterdam. Come a week early and check out Mdevcon. It’s a great scene, and the perfect warmup, but it’s also worth it just to see the most beautiful venue in the most livable city in the world. You can even attend some Appsterdam events while you’re in town. ;)” -Mike Lee

“Mdevcon is held at the fabulous Tuschinski Theater in Amsterdam. It’s a great location, right downtown. The conference is very well organized, usually in multiple tracks, covering both design and development topics related to mobile platforms. Attendees are very very friendly and I had great conversations during breaks and dinners. Definitely a conference that I recommend.” -Cesare Rocchi

7) #Pragma Conference

PragmaConference

#Pragma Conference is a conference in Italy that has both technical and design tracks, and covers both iOS and OS X development. Another one I’ve heard great things about!

“Pragma conference is held in Milan, which is easily reachable within a couple of hours by plane from anywhere in Europe. I’ve been to both editions. The 2014’s edition was held entirely in English and brought an international audience that was missing in the first edition. What I like best about it is that it is held after the WWDC, so there are a lot of new frameworks and APIs to inspire the speakers. Pragma conference should be a must for any iOS developer in Europe.” -Mouhcine El Amine

“I can’t recommend enough Pragma Mark – the guys organizing this event always manage to create a very special feeling about the conference. It’s an event happening usually just few weeks after the new version of iOS comes out so you can count on attending interesting and entertaining sessions and meeting cool people. Pragma Mark really puts Italy on the conference map by attracting people from around the world to come and meet up in Milan.” -Marin Todorov

8) MCE

MCEConf

MCE is a relatively new conference in Warsaw, Poland – its first event was in 2014. The 2015 edition has several tracks on both deign and programming topics and appears to be very well organized and received.

“If you are planning a visit to Europe, you’re probably thinking about Berlin, but if you’ve never been to Berlin, you’re actually thinking about Warsaw. If you have been to Berlin, you’ll feel you’ve gone back in time 10 years. Either way, there’s no travel like business travel, and there’s no better excuse to visit Warsaw than MCE. Be sure to visit the museum of the Warsaw resistance while you’re there to learn some things about World War 2 they left out of your textbook.” -Mike Lee

“Easy location in Warsaw, impressive first edition organized in cinema, good lineup of cross discipline speakers and after party;)” -Krzysztof Zablocki

  • When: February 4-6, 2015
  • Where: Warsaw, Poland
  • Team members planning to attend: N/A
  • Websites: MCE

9) NSNorth

NSNorth

NSNorth is Canada’s premier iOS developer and designer conference, with a mix of both technical and non-technical talks. Attendance is usually kept small for an intimate feel.

“NSNorth is one of the best conferences I have ever been to. You are likely to meet every single attendee, including the presenters, when you’re there. I’ve personally made some great friends in our community every time I attend. If top-notch speakers weren’t enough, the organizers usually plan some wonderful social events like drinks in a dinosaur museum or ‘find-the-iBeacon’.” -Ryan Nystrom

“For the last two years, I’ve attended NSNorth, an iOS conference held in Ottawa (Canada). It is a single-track conference that combines traditional talks with short blitz talks all centered around one main goal: bringing together independent iOS and Mac developers as well as designers to discuss the business of app development as well as creating new friendships and hopefully new business relationships. Both organizers of NSNorth go above and beyond to ensure everyone enjoys their experience at the event.” -Pierre Rochon

“NSNorth was a terrific conference, attended both so far. 2014 was really a big step up — a really interesting lineup of speakers, with a few well-known speakers from our community as well as some great lesser-known and local speakers — it makes for a nice balance since you’re not just hearing from the same voices as other conferences. They balance technical talks with inspirational and business topics, so you get a nice mix. They seem to achieve about a 50/50 mix of men and women speakers.” -Chris Saldanha

10) Yow! Australia

Yow

Last but not least, Yow! is a great mobile conference for anyone in Australia!

“If you’re in Australia and you’re into technology, Yow! is a no-brainer, but I am going to sing their praises anyway. I’ve never been so humbled to meet another speaker, or so honored to be in the presence of an conference organizer, as I have been with Yow! It’s not just impressive. It’s a reason to go to Australia.” -Mike Lee

“I attended and spoke at the Yow! conference for the first time last year. I had a lovely time. Melbourne is a great city that I enjoyed a lot. The conference was very well conceived. The keynote, about an open source satellite, blew me away. I really liked the cozy environment, many of the presentations and all the satellite events organized after the conference. If it’s always like that I am back any time :).” -Cesare Rocchi

Honorable Mentions

As I mentioned, it was very difficult (and subjective) to put together the top 10 list this year as there are so many great conferences. I felt it would be amiss if I didn’t also include a few honorable mentions.

  • CocoaLove: CocoaLove is a new conference that focuses more on the people behind the tech.
  • Cmd-R: Cmd-R is a relatively new conference in London that had some great speakers and I’ve heard good things about their first edition.
  • NSScotland: NSScotland is small, but well liked – worth checking out if you’re in the UK.
  • NSSpain: Another relatively new conference, with a single track and focus on meeting new people.
  • RWDevCon: We’re also running our own conference for the first time this year. The focus is on hands-on tutorials, inspiration, team coordination, and friendship.

Which Should I Choose?!

If you’re unsure which to choose, here’s my advice – in a handy flowchart!

Best iOS Conferences in 2015

[Click to see full-size version]

Again, note that this article is my personal opinion only and is based on what I’ve heard from team and community members who have attended these conferences.

Huge thank you to all who took the time to send me their thoughts and quotes about the conferences you attended in 2014 for this post – I really appreciate it! :]

We’d love to hear your opinion too! Please let us know about your experiences at any iOS conferences you attended in 2014 – it will be really helpful for those trying to decide which to attend.

We hope to see you at some iOS conferences this year!

Top 10 iOS Conferences in 2015 is a post from: Ray Wenderlich

The post Top 10 iOS Conferences in 2015 appeared first on Ray Wenderlich.

NSRegularExpression in Swift Tutorial

$
0
0
NSRegularExpression tutorial and cheat sheet!

NSRegularExpression tutorial and cheat sheet!

Update note: This tutorial was updated to iOS 8 and Swift by James Frost. Original post by Tutorial Team member Soheil Azarpour.

A regular expression (commonly known as a “regex”) is a string or a sequence of characters that specifies a pattern. Think of it as a search string — but with super powers!

A plain old search in a text editor or word processor will allow you to find simple matches. A regular expression can also perform these simple searches, but it takes things a step further and lets you search for patterns, such as two digits followed by a letter, or three letters followed by a hyphen.

This pattern matching allows you to do useful things like validate fields (phone numbers, email addresses), check user input, perform advanced text manipulation and much, much more.

If you have been eager to know more about using regular expressions in iOS, look no further than this tutorial — no previous experience required!

In this NSRegularExpression tutorial, you will implement code to search for patterns in text, replace those matches with whatever you wish, validate user input information, and find and highlight complex strings in a block of text.

In addition, I’ll give you a handy NSRegularExpression Cheat Sheet PDF that you can print out and use as reference as you’re developing, and a Swift playground which contains a load of examples that you can use to try out lots of different regular expressions! In fact, all of the examples of regular expressions that appear in this tutorial have live examples in the playground, so be sure to check them out.

Without further ado, it’s time to start crunching some regular expressions.

/The (Basics|Introduction)/

Note: If you’re already familiar with the basics of regular expressions, feel free to skip ahead to the section Implementing Regex in iOS.

If you are new to regular expressions and are wondering what all the hype is about, here’s a simple definition: regular expressions provide a way to search a given text document for matches to a specific pattern, and they may possibly alter the text based on those matches. There are many awesome books and tutorials written about regular expressions – you’ll find a short list of them at the end of this tutorial.

Regular Expressions Playground

NSRegularExpression Playground

NSRegularExpression playground

In this tutorial, you’ll be creating a lot of regular expressions. If you want to try them out visually as you’re working with them, then a Swift playground is an excellent way to do so!

The starter project includes the playground for this tutorial. Once you’ve downloaded it, load up the project in Xcode and open iRegex.playground. You can also download the playground on its own.

The playground contains a number of functions at the top to highlight the search results from a regular expression within a piece of text, display a list of matches or groups in the results pane of the playground, and replace text. Don’t worry about the implementation of these methods for now though, as you’ll revisit them later. Instead, scroll down to the Basic Examples and Cheat Sheet sections and follow along with the examples.

In the results sidebar of the playground, you’ll see a list of matches alongside each example. For ‘highlight’ examples, you can hover over the result and click the eye or the empty circle icons to display the highlighted matches in the search text.

You’ll learn how to create NSRegularExpressions later, but for now you can use this playground to get a feeling for how various regular expressions work, and to try out your own patterns. At any point you can use the Editor > Reset Playground menu item in Xcode to reset any of your changes.

Examples

Let’s start with a few brief examples to show you what regular expressions look like.

Here’s an example of a regular expression that matches the word “jump”:

jump

That’s about as simple as regular expressions get. You can use some APIs that are available in iOS to search a string of text for any part that matches this regular expression – and once you find a match, you can find where it is, or replace the text, etc.

Here’s a slightly more complicated example – this one matches either of the words “jump” or “jumping”:

jump(ing)?

This is an example of using some special characters that are available in regular expressions. The parenthesis create a group, and the question mark says “match the previous element (the group in this case) 0 or 1 times”.

Now for a really complex example. This one matches a pair of opening and closing HTML tags and the content in between.

<([a-z][a-z0-9]*)\b[^>]*>(.*?)\1>

Wow, looks complicated, eh? :] Don’t worry, you’ll be learning about all the special characters in this regular expression in the rest of this tutorial, and by the time you’re done you should be able to understand how this works! :]

If you want more details about the previous regular expression, check out this discussion for an explanation.

Note: in real-world usage you probably shouldn’t use regular expressions alone to parse HTML. Use a standard XML parser instead!

Overall Concepts

Before you go any further, it’s important to understand a few core concepts about regular expressions.

Literal characters are the simplest kind of regular expression. They’re similar to a “find” operation in a word processor or text editor. For example, the single-character regular expression t will find all occurrences of the letter “t”, and the regular expression jump will find all appearances of “jump”. Pretty straightforward!

Just like a programming language, there are some reserved characters in regular expression syntax, as follows:

It's easy to get carried away with regular expressions!

It’s easy to get carried away with regular expressions!

  • [
  • ( and )
  • \
  • *
  • +
  • ?
  • { and }
  • ^
  • $
  • .
  • | (pipe)
  • /

These characters are used for advanced pattern matching. If you want to search for one of these characters, you need to escape it with a backslash. For example, to search for all periods in a block of text, the pattern is not . but rather \..

Each environment, be it Python, Perl, Java, C#, Ruby or whatever, has special nuances in its implementation of regular expressions. And Swift is no exception!

Both Objective-C and Swift require you to escape special characters in literal strings (i.e., precede them by a backslash \ character). One such special character is the backslash itself! Since the patterns used to create a regular expression are also strings, this creates an added complication in that you need to escape the backslash character when working with String and NSRegularExpression.

That means the standard regular expression \. will appear as \\. in your Swift (or Objective-C) code.

To clarify the above concept in point form:

  • The literal "\\." defines a string that looks like this: \.
  • The regular expression \. will then match a single period character.

Capturing parentheses are used to group part of a pattern. For example, 3 (pm|am) would match the text “3 pm” as well as the text “3 am”. The pipe character here (|) acts like an OR operator. You can include as many pipe characters in your regular expression as you would like. As an example, (Tom|Dick|Harry) is a valid pattern that matches any of those three names.

Grouping with parentheses comes in handy when you need to optionally match a certain text string. Say you are looking for “November” in some text, but it’s possible the user abbreviated the month as “Nov”. You can define the pattern as Nov(ember)? where the question mark after the capturing parentheses means that whatever is inside the parentheses is optional.

These parentheses are termed “capturing” because they capture the matched content and allow you reference it in other places in your regular expression.

As an example, assume you have the string “Say hi to Harry”. If you created a search-and-replace regular expression to replace any occurrences of (Tom|Dick|Harry) with that guy $1, the result would be “Say hi to that guy Harry”. The $1 allows you to reference the first captured group of the preceding rule.

Capturing and non-capturing groups are somewhat advanced topics. You’ll encounter examples of capturing and non-capturing groups later on in the tutorial.

Character classes represent a set of possible single-character matches. Character classes appear between square brackets ([ and ]).

As an example, the regular expression t[aeiou] will match “ta”, “te”, “ti”, “to”, or “tu”. You can have as many character possibilities inside the square brackets as you like, but remember that any single character in the set will match. [aeiou] looks like five characters, but it actually means “a” or “e” or “i” or “o” or “u”.

You can also define a range in a character class if the characters appear consecutively. For example, to search for a number between 100 to 109, the pattern would be 10[0-9]. This returns the same results as 10[0123456789], but using ranges makes your regular expressions much cleaner and easier to understand.

But character classes aren’t limited to numbers — you can do the same thing with characters. For instance, [a-f] will match “a”, “b”, “c”, “d”, “e”, or “f”.

Character classes usually contain the characters you want to match, but what if you want to explicitly not match a character? You can also define negated character classes, which start with the ^ character. For example, the pattern t[^o] will match any combination of “t” and one other character except for the single instance of “to”.

NSRegularExpressions Cheat Sheet

Regular expressions are a great example of a simple syntax that can end up with some very complicated arrangements! Even the best regular expression wranglers keep a cheat sheet handy for those odd corner cases.

To help you out, we have put together an official raywenderlich.com NSRegularExpression Cheat Sheet PDF for you! Please download it and check it out.

In addition, here’s an abbreviated form of the cheat sheet below with some additional explanations to get you started:

  • . matches any character. p.p matches pop, pup, pmp, p@p, and so on.
  • \w matches any “word-like” character which includes the set of numbers, letters, and underscore, but does not match punctuation or other symbols. hello\w will match “hello_” and “hello9″ and “helloo” but not “hello!”
  • \d matches a numeric digit, which in most cases means [0-9]. \d\d?:\d\d will match strings in time format, such as “9:30″ and “12:45″.
  • \b matches word boundary characters such as spaces and punctuation. to\b will match the “to” in “to the moon” and “to!”, but it will not match “tomorrow”. \b is handy for “whole word” type matching.
  • \s matches whitespace characters such as spaces, tabs, and newlines. hello\s will match “hello ” in “Well, hello there!”.
  • ^ matches at the beginning of a line. Note that this particular ^ is different from ^ inside of the square brackets! For example, ^Hello will match against the string “Hello there”, but not “He said Hello”.
  • $ matches at the end of a line. For example, the end$ will match against “It was the end” but not “the end was near”
  • * matches the previous element 0 or more times. 12*3 will match 13, 123, 1223, 122223, and 1222222223
  • + matches the previous element 1 or more times. 12+3 will match 123, 1223, 122223, 1222222223, but not 13.
  • Curly braces {} contain the minimum and maximum number of matches. For example, 10{1,2}1 will match both “101” and “1001” but not “10001” as the minimum number of matches is one and the maximum number of matches is two. He[Ll]{2,}o will match “HeLLo” and “HellLLLllo” and any such silly variation of “hello” with lots of L’s, since the minimum number of matches is 2 but the maximum number of matches is not set — and therefore unlimited!

That’s enough to get you started!

It’s time to start experimenting with these examples yourself, as they’re all included in the Playground mentioned above.

Implementing Regex in iOS

Now that you know the basics, it’s time to use regular expressions in an app.

If you haven’t done so yet, download the starter project for this NSRegularExpression tutorial. Once you’ve downloaded it, open up the project in Xcode and run it.

The UI for the app is mostly complete, but the core functionality of the app relies on regular expressions, which it doesn’t have…yet! Your job in this tutorial is to add the required regular expressions into this app to make it shine.

A few sample screenshots demonstrating the content of the application are shown below:

iRegex App Screenshots

The sample application covers two common use-cases with regular expressions:

  1. Performing text search: both highlighting and search & replace
  2. Validating user input

You’ll start by implementing the most straightforward use of regular expressions: text search.

/Search( and replace)?/

Here’s the basic overview of the search-and-replace functionality of the app:

  • The search view controller, SearchViewController has a read-only UITextView that contains an excerpt from Pride and Prejudice.
  • The navigation bar contains a search button that will present SearchOptionsViewController modally.
  • The user will then type some information into the field and tap “Search”.
  • The app will then dismiss the search view and highlight all matches in the text view.
  • If the user selected the “Replace” option in SearchOptionsViewController, the app will perform a search-and-replace for all matches in the text, instead of highlighting the results.

Note: Your app uses the NSAttributedString property of UITextView to highlight the search results. You can read more about it in chapter 15 of iOS 6 by Tutorials, “What’s New with Attributed Strings”.

You could also implement the highlighting functionality using Text Kit. Be sure to check out the Text Kit Tutorial in Swift to find out more.

There’s also a “Bookmark” button that allows the user to highlight any date, time or location in the text. For simplicity’s sake, you won’t cover every possible format of date, time and location strings that can appear in your text. You’ll implement this highlighting functionality at the very end of the tutorial.

Your first step to getting the search functionality working is to turn standard strings representing regular expressions into NSRegularExpression objects.

Open up SearchOptionsViewController.swift. SearchViewController presents this view controller modally, and allows the user to enter their search (and optional replace) terms, as well as specifying whether the search should be case sensitive or match only whole words.

Take a look at the SearchOptions struct at the top of the file. SearchOptions is a simple struct that encapsulates the user’s search options. The code passes an instance of SearchOptions back to SearchViewController. It would be good to be able to use this directly to construct an appropriate NSRegularExpression. You can do this by adding a custom initializer to NSRegularExpression with an extension.

Choose File > New > File… and choose Swift File. Name your file RegexHelpers.swift. Open up the new file, and add the following code:

extension NSRegularExpression {
  convenience init?(options: SearchOptions) {
    let searchString = options.searchString
    let isCaseSensitive = options.matchCase
    let isWholeWords = options.wholeWords
 
    let regexOption: NSRegularExpressionOptions = (isCaseSensitive) ? .allZeros : .CaseInsensitive
 
    let pattern = (isWholeWords) ? "\\b\(searchString)\\b" : searchString
 
    self.init(pattern: pattern, options: regexOption, error: nil)
  }
}

This code adds a convenience initializer to NSRegularExpression. It uses the various settings within the passed in SearchOptions instance to configure things correctly.

  • Whenever the user requests a case-insensitive search, the regular expression uses the .CaseInsensitive NSRegularExpressionOptions value. The default behavior of NSRegularExpression is to perform case-sensitive searches, but in this case you’re using the more user-friendly default of case-insensitive searches.
  • If the user requests a whole word search, then the app wraps the regular expression pattern in the \b character class. Recall that \b is the word boundary character class, so putting \b before and after the search pattern will turn it into a whole word search (that is, the pattern “\bcat\b” will match only the word “cat”, but not “catch”).

If for any reason it’s not possible to create the NSRegularExpression, then the initializer will fail and return nil. Now that you have the NSRegularExpression object, you can use it for matching text along with many other operations.

Open SearchViewController.swift, find searchForText, and replace it with the code below:

func searchForText(searchText: String, replaceWith replacementText: String, inTextView textView: UITextView) {
  let beforeText = textView.text
  let range = NSMakeRange(0, countElements(beforeText))
 
  if let regex = NSRegularExpression(options: self.searchOptions!) {
    let afterText = regex.stringByReplacingMatchesInString(beforeText, options: .allZeros, range: range, withTemplate: replacementText)
 
    textView.text = afterText
  }
}

First, this method captures the current text in the UITextView and calculates the text length. It’s possible to apply a regular expression to just a subset of your text, which is why you need to specify the range. In this case, you’re just using the the entire length of the string which will result in the regular expression being exercised over all of your text.

The real magic happens in the call to stringByReplacingMatchesInString. This method returns a new string without mutating the old string. Then the method sets the new string on the UITextView so the user can see the results.

Still in SearchViewController, find highlightText and replace it with the following:

func highlightText(searchText: String, inTextView textView: UITextView) {
  // 1 
  let attributedText = textView.attributedText.mutableCopy() as NSMutableAttributedString
  // 2
  let attributedTextRange = NSMakeRange(0, attributedText.length)
  attributedText.removeAttribute(NSBackgroundColorAttributeName, range: attributedTextRange)
  // 3
  if let regex = NSRegularExpression(options: self.searchOptions!) {
    let range = NSMakeRange(0, countElements(textView.text))
    let matches = regex.matchesInString(textView.text, options: .allZeros, range: range)
   // 4
    for match in matches as [NSTextCheckingResult] {
      let matchRange = match.range
 
      attributedText.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellowColor(), range: matchRange)
    }
  }
  // 5
  textView.attributedText = attributedText.copy() as NSAttributedString
}

Here’s a step-by-step explanation of the above code:

  1. First, get a mutable copy of the textview’s attributedText.
  2. Then create an NSRange for the entire length of the text, and remove any background color text attributes that already exist within it.
  3. As with find and replace, next create a regular expression using your convenience initializer and fetch an array of all matches for the regular expression within the textview’s text.
  4. Loop through each match (casting them as NSTextCheckingResult objects), and add a yellow colour background attribute for each one.
  5. Finally, update the UITextView with the highlighted results.

Build and run your app and try searching on some various words and groups of words! You’ll see the search term highlighted throughout your text, as shown in the image below:

iRegex text highlighting

Try searching for the word “the” using various options and see the effects. Notice, for example, that when using whole words, the ‘the’ in ‘them’ does not highlight.

Also, test out the search and replace functionality to see that your text strings are replaced as expected, and try both the ‘match case’ and ‘whole words’ options.

Highlighting and replacing text are both great. But how else can you effectively use regular expressions in your apps?

Data Validation

Many apps will have some kind of user input, such as a user entering their email address or phone number. You’ll want to perform some level of data validation on this user input, both to ensure data integrity and to inform the user if they’ve made a mistake entering their data.

Regular expressions are perfect for many kinds of data validation, since they are excellent at pattern matching.

There are two things you need to add to your app: the validation patterns themselves, and a mechanism to validate the user’s input with those patterns. To make things easy for the user, all of the validations in your app will be case-insensitive, so you can just use lower-case in your patterns.

As an exercise, try to come up with the regular expressions to validate the following text strings (don’t worry about case sensitivity):

  • First name — should consist of standard English letters and between one and ten characters in length.
  • Middle initial — should consist of a single English letter.
  • Last name — should consist of standard English letters plus the apostrophe (for names such as O’Brian) and between two and ten characters in length.
  • Date of birth – should be one of the following date formats: dd/mm/yyyy, dd-mm-yyyy, or dd.mm.yyyy, and fall between 1/1/1900 and 31/12/2099.

Of course, you can use the iRegex playground to try out your expressions as you develop them.

How did you do with coming up with the required regular expressions? If you’re stuck, just go back to the cheat sheet above and look for the bits that will help you in the scenarios above.

The spoiler below shows the regular expressions you’ll be using. But try to figure them out yourself first, and check your results before reading further!

Solution Inside SelectShow>

Open up SignUpViewController.swift and replace viewDidLoad with the following code:

override func viewDidLoad() {
  super.viewDidLoad()
 
  textFields = [ firstNameField, middleInitialField, lastNameField, dateOfBirthField ]
 
  let patterns = [ "^[a-z]{1,10}$",      // First name
                   "^[a-z]$",            // Middle Initial
                   "^[a-z']{2,10}$",     // Last Name
                   "^(0[1-9]|1[012])[-/.](0[1-9]|[12][0-9]|3[01])[-/.](19|20)\\d\\d$" ]  // Date of Birth
 
  regexes = patterns.map {
    NSRegularExpression(pattern: $0, options: .CaseInsensitive, error: nil)
  }
}

This creates an array of the text fields in the view controller and an array of string patterns. It then uses Swift’s map function to create an array of NSRegularExpression objects, one for each pattern.

To create the regular expression to validate the first name, you first match from the beginning of the string, then you match a range of characters from a-z and then finally match the end of the string ensuring that it is between 1 to 10 characters in length.

The next two patterns, middle initial, and last name, follow the same logic. In case of the middle initial, you don’t need to specify the length — {1} — since ^[a-z]$ matches on one character by default.

Note that you’re not worrying about case sensitivity here — you’ll take care of that when instantiating the regular expression.

For the date of birth, you have a little more work to do. You match on the start of the string, then for the month portion you have a capturing group to match one of 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11 or 12, followed by another capturing group to match either -, / or ..

For the day portion, you then have another capturing group to match one of 01, 02, … 29, 30, or 31, followed by capturing group to match either -, / or .

Finally, there is a capturing group to match either 19 or 20, followed by any two numeric characters.

You can get very creative with regular expressions. There are other ways to solve the above problem, such as using \d instead of [0-9]. However, any solution is perfectly fine as long as it works!

Note: In real-world usage, you likely wouldn’t use a regular expression to verify a date (and certainly not to check it’s within a particular date range). Instead, you’d probably want to use an NSDateFormatter to attempt to parse a date from a string, and then compare your parsed NSDate against reference dates.

Now that you have the patterns, you need to validate the entered text in each of the text fields.

Still in SignUpViewController.swift, find validateTextField and replace the implementation with the following:

func validateTextField(textField: UITextField) {
  let index = find(textFields, textField)
  if let regex = regexes[index!] {
    let text = textField.text.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
    let range = NSMakeRange(0, countElements(text))
 
    let matchRange = regex.rangeOfFirstMatchInString(text, options: .ReportProgress, range: range)
 
    let valid = matchRange.location != NSNotFound
 
    textField.textColor = (valid) ? UIColor.trueColor() : UIColor.falseColor()
  }
}

This is very similar to what you did in SearchViewController.swift. You start off by grabbing the relevant regex from the regexes array, trimming any whitespace off the user’s input for the textfield, and then creating a range for the whole text.

To actually check for a match, the codes tests the result of rangeOfFirstMatchInString(_:options:range:). This is probably the most efficient way to check for a match, since this call exits early when it finds the first match. However, there are other alternatives such as numberOfMatchesInString(_:options:range:) if you need to know the total number of matches.

Run the project now, click on the Contacts button at the bottom right, and try entering some information into the sign up form. When you complete each field, you should see its text turn green or red depending on whether or not it’s valid, as in the screenshot below:

iRegex Validation

The above code uses stringByTrimmingCharactersInSet to remove leading or trailing spaces in the user’s input – otherwise the patterns above will fail to validate if any whitespace was present. This is a textbook use of stringByTrimmingCharactersInSet, but seeing as how this is a regular expressions tutorial, it’s interesting to take a brief diversion and consider how you could tackle whitespace using more regular expressions instead.

Trimming Whitespace: An Interlude

regex-whitespace

In a case like this, you have two options: either update the patterns to account for leading and trailing spaces, or create and apply another pattern to take out leading and trailing spaces before applying those validation patterns. The second approach keeps the validation patterns simple and concise, and you can refactor it into its own method. All you need is a regex!

Can you think of a regular expression to find spaces at the start and end of a string? Try it yourself before checking the spoiler below:

Solution Inside SelectShow>

Great. The pattern ^\s+ will find leading whitespace and \s+$ will find trailing whitespace. Now you have a regular expression that matches whitespace, it’s time to put it to use. At the very bottom of SignUpViewController.swift, outside the curly braces of the class definition, add the following code:

extension String {
  func stringByTrimmingLeadingAndTrailingWhitespace() -> String {
    let leadingAndTrailingWhitespacePattern = "(?:^\\s+)|(?:\\s+$)"
 
    if let regex = NSRegularExpression(pattern: leadingAndTrailingWhitespacePattern, options: .CaseInsensitive, error: nil) {
      let range = NSMakeRange(0, countElements(self))
      let trimmedString = regex.stringByReplacingMatchesInString(self, options: .ReportProgress, range:range, withTemplate:"$1")
 
      return trimmedString
    } else {
      return self
    }
  }
}

Just as you did for NSRegularExpression, you’re adding an instance method to String here. Before we get into how the method works, change validateTextField to use this new method by changing the line that trims whitespace:

let text = textField.text.stringByTrimmingLeadingAndTrailingWhitespace()

The new string method uses the pattern from above to create a regular expression, and returns a new string with any regex matches replaced with $1. But what does $1 mean?

When you have capture groups in a regular expression (denoted by (round brackets)), you can use $ and a number to reference the contents of a group. Regular expression documentation refers to this as a backreference. The number specifies which capture group you’re referencing.

As an example, given the regular expression:

Customer ID: (\d{3})-(\d{4})

Matched against the following text:

Customer ID: 123-0089

Then the value of $1 would be 123, and the value of $2 would be 0089. Useful stuff!

Going back to the whitespace example, though – won’t $1 just replace the whitespace with itself — since it’s a captured group — which effectively does nothing?

In this case, the ?: inside the parentheses tells the regular expression engine to create a non-capturing group. This means that the matched text isn’t stored in a buffer as it normally would be.

Since the first capturing group is a non-capturing group, the engine doesn’t capture anything — and therefore it’s empty! Thus the engine ends up matching white spaces and replacing them with nothing, which effectively removes the leading and trailing spaces.

Of course, this is something of a contrived use of a capture value. You could in fact (and in real life certainly would!) just use an empty string, "", as your template value!

More on Capture Groups

As a more realistic example, suppose you wanted to alter all your customer ID’s in a file so that the 4-digit part came before the 3-digit part, you want extra space between the number groups, and two hyphens instead of one? More specifically, you’d like:

Bob ID: 143-5546
Ellen ID: 447-6091
Piper ID: 314-1596
Easy ID: 217-1828

To become:

Bob ID: 5546 -- 143
Ellen ID: 6091 -- 447
Piper ID: 1596 -- 314
Easy ID: 1828 -- 217

How would you do it? The spoiler below has the answer. But try it yourself first.

Solution Inside SelectShow>

You can see the effects of this example at the end of the included playground.
Note that the regular expression here allows for an arbitrary amount of white space between the name and ID and also between “ID:” and the actual numerical ID.

If you are still struggling to make sense out of non-capturing, capturing and back referencing, try out the following different scenarios in the playground to see what the results are (hint: you can use the `replaceMatches` function):

  • replace the whitespace pattern above with “(^\\s+)|(\\s+$)” and template with “BOO”
  • replace the whitespace pattern above with “(?:^\\s+)|(\\s+$)” and template with “$1BOO”
  • replace the whitespace pattern above with “(?:^\\s+)|(\\s+$)” and template with “$2BOO”

Handling Multiple Search Results

You haven’t yet implemented the Bookmark button found on the navigation bar. When the user taps on it, the app should highlight any date, time or location strings in the text.

Open up SearchViewController.swift in Xcode, and find the following implementation for the Bookmark bar button item:

//MARK: Underline dates, times, and locations
 
@IBAction func underlineInterestingData(sender: AnyObject) {
  underlineAllDates()
  underlineAllTimes()
  underlineAllLocations()
}

The method above calls three other helper methods to underline dates, times and locations in the text. If you look at the implementation of each of the helper methods above, you will see they are empty!

First, fill out the implementations of each method. Replace them all with the following:

func underlineAllDates() {
  if let regex = NSRegularExpression.regularExpressionForDates() {
    let matches = matchesForRegularExpression(regex, inTextView: textView)
    highlightMatches(matches)
  }
}
 
func underlineAllTimes() {
  if let regex = NSRegularExpression.regularExpressionForTimes() {
    let matches = matchesForRegularExpression(regex, inTextView: textView)
    highlightMatches(matches)
  }
}
 
func underlineAllLocations() {
  if let regex = NSRegularExpression.regularExpressionForLocations() {
    let matches = matchesForRegularExpression(regex, inTextView: textView)
    highlightMatches(matches)
  }
}

Each of these methods calls a factory method on NSRegularExpression to create the appropriate regular expression. These don’t exist yet, but it’s a convenient place to encapsulate this behavior. The methods then find matches, and call highlightMatches to color and underline each string in the text. Check out its implementation if you’re interested to see how it works.

Now to populate those regular expression methods. Open up RegexHelpers.swift and add the following placeholders inside the NSRegularExpression extension:

class func regularExpressionForDates() -> NSRegularExpression? {
  let pattern = " "
  return NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: nil)
}
 
class func regularExpressionForTimes() -> NSRegularExpression? {
  let pattern = " "
  return NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: nil)
}
 
class func regularExpressionForLocations() -> NSRegularExpression? {
  let pattern = " "
  return NSRegularExpression(pattern: pattern, options: .allZeros, error: nil)
}

Now it’s your job to complete these patterns! Here are the requirements:

Date Requirements:

  • xx/xx/xx or xx.xx.xx or xx-xx-xx format. Day, month and year placement is not important since the code will just highlight them. Example: 10-05-12.
  • Full or abbreviated month name (e.g. Jan or January, Feb or February, etc.), followed by a one or two digit number (e.g. x or xx). The day of the month can be ordinal (e.g. 1st, 2nd, 10th, 21st, etc.), followed by a comma as separator, and then a four-digit number (e.g. xxxx). There can be zero or more white spaces between the name of the month, day and year. Example: March 13th, 2001

Time requirements:

  • Find simple times like “9am” or “11 pm”: One or two digits followed by zero or more white spaces, followed by either lowercase “am” or “pm”.

Location requirements:

  • Any word at least one character long, immediately followed by a comma, followed by zero or more white spaces followed by any capitalized English letter combination that is exactly 2 characters long. For example “Boston, MA”.

You can use the playground to try these out. See if you can sketch out the needed regular expressions!

Here’s three sample patterns you can try. Replace the empty pattern for of regularExpressionForDates with the following:

let pattern = "(\\d{1,2}[-/.]\\d{1,2}[-/.]\\d{1,2})|(Jan(uary)?|Feb(ruary)?|Mar(ch)?|Apr(il)?|May|Jun(e)?|Jul(y)?|Aug(ust)?|Sep(tember)?|Oct(ober)?|Nov(ember)?|Dec(ember)?)\\s*(\\d{1,2}(st|nd|rd|th)?+)?[,]\\s*\\d{4}"

This pattern has two parts separated by the | (OR) character. That means either the first part or the second part will match.

The first part reads: (\d{1,2}[-/.]\d{1,2}[-/.]\d{1,2}). That means two digits followed by one of - or / or . followed by two digits, followed by - or / or ., followed by a final two digits.

The second part starts with (Jan(uary)?|Feb(ruary)?|Mar(ch)?|Apr(il)?|May|Jun(e)?|Jul(y)?|Aug(ust)?|Sep(tember)?|Oct(ober)?|Nov(ember)?|Dec(ember)?), which will match a full or abbreviated month name.

Next up is \\s*\\d{1,2}(st|nd|rd|th)? which will match zero or more spaces, followed by 1 or 2 digits, followed by an optional ordinal suffix. As an example, this will match both “1” and “1st”.

Finally [,]\\s*\\d{4} will match a comma followed by zero or multiple spaces followed by a four-digit number for the year.

That’s quite the intimidating regular expression! However, you can see how regular expressions are concise and pack a lot of information — and power! — into a seemingly cryptic string.

Next up are the the patterns for regularExpressionForTimes and regularExpressionForLocations. Fill in the blank patterns with the following:

// Times
let pattern = "\\d{1,2}\\s*(pm|am)"
 
// Locations
let pattern = "[a-zA-Z]+[,]\\s*([A-Z]{2})"

As an exercise, see if you can explain the regular expression patterns based on the specifications above.

Build and run the app and tap on the Bookmark icon. You should see the link-style highlighting for dates, times, and locations, as shown below:

iRegex Date, Time, and Location Highlighting

While it’s fine for this example, can you see why the regular expression for time might not be right for more general searching? As it stands it would not match the time 3:15pm and it would match 28pm.

Here’s a challenging problem! Figure out how to rewrite the regular expression for time so that it matches a more a general time format.

Specifically, your answer should match times in the format ab:cd am/pm for a standard 12-hour clock. So it should match: 11:45 am, 10:33pm, 04:12am but not 2pm, 0:00am 18:44am 9:63pm or 7:4 am. There should be at most one space before the am/pm. By the way, it’s acceptable if it matches the 4:33am in 14:33am.

One possible answer appears below, but try it yourself first. Check the end of the accompanying playground to see it in action.

Solution Inside SelectShow>

Where To Go From Here?

Here is the final example project that you developed in the above tutorial.

Congratulations! You now have some practical experience with using regular expressions.

Regular expressions are powerful and fun to work with — they’re a lot like solving a math problem. The flexibility of regular expressions gives you many ways to create a pattern to fit your needs, such as filtering input strings for white spaces, stripping out HTML or XML tags before parsing, or finding particular XML or HTML tags — and much more!

There are a lot of real-world examples of strings that you can validate with regular expressions.
As a final exercise, try to untangle the following regular expression that validates an email address:

[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?

It looks like a jumble of characters at first glance, but with your new-found knowledge (and the helpful links below) you’re one step closer to understanding it and becoming a master of regular expressions!

Here is a short list of some useful resources about regular expressions:

And in case you missed the links earlier, check out these resources we’ve prepared for you:

I hope you enjoyed this NSRegularExpression tutorial, and if you have any comments or questions, please join the forum discussion below!

NSRegularExpression in Swift Tutorial is a post from: Ray Wenderlich

The post NSRegularExpression in Swift Tutorial appeared first on Ray Wenderlich.

Video Tutorial: Collection Views Part 2: Custom Cells

Creating a Cross-Platform Multiplayer Game in Unity — Part 1

$
0
0

Learn to make a cross platform game using Google Play Services!

Learn to make a cross platform game using Google Play Services!

What? You want to make a multiplayer game? Are you nuts!? These things are hard! Seriously, when it comes to game development, few things drive a developer crazier than multiplayer: it’s a pain to test, difficult to debug, can harbor bugs that are nearly impossible to reproduce — until, of course, you demo your game for the press — and, even when all your code is working perfectly, can still break thanks to flaky network connections.

In spite of the challenge of creating multiplayer games, they are totally worth it. No AI, no matter how clever, will have the same ingenuity as a human opponent, nor will trash talking an AI be as fun as trash talking your friends. And I haven’t heard of any player starting a relationship with an AI that they met online…movies starring Joaquin Phoenix notwithstanding. :]

In this tutorial, you’ll create a networked multiplayer game that works across different brands of devices. When you show off an iOS and Android game playing together, your co-workers are going to think you’re some kind of crazy wizard!

But try not to let it go to your head.

But try not to let it go to your head.

In the first part of this tutorial series, you will learn the following:

  • How to configure and install Google Play Games Services
  • How to setup an iOS project to work with Google Play Games Services.
  • How to sign in a user.
  • How to sign out a user.

You should have the following prerequisites before you move ahead with this tutorial:

  • A basic understanding of Unity and C#. All of your coding is going to be in C# — no Objective-C, Swift, or Java here!
  • Some experience running a Unity game on an actual device.
  • At least two devices to develop on: either iOS, Android, or one of each. Testing a multiplayer game with just one device tends to be rather difficult. :]
  • A Google Play Developer account, which you can sign up for at https://play.google.com/apps/publish/. Even if you’re going to test this on iOS only, you’ll still need one of these accounts. Obtaining a Play Developer account will set you back $25 — but you’ll consider it worthwhile once you experience your game in action!
Note: For those of you considering trying this tutorial using just the simulators, you will find yourself tearing out your hair. The simulators will not work. You will need two devices. Ideally, you should have one iOS device and one Android device, but in a pinch, two of the same devices will work fine.

If you are just starting out with Unity, it would be well worth your time to work through Christopher LaPollo’s excellent Unity 2D tutorial series on this site before you tackle an advanced-level Unity tutorial like this one.

Getting Started

It’s time to get familiar with the game you’re going to turn into a multiplayer showcase of awesomeness. Download the starter project for Circuit Racer, The Unity Version here, then unzip the file to extract the project.

In Unity, select File\Open Project. Select Open Other… from the dialog box that appears, and then select the Circuit Racer folder you just unzipped:

Opening the Unity Project

Start by loading up the Main Menu scene. From the Assets panel, select MenuStuff and then double-click MainMenu to open up the main menu.

Hit the Play button to try out the game within Unity; select the single player experience, and watch as your car performs death-defying feats as it circles around the track before time runs out!

Gameplay

Oh, by the way — watch out for those crates. :]

Running on a Real Device

It’s time to try this on an actual device. Since you’re reading this on RayWenderlich.com, I’ll assume you’ll want to run this on your favorite iOS device. Readers on RoyWenderlich.com, the Android tutorial site run by Ray’s evil twin, probably have a different experience in mind:

A rare photo of Ray and Roy together, before what has only been referred to as "the incident".

A rare photo of Ray and Roy together, before what has only been referred to as “the incident”.

You’ll first need to change the Bundle Identifier of your project to something a little less generic. Go to File\Build Settings, then select iOS. The iOS platform should already be selected as your target platform, but if it isn’t, click the Switch Platform button with iOS selected.

Click Player Settings and the PlayerSettings should appear in your Inspector panel. In the Other Settings area, look for the Bundle Identifier and change it from com.example.CircuitRacerUnity to something that’s unique to you and appropriate for your provisioning profile, like com.<your company name>.<your user name>.CircuitRacerUnity:

Setting your Bundle ID

Now connect your physical device to your Mac. If you haven’t run an Xcode project on a device before, then read up on what’s required in this tutorial first.

Head back to the Build Settings dialog box, which should still be open, and select Build and Run. Unity will ask you to save the generated Xcode project; give it a fun and original name such as CircuitRaceriOS.

Once Unity has finished exporting the project, it will open up the newly created project in Xcode. Xcode will take a few moments to process the file, and it will then run the game on your actual device! However, sometimes Xcode forgets to run the project for you. If this happens, just click the Run button to remind Xcode why it opened in the first place. :]

Note: If you’re running an older version of Unity and a newer version of Xcode, Unity might have trouble opening up Xcode after it exports the file to an iOS project. If that happens to you, either upgrade your version of Unity to the latest version, or simply open Xcode, open your project manually, and then continue running your project from there.

In addition to the on-screen gamepad support, the game also supports tilt controls — something you probably wouldn’t have noticed when you were playing the game within Unity.

Understanding Circuit Racer

The Circuit Racer game in this tutorial is a mashup of two different tutorials:

This tutorial won’t go into too much detail about how the controls work, how the game knows you’ve completed a circuit, or how the car turns, since that’s been covered by the two aforementioned tutorials and it’s not directly relevant to your goal of turning this into a multiplayer game. Still, feel free to browse through the code, in particular the GameController, CarController, and AnalogControl classes, which will give you a general idea of how things work.

Note: At the time of this writing, Unity 4.6 was still in beta, so this project is still using the older GUI system supported by Unity 4.5 and earlier.

As long as you’re trying out the game on your device, you might as well see what the multiplayer option has to offer. Head back to the main menu and click on the Multiplayer button:

TK1DoesNothing

Well, that was underwhelming, to say the least. But the good news is that you’re going to fix it all up!

Introducing Google Play Game Services

Although negotiating network connections between devices behind various firewalls might sound like fun to some, most developers choose to use third-party libraries to handle many of the “complicated but not fun” tasks of initiating multiplayer games. There are lots of choices for third-party frameworks out there, but this tutorial uses Google Play game services for the following reasons:

  1. It’s free.
  2. There’s a plug-in available for Unity.
  3. The plug-in is also free.

TKCerealGameCenter

Yes, skeptical cereal-eating man, it’s true. Game Center does provide some really good support for real-time multiplayer, but it’s only available for iOS devices. In this day and age, there’s probably a very good chance that even the most hardcore iOS fan has an Android-using friend or two that they’d like to play games with.

One of the advantages of using Unity to make your 2D game is that you can easily export your game to both Android and iOS, and it would be awfully nice if your multiplayer solution followed suit. In the interest of full disclosure, I work for Google and I occasionally play Towerfall with the people who made Google Play game services, so I’m just a teeny bit biased.

Google Play game services includes a number of features for game developers including achievements, leaderboards, saving your game in the cloud, creating quests, and, most importantly for this tutorial, support for turn-based and real-time multiplayer. You won’t implement any of these features right now except for real-time multiplayer. However, once you go through the work to integrate Play services into your game, it’s probably worth checking out some of the other features to see what they can offer you.

Setting Up Google Play Game Services

Okay, I’ll level with you here: there’s a bit of setup required to get things going. But stick with me — once you get through this part, you can go back to writing glorious code!

Registering Your Game

Before you do anything else, you’ll need to register your application with the Google Play Developer Console and get yourself a Client ID. This identifies your app with the service and lets you perform important actions like signing in the user.

Open https://play.google.com/apps/publish/ in your browser. If you don’t have a Google Play developer account, you can set one up at this point:

TK1CreateNewAccount

If you have an account and log in, you will see a screen that either looks like this:

TK1NoApps

…or one that shows your existing applications:

TK1ListOfApps

Either way, get started by clicking the little gamepad icon on the left:

TK1ClickIcon

Next, click the Set Up Google Play game services button like so:

Tk1NoGameServices

If you’ve set up Play game services before, click on the Add a new game button at the top of the screen:

This is a caption

A dialog box will appear with two tabs: the first tab is relevant if you aren’t yet using any Google APIs in your game, and the second is for the case where you’re working with an existing game that already uses Google APIs. You’re definitely in the first situation, so leave the dialog on the first tab.

Name your project appropriately; you can call it whatever you like, as it doesn’t need to be unique. Choose a game category as well; “Racing” is probably a good choice, but hey, it’s a free country. When you’re done, your dialog should look like the following:

Add Game Services Dialog

Click Continue, and you’ll find yourself on the Game Details page, where you can add a name, a description and some graphics to your game. This are things you’ll have to handle before you publish your game in real life, but for the purposes of this tutorial you can skip filling out these items for now.

Instead, you’ll set up your Client ID. Click on the Linked Apps tab on the left side as shown below:

Select Linked Apps

Next, select iOS from the given options like so:

TK1SelectiOS

In the next page that appears, leave Name set to whatever is prefilled for you and leave both iPad and iPhone set to YES. Next, enter the Bundle ID you assigned to your game back in the Build Settings step.

Note: Changing your bundle identifier later can be a huge pain. To make your life easy, ensure you have a Bundle ID that matches what you entered in Unity and is unique to you and your organization.

Leave the iTunes App ID blank; you don’t need this for now. Your page should now look like the following:

Linking an iOS App

Below that, you have two separate options in the Multiplayer Settings section to enable turn-based and real-time multiplayer support for your game. But which one do you want?

Turn-Based vs. Real-Time: A Quick Primer

Google Play game services, along with many other game frameworks, supports turn-based and real-time games. Here’s a quick introduction to what each of these mean:

A real-time game requires that everybody be present and playing at the same time; it generally applies to games where all players play at once and see each other’s moves as they happen. Examples of this include first-person shooters, multiplayer online battle arenas, and most sports games.

Turn-based games, on the other hand, are generally played by one person at a time, usually while the other players aren’t active and — gasp! — might not even have their phones with them. With these types of games it might take a day or two for players to take their next turn; as a result, this option is most useful for games that are slower, more contemplative, or involve situations where a single player’s turn can take a long time. Words With Friends and Draw Something are prototypical examples of turn-based games.

Pop quiz, hotshot: you’re making a Texas Hold’em poker game. Should this be turn-based, or real-time? Check your answer below.

Solution Inside: Answer SelectShow>

In your racing game, you’ll have two players racing against each other on the same playing field, so turn the Real-time multiplayer setting to ON.

Click the Save and continue button at the top of the screen. Next, you’ll see a screen like the one below where you’re asked to authorize your app:

Authorize your App Now

This is where you create your client ID, which is essentially an identifier that identifies your app on Google’s servers. Click the Authorize your app now button.

The first dialog that appears lets you add your company’s name, logo, and home page; these settings determine how your game appears in the various screens that prompt the user to sign in to your application:

Auth Dialog Branding

For this tutorial, just click Continue.

The next dialog asks for your bundle identifier and App Store ID. It’s been pre-populated with the same information you added previously in Step 1 of the dialog, so you shouldn’t have to do anything here. Simply confirm everything is correct, then click Create client:

TK1AuthPart2

Once the page finishes processing, you will see a giant string ending with apps.googleusercontent.com — this is your Client ID:

Your Client ID

Copy and paste this string into a text file somewhere, as you’re going to need it later.

Adding Testers

There’s just one last step before you can get coding. Click the Testing tab on the left side of the screen to go the Testing panel. In the Testing Access section you should see your email address, and possibly those of your other team members like so:

The Testing Panel

When Play game services are in an unpublished state, they can only by used by accounts that are listed here. Since you’re going to be testing with a few other Google accounts — for your multiplayer game, you’re going to need at least two — click on the Add testers button and add the addresses you’d like to whitelist:

Adding new Testers

Give yourself a pat on the back — you’re all done with the Google Play setup! :]

More fun than taxes!

Installing the Unity Plugin

Now you need to install the plugin that lets Unity talk to Play Games. Go to https://github.com/playgameservices/play-games-plugin-for-unity and click the “Download ZIP” button on the right:

This button either means you're downloading something from the cloud, or it's raining deadly arrows. Either way, you should probably stay indoors.

This button either means you’re downloading something from the cloud, or it’s raining deadly arrows. Either way, you should probably stay indoors.

Once that’s done, unzip the package you downloaded and head back to Unity. Hello, Circuit Racer! Did you miss us? :] To install the plugin, select Assets\Import Package\Custom Package as follows:

Importing a custom package

Browse to the folder you created, then navigate to the contained current-build folder. Select the file that ends with unity package.

Note: At the time of this writing, the package is named GooglePlayGamesPlugin-0.9.10.unitypackage, but the version number will likely have changed by the time you’re reading this.

Import a custom Unity Package 2

You should now see an Importing package dialog box; all options should be already selected, but if not, simply click the All button. Finally, click Import:

Import All the Things!

You now have a couple of exciting new folders, and a new “Google Play Games” menu item. If you don’t see the menu item, click on the menu bar to force Unity to refresh its contents. Select Google Play Games\iOS Setup from the menu and you’ll see a dialog box where you need to provide two pieces of information:

iOS Setup Dialog in Unity

  • The “Client ID” is the big string you copied earlier in this tutorial.
  • The “Bundle ID” is the Bundle ID you’ve been using all along.

Enter the two pieces of information into the dialog and click Setup. You should receive an alert that all is well, at which point you can close the dialog.

Oh shoot — did you forget to copy down the Client ID earlier? No problem; here’s how to find your Client ID again:

  1. Go back to the Play Developer console at https://play.google.com/apps/publish/.
  2. Click on Game Services (the little controller icon) on the left.
  3. Click on your game.
  4. Click on “Linked Apps”.
  5. Click on the entry for the iOS version.
  6. At the bottom, you should see your Client ID next to a label that says, OAuth2 Client ID.
    Finding your Client ID again
  7. Copy this value to the clipboard and continue on with the tutorial!

Build and run your project in Xcode (Command-B is a handy keyboard shortcut). If everything goes well, Unity will export your application to Xcode, which will then compile and run your application on your device, and..whoops. What happened?

Build Error

There are a few modifications to make to your Xcode project before you can run your game on a physical device. Fortunately, the post-build script that Unity runs brings up a dialog box that tells you exactly what you need to do to get things working:

That doesn’t look too serious; you’ll tackle them in order.

Adding Missing Frameworks

In Xcode, select the Unity-iPhone project on the left, and then the Unity-iPhone target in the main panel. Go to Build Phases, expand the Link Binary With Libraries section on the right, and then click the + button to bring up the dialog where you can add all the frameworks you need for this project to run:

Adding Frameworks

The dialog box should list the frameworks you need to include, but at the time of this writing, you’ll need to add the following frameworks:

  • AddressBook
  • AssetsLibrary
  • CoreData
  • CoreTelephony
  • CoreText
  • libc++.dylib
  • libz.dylib
  • Security

To make your life easier, you can hold down the Command button to select several frameworks at once.

Adding More Frameworks

Now you’ll need to download the missing SDKs. Head to https://developers.google.com/games/services/downloads/. Download and unzip both the the Games C++ SDK and the Google+ iOS SDK as shown below:

Download these two

Note: Be careful: there is an iOS Games SDK listed on the same page. You don’t want that one, even though you’re building an iOS game. You need to download and use the C++ SDK instead.

Also note that clicking the link to download the Google+ iOS SDK takes you to a separate download page where you need to click the Download zip… link at the bottom of the screen, which in turn takes you to another page where you need to click on the Download the iOS+ SDK button to finally download the package. They, uh… really like having you click on things, apparently! :]

Navigate into the gpg-cpp-sdk folder you just unzipped, go into the iOS subfolder and drag the gpg.framework and GooglePlayGames.bundle files directly into Xcode. In the resulting dialog box, ensure the Unity-iPhone target is checked. It’s up to you whether or not to select the Copy items if needed checkbox; personally, I like doing it, but if you’re the type of person that likes to have all your third-party libraries in one location, you can leave it unchecked.

Next, navigate into the google-plus-ios-sdk folder and drag the GoogleOpenSource.framework, GooglePlus.bundle and GooglePlus.framework files into Xcode where you should see them added to the list of frameworks:

After SDKs have been added

Adding the -ObjC Flag

Finally, head back to XCode and select the Build Settings tab for your target. Find the Other Linker Flags option — it’s in the Linking section. Double click the part that reads: -weak_framework CoreMotion -weak-ISystem, then click the + button at the bottom of the dialog to add the -ObjC flag. Your project should look like this once you’re done:

After Obj-C

Note: Don’t skip the -ObjC step above; it’s a particularly insidious error because your code will compile and build just fine without it. But when you try to make a call, your project will crash with a mysterious unrecognized selector message.

Now you can finally go back and run your game again! Click the Run button, and if all has gone according to plan, you should see your Circuit Racer running on your device.

Hold on -- this game looks exactly the same as before!

True, the game looks the same as before — but there’s a whole slew of code and libraries underneath just waiting for you to make use of them! :]

Signing in the Player

Your first task will be to sign in the user when they click the multiplayer button. If you can get that piece working, you know you’ve set up the game services correctly and you can move on to the more interesting parts of developing a multiplayer game.

Create a new class to handle some of your multiplayer code. Double-click the Scripts folder in the Unity Assets panel. Then right-click the assets panel, select Create\C# Script and name the new script MultiplayerController. Double-click to edit the script in your code editor of choice.

Add the two following imports to the beginning of the file:

using GooglePlayGames;
using GooglePlayGames.BasicApi.Multiplayer;

Next, find the following line:

public class MultiplayerController : MonoBehaviour {

…and replace it with the following:

public class MultiplayerController {

Then delete the two boilerplate Start() and Update() methods.

If you’re fairly new to Unity, you might not recognize what’s happening here. You’re creating a piece of code that won’t be attached as a component to a Game Object; instead, you’re creating the object purely in code. With this approach you can’t use MonoBehavior as your base class; you can either use ScriptableObject as your base class, or, as in this situation, nothing at all.

Next, add the following content your class so that it’s created as a singleton:

private static MultiplayerController _instance = null;
 
private MultiplayerController() {
    // Code to initialize this object would go here
}
 
public static MultiplayerController Instance {
    get {
        if (_instance == null) {
            _instance = new MultiplayerController();
        }
        return _instance;
    }
}

Not sure what a singleton is? It’s a common coding pattern where you create one (and only one) instance of an object that can be easily referenced by other classes in your project.

In Unity, creating a class as a singleton can be useful when you need to use it across multiple scenes. The UserPreferences object is a good example, because it’s an object you need to access from the main menu, the game, and many other sub-menu scenes. Similarly, you’ll need to access MultiplayerController from both the main menu as well as in the game, so it helps to implement this as a singleton as well.

In order to access the object in the future, you won’t look for specific game objects or create new objects. Instead, you’ll call the static MultiplayerController.Instance method, which either creates the singleton if it doesn’t yet exist, or returns the one that’s already been instantiated.

Note: Want to find out more about singletons? Check out Eli Ganem’s excellent iOS Design Patterns tutorial on this site for more detail.

Some coding aficionados aren’t terribly fond of singletons, and claim that singletons suffer from overuse. That’s a debate for another time — right now, you’ve got a game to make! :]

Next, modify the code inside MultiplayerController() as follows:

private MultiplayerController() {
    PlayGamesPlatform.DebugLogEnabled = true;
    PlayGamesPlatform.Activate ();
}

The first line sets your logs to be nice and verbose, and the second line tells the PlayGamesPlatform to initialize itself.

Next, add the following method to your class:

public void SignInAndStartMPGame() {
    if (! PlayGamesPlatform.Instance.localUser.authenticated) {
        PlayGamesPlatform.Instance.localUser.Authenticate((bool success) => {
            if (success) {
                Debug.Log ("We're signed in! Welcome " + PlayGamesPlatform.Instance.localUser.userName);
                // We could start our game now
            } else {
                Debug.Log ("Oh... we're not signed in.");
            }
        });
    } else {
        Debug.Log ("You're already signed in.");
        // We could also start our game now
    }
}

This is your basic sign-in logic. Calling PlayGamesPlatform.Instance brings up Google Play’s own singleton instance. The first if statement checks to see if the local player is signed in. If not, then you call the platform’s Authenticate method, which attempts to sign the user in.

Also notice that the argument you’re passing to Authenticate is, itself, a function. Since Authenticate might take a while to execute, you pass in a callback function to execute when Authenticate has completed; this is similar to blocks in Objective-C.

Now that you have a method to sign the user in, you need a place from which to call it.

Open up MainMenuScript.cs, found in the MenuStuff folder. Find the following line in OnGUI():

Debug.Log("We would normally load a multiplayer game here");

…and replace it with the following:

MultiplayerController.Instance.SignInAndStartMPGame();

This calls SignInAndStartMPGame() which you created just a moment ago.

Build your project:

Note: When you rebuild your Unity game, you’re often given the option to Replace or Append your project. Make sure to select Append:

Use Append Here

Don’t select Replace, unless you feel like re-doing all those setup steps in the Fixing Your Xcode Project section all over again. I know I don’t!

Once you have built your project, run the app. Click the Multiplayer button once your application launches; you’ll be taken to a screen where you’re asked to sign in. Use one of the accounts you listed as a “Tester” account earlier in the tutorial. Click Accept and you’ll be taken back to Circuit Racer where you should see a “Welcome back!” graphic appear at the top of the screen and a “We’re signed in! Welcome ” message in your console log as follows:

Welcome Back Notification

You’ve signed in — looks like everything is hooked up and authenticating correctly!

Improving the Sign-In Process

Stop the application and start it again in Xcode. Notice that you’re no longer signed in, but when you click the Multiplayer button this time around, you’ll see the “welcome back” message without having to go through the sign-in process. What’s going on?

If you’ve signed in previously, haven’t explicitly signed out, and the game isn’t asking for any new permissions, then Google Play Games assumes it’s okay to sign you in again without showing you another sign-in dialog, which would get pretty annoying over time.

You can take advantage of this fact by “silently” signing the user in when the application starts. Go back to your MultiplayerController.cs script and add the following new method:

public void TrySilentSignIn() {
    if (! PlayGamesPlatform.Instance.localUser.authenticated) {
        PlayGamesPlatform.Instance.Authenticate ((bool success) => {
            if (success) {
                Debug.Log ("Silently signed in! Welcome " + PlayGamesPlatform.Instance.localUser.userName);
             } else {
                Debug.Log ("Oh... we're not signed in.");
             }
        }, true);
    } else {
        Debug.Log("We're already signed in");
    }
}

This looks awfully similar to the code in SignInAndStartMPGame; the difference is that you have a true argument at the end of your Authenticate call, which instructs the Play service to try to sign the user in silently. That is, Google Play will check to see, “Is this a case where I can sign the user in without having to show them a dialog?” If so, it will sign the user in silently.

You’ll need to call this from somewhere. Add the following line to the end of Start() in MainMenuScript:

MultiplayerController.Instance.TrySilentSignIn();

Build and run your project from within Unity; this time, the application should start up and, after a moment or two, you’ll see the welcome back notification and the console text indicating that you’re signed in. Tap the Multiplayer button and you should see a line in your console log noting that you’re already signed in.

Adding a Sign-Out Button

Since you’re knee-deep in the authentication code, you may as well add a sign-out button too. You wouldn’t typically put such a button on the main screen, as people tend to sign out infrequently enough that you could put this in a settings screen somewhere. However, I find it useful to quickly sign in and out of the app while testing during development.

Add the following public variable to the top of your MainMenuScript class:

public Texture2D signOutButton;

This public variable will hold a reference to your button image.

Next, add the following code to the end of OnGUI(), outside of the for loop:

if (MultiplayerController.Instance.IsAuthenticated()) {
    if (GUI.Button(new Rect(Screen.width  - (buttonWidth * 0.75f),
                            Screen.height - (buttonHeight * 0.75f),
                            buttonWidth * 0.75f,
                            buttonHeight * 0.75f), signOutButton)) {
        MultiplayerController.Instance.SignOut();
    }
}

This code checks if there is currently a signed-in user; if so, it displays a button to sign the user out. If the player taps that button, the app calls SignOut() on your MultiplayerController.

Next, you need to implement those two new methods that you’re calling in your MultiplayerController; fortunately, they’re pretty straightforward. Go back to MultiplayerController.cs and add the following two methods:

public void SignOut() {
    PlayGamesPlatform.Instance.SignOut ();
}
 
public bool IsAuthenticated() {
    return PlayGamesPlatform.Instance.localUser.authenticated;
}

Note: Are you wondering why you’re going through the trouble of calling SignOut() in MultiplayerController, which then calls SignOut() on the Play Games platform — instead of simply calling PlayGamesPlatform.Instance.SignOut() directly in your MainMenuScript?

This is because you’re trying to follow the general principle of encapsulation (or “information hiding”) and keep all related logic together in one class, rather than spread it out over many classes. If you wanted to swap out Play Games for a completely different multiplayer service, you’d only have to modify MultiplayerController; you wouldn’t have to change any of your other code.

Go back to Unity and open up the MainMenu scene; click on MainMenuGameObject in the hierarchy panel, and you’ll see that the Main Menu Script now has a Sign Out Button entry that is currently undefined. Click on the circle next to the input text field and select btn-signout from the game’s assets as shown below:


TK1SignOutButtonResized

Build and run your app; test the sign-in and sign-out process a few times to make sure it works consistently. The sign-out option will come in quite handy as you test your app with multiple players down the road.

Where to Go From Here?

The end result of all your hard work is that a lot of the setup work is done and you can finally get around to the fun parts of building a multiplayer game.

In Part 2 of this tutorial series, you’ll tackle the actual multiplayer aspects of the game. You’ll add matchmaking capabilities so you can connect your games client with other players across the room — or across the world. You’ll add the opponent’s car to the racetrack, and you’ll begin to send and receive gameplay messages so that you can get these cars racing!

You can download the completed project for this part over here. Just make sure add your own Google API Client ID.

In the meantime, if you have any questions or comments, please feel free to join in the discussion below!

Creating a Cross-Platform Multiplayer Game in Unity — Part 1 is a post from: Ray Wenderlich

The post Creating a Cross-Platform Multiplayer Game in Unity — Part 1 appeared first on Ray Wenderlich.

Viewing all 4370 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>