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

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.


Viewing all articles
Browse latest Browse all 4399

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>