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

Storyboards Tutorial in iOS 9: Part 2

$
0
0

Square Image

Update 9/22/15: This tutorial was updated to iOS 9, Xcode 7, and Swift 2 by Caroline Begbie. Original post by Tutorial Team member Matthijs Hollemans.

If you want to learn about storyboards, you’ve come to the right place!

In the first part of this storyboards tutorial series, you covered the basics of using Interface Builder to create and connect various view controllers, along with how to make custom table view cells directly from the storyboard editor.

In this second and final part of this storyboards tutorial series, we’ll cover segues, static table view cells, the Add Player scene, and a game picker scene!

We’ll start where we left off last tutorial, so open your project from last time, or download the example code from the previous tutorial first.

OK, let’s dive into some of the other cool features in storyboards!

Introducing Segues

It’s time to add more view controllers to the storyboard. You’re going to create a scene that allows users to add new players to the app.

Open up Main.storyboard and drag a Bar Button Item into the right slot of the navigation bar on the Players scene with your table view. In the Attributes inspector change Identifier to Add to make it a standard + button.

Players Add Button

When the user taps this button, you want the app to pop up a new modal scene for entering details of a new player.

Drag a new Table View Controller into the canvas to the right of the Players scene. Remember that you can double-click the canvas to zoom out so you have more room to work. With the new Table View Controller selected, choose Editor\Embed in\Navigation Controller.

Here’s the trick: Select the + button that you just added on the Players scene and ctrl-drag to the new Navigation Controller. Release the mouse button and a small popup menu shows up. Select present modally from the popup menu:

PresentModally

Reminder: You can’t add to or modify the contents of a storyboard when zoomed out. If you’re having issues creating the segue, try double clicking to zoom back in!

This places a new arrow between the Players scene and the Navigation Controller:

Modal relationship

This type of connection is known as a segue (pronounce: seg-way) and represents a transition from one scene to another. The storyboard connections you’ve seen so far were relationships and they described one view controller containing another. A segue, on the other hand, changes what is on the scene. Segues are triggered by taps on buttons, table view cells, gestures, and so on.

The cool thing about using segues is that you don’t have to write any code to present the new scene, nor do you have to hook up your buttons to IBAction methods. What you just did, dragging from the Bar Button Item to the next scene, is enough to create the transition. (Note: If your control already had an IBAction connection, then the segue overrides that.)

Run the app and press the + button. A new table view will slide up the scene.

AppPresentModally

This is a so-called “modal” segue. The new scene completely obscures the previous one. The user cannot interact with the underlying scene until they close the modal scene first. Later on you’ll also see “show” segues that push new scene on the navigation stack of a Navigation Controller.

The new scene isn’t very useful yet – you can’t even close it to go back to the main scene. That’s because segues only go one way – so while it can go from the Players scene to this new one, it can’t go back.

Storyboards provide the ability to ‘go back’ with something called an unwind segue, which you’ll implement next. There are three main steps:

  1. Create an object for the user to select, usually a button.
  2. Create an unwind method in the controller that you want to return to.
  3. Hook up the method and the object in the storyboard.

First, open Main.storyboard and select the new Table View Controller scene. Change the title of the scene to Add Player (by double-clicking in the navigation bar). Then add two Bar Button Items, one to each side of the navigation bar. In the Attributes inspector, set the System Item property of the button to the left to Cancel, and the one on the right to Done.

Add Player Buttons

Next, add a new file to the project using the Cocoa Touch Class template – name it PlayerDetailsViewController and make it a subclass of UITableViewController. To hook this new class up to the storyboard, switch back to Main.storyboard and select the Add Player scene. In the Identity inspector set its Class to PlayerDetailsViewController. I always forget this very important step, so to make sure you don’t; I’ll keep pointing it out.

Now you can finally create the unwind segue. In PlayersViewController.swift (not the detail controller), add the unwind methods at the end of the class:

@IBAction func cancelToPlayersViewController(segue:UIStoryboardSegue) {
}
 
@IBAction func savePlayerDetail(segue:UIStoryboardSegue) {
}

cancelToPlayersViewController(_:) is simply a marker for the unwind segue. Later you’ll add code to savePlayerDetail(_:) to allow it to live up to it’s name!

Lastly, switch back to Main.storyboard and hook up the Cancel and Done buttons to their respective action methods. Ctrl-drag from the bar button to the exit object above the view controller and then pick the correct action name from the popup menu:

Make connections

Note the name that you gave the cancel method. When you create an unwind segue, the list will show all unwind methods (i.e. ones with the signature @IBAction func methodname(segue:UIStoryboardSegue)) in the entire app, so ensure that you create a name that you recognize.

Run the app, press the + button, and test the Cancel and Done buttons. A lot of functionality for very little code!

Static Cells

When you’re finished with this section, the Add Player scene will look like this:

Add Player

That’s a grouped table view, but you don’t have to create a data source for this table. You can design it directly in the storyboard — no need to write cellForRowAtIndexPath(_:) for this one! The feature that makes this possible is called static cells.

Select the table view in the Add Player scene and in the Attributes inspector change Content to Static Cells. Change Style from Plain to Grouped and give the table view 2 sections.

StaticCells

Note: When you change the value of the Sections attribute, the editor will clone the existing section. (You can also select a specific section in the Document Outline on the left and duplicate it.)

The finished scene will have only one row in each section, so select two cells in each of the sections and delete them using the Document Outline.

Select the top-most Table View Section (from the Document Outline). In its Attributes inspector, give the Header field the value Player Name.

Player Name

Drag a new Text Field into the cell for this section. Stretch out its width and remove its border so you can’t see where the text field begins or ends. Set the Font to System 17.0 and uncheck Adjust to Fit.

FormatPlayerName

You’re going to make an outlet for this text field on the PlayerDetailsViewController using the Assistant Editor feature of Xcode. While still in the storyboard, open the Assistant Editor with the button from the toolbar (the one at the top right with two intertwining rings). It should automatically open on PlayerDetailsViewController.swift (if it doesn’t, use the jumpbar in the right hand split window to select that .swift file).

Select the new text field and ctrl-drag to the top of the .swift file, just below the class definition. When the popup appears, name the new outlet nameTextField, and click Connect. After you click Connect, Xcode will add the property to the PlayersDetailViewController class and connect to it in the storyboard:

NameTextField

Creating outlets for views on your table cells is exactly the kind of thing I said you shouldn’t try with prototype cells, but for static cells it is OK. There will be only one instance of each static cell and so it’s perfectly acceptable to connect their subviews to outlets on the view controller.

Set the Style of the static cell in the second section to Right Detail. This gives you a standard cell style to work with. Change the label on the left to read Game by double clicking it and give the cell a Disclosure Indicator accessory.

Player Game

Just as you did for the Name text field, make an outlet for the label on the right (the one that says “Detail”) and name it detailLabel. The labels on this cell are just regular UILabel objects. You might need to click a few times on the text “Detail” to select the label (and not the whole cell) before ctrl-clicking and dragging to PlayerDetailsViewController.swift. Once done, it will look something like this:

Detail Label

The final design of the Add Player scene looks like this:

Add Player

Note: The scenes you have designed so far in this storyboard all have the width and height of the 4.7-inch screen of the iPhone 6, which is 667 points tall. Obviously, your app should work properly with all the different screen sizes, and you can preview all these sizes within your Storyboard.

Open the Assistant Editor from the toolbar, and use the jump bar to select Preview. At the bottom left of the assistant editor, click the + symbol to add new screen sizes to preview. To remove a screen size, select it and hit the Delete key.

Preview

For the Ratings app, you don’t have to do anything fancy. It only uses table view controllers and they automatically resize to fit the screen space. When you do need to support different layouts for different sized devices, you will use Auto Layout and Size Classes.

Build and run now, and you’ll notice that the Add Player scene is still blank!

Add Player Blank

When you use static cells, your table view controller doesn’t need a data source. Because you used an Xcode template to create the PlayerDetailsViewController class, it still has some placeholder code for the data source and that will prevent the static cells from working properly – that’s why your static content wasn’t visible here. Time to fix it!

Open PlayerDetailsViewController.swift and delete everything from the following line down (except for the class closing bracket):

// MARK: - Table view data source

Run the app and check out the new scene with the static cells. All without writing a line of code – in fact, you threw away a bunch of code!

Add Player

One more thing about static cells: they only work in UITableViewController. Even though Interface Builder will let you add them to a Table View object inside a regular UIViewController, this won’t work during runtime. The reason for this is that UITableViewController provides some extra magic to take care of the data source for the static cells. Xcode even prevents you from compiling such a project with the error message: “Illegal Configuration: Static table views are only valid when embedded in UITableViewController instances”. Prototype cells, on the other hand, work just fine in a table view that you place inside a regular view controller.

Note: If you’re building a scene that has a lot of static cells — more than can fit in the visible frame — then you can scroll through them in Interface Builder with the scroll gesture on the mouse or trackpad (2 finger swipe). This might not be immediately obvious, but it does work.

You can’t always avoid writing code altogether though, even for a table view of static cells. When you dragged the text field into the first cell, you probably noticed it didn’t fit completely. There is a small margin of space around the text field. The user can’t see where the text field begins or ends, so if they tap in the margin and the keyboard doesn’t appear, they’ll be confused.

To avoid that, you should let a tap anywhere in that row bring up the keyboard. That’s pretty easy to do – just open PlayerDetailsViewController.swift and add a tableView(_:didSelectRowAtIndexPath:) method as follows:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  if indexPath.section == 0 {
    nameTextField.becomeFirstResponder()
  }
}

This just says that if the user tapped the first cell, the app should activate the text field. There is only one cell in the section so you only need to test for the section index. Making the text field the first responder will automatically bring up the keyboard. It’s just a little tweak, but one that can save users a bit of frustration.

Tip: when adding a delegate method, or overriding a view controller method, just start typing the method name (without preceding it with “func”), and then you will be able to select the correct method from the list available.

You should also set the Selection Style for that cell to None (instead of Default) in the storyboard Attributes inspector, otherwise the row appears highlighted if the user taps in the margin around the text field.

Selection None

All right, that’s the design of the Add Player scene. Now let’s actually make it work.

The Add Player Scene at Work

For now you will ignore the Game row and just let users enter the name of the player.

When the user presses the Cancel button the scene should close and whatever data they entered will be lost. That part already works with the unwind segue.

When the user presses Done, however, you should create a new Player object and fill in its properties and update the list of players.

prepareForSegue(_:sender:) is invoked whenever a segue is about to take place. You’ll override this method to store the data entered into a new Player object before dismissing the view.

Note: You never call prepareForSegue(_:sender:) yourself. It’s a message from UIKit to let you know that a segue has just been triggered.

Inside PlayerDetailsViewController.swift, first add a property at the top of the class to hold details of the player that you are adding:

var player:Player?

Next, add the following method to PlayerDetailsViewController.swift:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SavePlayerDetail" {
    player = Player(name: nameTextField.text!, game: "Chess", rating: 1)
  }
}

prepareForSegue(_:sender:) creates a new Player instance with default values for game and rating. It does this only for a segue that has the identifier of SavePlayerDetail.

In Main.storyboard, find the Add Player scene in the Document Outline and select the unwind segue tied to the savePlayerDetail Action. Change Identifier to SavePlayerDetail:

Save Player Detail

Hop over to PlayersViewController and change the unwind segue method savePlayerDetail(segue:) to look like this:

@IBAction func savePlayerDetail(segue:UIStoryboardSegue) {
  if let playerDetailsViewController = segue.sourceViewController as? PlayerDetailsViewController {
 
    //add the new player to the players array
    if let player = playerDetailsViewController.player {
      players.append(player)
 
      //update the tableView
      let indexPath = NSIndexPath(forRow: players.count-1, inSection: 0)
      tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
    }
  }
}

This obtains a reference to the PlayerDetailsViewController via the segue reference passed to the method. It uses that to add the new Player object to the array of players used in the datasource. Then it tells the table view that a new row was added (at the bottom), because the table view and its data source must always be in sync.

You could have just done tableView.reloadData() but it looks nicer to insert the new row with an animation. UITableViewRowAnimation.Automatic automatically picks the proper animation, depending on where you insert the new row. Very handy.

Try it out, you should now be able to add new players to the list!

App with new players

Performance

Now that you have several view controllers in the storyboard, you might be wondering about performance. Loading a whole storyboard at once isn’t a big deal. The Storyboard doesn’t instantiate all the view controllers right away – only the initial view controller is immediately loaded. Because your initial view controller is a Tab Bar Controller, the two view controllers that it contains are also loaded (the Players scene from the first tab and the scene from the second tab).

The other view controllers are not instantiated until you segue to them. When you close these view controllers they are immediately deallocated, so only the actively used view controllers are in memory.

Let’s see that in practice. Add an initializer and deinitializer to PlayerDetailsViewController:

required init?(coder aDecoder: NSCoder) {
  print("init PlayerDetailsViewController")
  super.init(coder: aDecoder)
}
 
deinit {
  print("deinit PlayerDetailsViewController")
}

You’re overriding init?(coder:) and deinit, and making them log a message to the Xcode Debug pane. Now run the app again and open the Add Player scene. You should see that this view controller did not get allocated until that point.

When you close the Add Player scene, either by pressing Cancel or Done, you should see the print() log statement from deinit. If you open the scene again, you should also see the message from init?(coder:) again. This should reassure you that view controllers are loaded on-demand only.

The Game Picker Scene

Tapping the Game row in the Add Player scene should open a new scene that lets the user pick a game from a list. That means you’ll be adding yet another table view controller, although this time you’re going to push it on the navigation stack rather than show it modally.

Drag a new Table View Controller into Main.storyboard. Select the Game table view cell in the Add Player scene (be sure to select the entire cell, not one of the labels) and ctrl-drag to the new Table View Controller to create a segue between them. Make this a Show segue (under Selection Segue in the popup, not Accessory Action).

Select this new segue and give it the identifier PickGame in the Attributes Inspector.

Select the new Table View Controller in the Document Outline and in the Attributes Inspector, name this scene Choose Game.

Choose Game

Set the Style of the prototype cell to Basic, and give it the reuse identifier GameCell. That’s all you need to do for the design of this scene:

Game Cell

Add a new Swift file to the project, using the Cocoa Touch Class template and name it GamePickerViewController, subclass of UITableViewController.

Go back to your new Choose Game Scene in Main.storyboard and in the Identity Inspector, set its Custom Class to GamePickerViewController.

Now let’s give this new scene some data to display. In GamePickerViewController.swift, add a games string array property to the top populated with some hard coded values:

var games:[String] = [
    "Angry Birds",
    "Chess",
    "Russian Roulette",
    "Spin the Bottle",
    "Texas Hold'em Poker",
    "Tic-Tac-Toe"]

Now replace the data source methods from the template with:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
  return 1
}
 
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return games.count
}
 
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) 
  cell.textLabel?.text = games[indexPath.row]
  return cell
}

Standard stuff here – you’re just setting up the data source to use the games array and placing the string values in the cell’s textLabel.

That should do it as far as the data source is concerned. Run the app and tap the Game row. The new Choose Game scene will slide into view. Tapping the rows won’t do anything yet, but because this scene is presented on the navigation stack, you can always press the back button to return to the Add Player scene.

Choose Game

This is pretty cool, huh? You didn’t have to write any code to invoke this new scene. You just ctrl-dragged from the static table view cell to the new scene and that was it. The only code you wrote was to populate the contents of the tableView, which is typically something more dynamic rather than a hardcoded list.

Of course, this new scene isn’t very useful if it doesn’t send any data back, so you’ll have to add a new unwind segue for that.

At the top of the GamePickerViewController class, add properties to hold the name and the index of the currently selected game:

  var selectedGame:String? {
    didSet {
      if let game = selectedGame {
        selectedGameIndex = games.indexOf(game)!
      }
    }
  }
  var selectedGameIndex:Int?

Whenever selectedGame is updated, didSet will locate the game string in games and automatically update selectedGameIndex with the correct index into the table.

Next, change tableView(_:cellForRowAtIndexPath:) to:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath)
  cell.textLabel?.text = games[indexPath.row]
 
  if indexPath.row == selectedGameIndex {
    cell.accessoryType = .Checkmark
  } else {
    cell.accessoryType = .None
  }
  return cell
}

This sets a checkmark on the cell that contains the name of the currently selected game. Small gestures such as these will be appreciated by the users of the app.

Now add the delegate method tableview(_:didSelectRowAtIndexPath:):

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  tableView.deselectRowAtIndexPath(indexPath, animated: true)
 
  //Other row is selected - need to deselect it
  if let index = selectedGameIndex {
    let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0))
    cell?.accessoryType = .None
  }
 
  selectedGame = games[indexPath.row]
 
  //update the checkmark for the current row
  let cell = tableView.cellForRowAtIndexPath(indexPath)
  cell?.accessoryType = .Checkmark
}

This method is called by the Table View delegate whenever the user taps a row.

First this method deselects the row after it was tapped. That makes it fade from the gray highlight color back to the regular white. Then it removes the checkmark from the cell that was previously selected, and puts it on the row that was just tapped.

Run the app now to test that this works. Tap the name of a game and its row will get a checkmark. Tap the name of another game and the checkmark moves along with it.

Game Checkmark

The scene ought to close as soon as you tap a row but that doesn’t happen yet because you haven’t actually hooked up an unwind segue. Sounds like a great next step!

In PlayerDetailsViewController.swift, at the top of the class, add a property to hold the selected game so that you can store it in the Player object later. Give it a default of “Chess” so you always have a game selected for new players.

var game:String = "Chess" {
  didSet {
    detailLabel.text? = game
  }
}

didSet will display the name of the game in the static table cell whenever the name changes.

Still in PlayerDetailsViewController.swift, add the unwind segue method:

@IBAction func unwindWithSelectedGame(segue:UIStoryboardSegue) {
  if let gamePickerViewController = segue.sourceViewController as? GamePickerViewController,
    selectedGame = gamePickerViewController.selectedGame {
      game = selectedGame
  }
}

This code will get executed once the user selects a game from the Choose Game Scene. This method updates both the label on screen and the game property based on the game selected. The unwind segue also pops GamePickerViewController off the navigation controller’s stack.

In Main.storyboard, ctrl-drag from the tableview cell to the Exit as you did before, and choose unwindWithSelectedGame: from the popup list:

UnwindWithSelectedGame

In the Attributes Inspector give the new unwind segue the Identifier SaveSelectedGame.

Run the app to check it out so far. Create a new player, select the player’s game row and choose a game.

19_sb2_selectedGame

The game is not updated on the Add Player scene!

Unfortunately, the unwind segue method is performed before tableView(_:didSelectRowAtIndexPath:), so that the selectedGameIndex is not updated in time. Fortunately, you can override prepareForSegue(_:sender:) and complete that operation before the unwind happens.

In GamePickerViewController, override prepareForSegue(_:sender:):

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SaveSelectedGame" {
    if let cell = sender as? UITableViewCell {
      let indexPath = tableView.indexPathForCell(cell)
      if let index = indexPath?.row {
        selectedGame = games[index]
      }
    }
  }
}

The sender parameter of prepareForSegue(_:sender:) is the object that initiated the segue, which in this case was the game cell that was selected. So you can use that cell’s indexPath to locate the selected game in games then set selectedGame so that it is available in the unwind segue.

Now when you run the app and select the game, it will update the player’s game details!

It Works

Next, you need to change PlayerDetailsViewController’s prepareForSegue(_:sender:) to return the selected game, rather than the hardcoded “Chess”. This way, when you complete adding a new player, their actual game will be displayed on the Players scene.

In PlayerDetailsViewController.swift, change prepareForSegue(_:sender:) to this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SavePlayerDetail" {
    player = Player(name: nameTextField.text, game:game, rating: 1)
  }
}

When you complete the Add Player scene and press done, the list of players will now update with the correct game.

One more thing – when you choose a game, return to the Add Player scene, then try to choose a game again, the game you chose before should have a checkmark by it. The solution is to pass the selected game stored in PlayerDetailsViewController over to the GamePickerViewController when you segue.

Still in PlayerDetailsViewController.swift, add this to the end of prepareForSegue(_:sender:):

if segue.identifier == "PickGame" {
  if let gamePickerViewController = segue.destinationViewController as? GamePickerViewController {
    gamePickerViewController.selectedGame = game
  }
}

Note that you now have two if statements checking segue.identifier. SavePlayerDetail is the unwind segue going back to the Players list, and PickGame is the show segue going forwards to the Game Picker scene. The code you added will set the selectedGame on the GamePickerViewController just before that view is loaded. Setting selectedGame will automatically update selectedGameIndex which is the index that the table view cell uses to set a checkmark.

Awesome. You now have a functioning Choose Game scene!

Functioning Game Scene

Where To Go From Here?

Here is the final Ratings iOS 9 example project with all of the code from the above tutorial.

Congratulations, you now know the basics of using the Storyboard Editor, and can create apps with multiple view controllers transitioning between each other with segues! Editing multiple view controllers and their connections to each other in one place makes it easy to visualize your app as a whole.

You’ve also seen how easy it is to customize table views and table view cells. Static cells make it easy to set up an interface without implementing all the data source methods.

If you want to learn more about storyboards, check out our book the iOS Apprentice or our iOS 101 video tutorial series.

If you have any questions or comments on this tutorial or on storyboards in general, please join the forum discussion below!

The post Storyboards Tutorial in iOS 9: Part 2 appeared first on Ray Wenderlich.


Auto Layout Tutorial in iOS 9 Part 1: Getting Started

$
0
0
Start thinking in auto layout constraints!

Start thinking in auto layout constraints!

Update 9/22/15: This tutorial was updated to iOS 9, Xcode 7, and Swift 2 by Brad Johnson. Original post by tutorial team member Matthijs Hollemans.

Have you ever been frustrated trying to make your apps look good in both portrait and landscape orientation? Is making screen layouts that support both the iPhone and iPad driving you to the brink of madness? Despair no longer, I bring you good news!

Not only does Auto Layout make it easy to support different screen sizes in your apps, as a bonus it also makes internationalization almost trivial. You no longer have to make new nibs or storyboards for every language that you wish to support, and this includes right-to-left languages such as Hebrew or Arabic.

So grab a snack and your favorite caffeinated beverage, and get ready to become an Auto Layout master!

The problem with springs and struts

Open Xcode 7 and create a new iPhone Swift project based on the Single View Application template. Call the app “StrutsProblem”:

RW Auto Layout iOS 9 2015-09-05 at 1.27.00 PM

You are no doubt familiar with autosizing masks – also known as the “springs and struts” model. The autosizing mask determines what happens to a view when its superview changes size. Does it have flexible or fixed margins (the struts), and what happens to its width and height (the springs)?

For example, with a flexible width the view will become proportionally wider if the superview also becomes wider. And with a fixed right margin, the view’s right edge will always stick to the superview’s right edge.

The autosizing system works well for simple cases, but it quickly breaks down when your layouts become more intricate. Let’s look at an example where springs and struts simply don’t cut it.

Click on Main.storyboard to open it in Interface Builder. Before you do anything else, first disable Auto Layout and Size Classes for this storyboard. You do that in the File inspector, the first of the six tabs:RW Auto Layout iOS9 2015-09-05 at 1.28.56 PM

Uncheck the Use Auto Layout box. This will tell you that size classes can’t be used either – that’s fine. Now the storyboard uses the old struts-and-springs model.

Drag three new views onto the main view. Line them up and give them color like this:

SwiftAutoLayoutUpdate5

For clarity, give each view its own color so that you can see which is which.

Each view is inset 20 points from the window’s borders; the padding between the views is also 20 points. The bottom view is 280 points wide and the two views on top are both 130 points wide. All views are 254 points high.

If you’d rather not do this by drag and drop, you can open the size inspector and enter the following values:

  • Top left: Origin 20,20, Size 130 x 254
  • Top right: Origin 170,20, Size 130 x 254
  • Bottom: Origin 20,294, Size 280 x 254

You can see what this looks like without having to run the app by using the Preview Assistant. Open the Assistant editor, then from the top bar of the assistant editor (which will normally say “Automatic”) Choose Preview/Main Storyboard:

RW Autolayout iOS 9 2015-09-05 at 1.39.09 PM

You can add as many simulated devices as you want to the preview assistant, which beats building and running on every different simulator! Click the + button on the bottom left to add a device. Set up two iPhone 4-inch screens, and rotate one of them to landscape using the button that appears next to the device name.

In landscape, the app doesn’t look quite right:

RW Auto Layout iOS 92015-09-05 at 1.41.05 PM

Instead, you want the app to look like this in landscape:

What landscape is supposed to look like

Obviously, the autosizing masks for all three views leave a little something to be desired. Change the autosizing settings for the top-left view to:

autolayoutSwift12

This makes the view stick to the top and left edges (but not the bottom and right edges), and resizes it both horizontally and vertically when the superview changes its size.

Similarly, change the autosizing settings for the top-right view:

swiftautolayout12

And for the bottom view:

autolayoutSwift12 at 10.51.47 AM

You’ll have seen the layout changing in the preview assistant. It will now look like this:

SwiftAutoLayoutUpdat10

Close, but not quite. The padding between the views is not correct. Another way of looking at it is that the sizes of the views are not 100% right. The problem is that the autosizing masks tell the views to resize when the superview resizes, but there is no way to tell them by how much they should resize.

You can play with the autosizing masks – for example, change the flexible width and height settings (the “springs”) – but you won’t get it to look exactly right with a 20-point gap between the three views.

Why?!?!?

To solve this layout problem with the springs and struts method, unfortunately you will have to write some code.

UIKit sends several messages to your view controllers before, during and after rotating the user interface. You can intercept these messages to make changes to the layout of your UI. Typically you would override viewWillLayoutSubviews to change the frames of any views that need to be rearranged.

Before you can do that, you first have to make outlet properties to refer to the views to be arranged.

Switch to the Assistant Editor mode (middle button on the Editor toolset on the Xcode toolbar) and Ctrl-drag from each of the three views onto ViewController.swift:RW Auto layout iOS9 2015-09-05 at 1.48.12 PM

Connect the views to these three properties, respectively:

@IBOutlet weak var topLeftView: UIView!
@IBOutlet weak var topRightView: UIView!
@IBOutlet weak var bottomView: UIView!

Add the following code to ViewController.swift:

  override func viewWillLayoutSubviews() {
 
    if UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft || UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeRight {
 
            var rect = topLeftView.frame
            rect.size.width = 254
            rect.size.height = 130
            topLeftView.frame = rect
 
            rect = topRightView.frame
            rect.origin.x = 294
            rect.size.width = 254
            rect.size.height = 130
            topRightView.frame = rect
 
            rect = bottomView.frame
            rect.origin.y = 170
            rect.size.width = 528
            rect.size.height = 130
            bottomView.frame = rect
 
    } else {
 
            var rect = topLeftView.frame
            rect.size.width = 130
            rect.size.height = 254
            topLeftView.frame = rect
 
            rect = topRightView.frame
            rect.origin.x = 170
            rect.size.width = 130
            rect.size.height = 254
            topRightView.frame = rect
 
            rect = bottomView.frame
            rect.origin.y = 295
            rect.size.width = 280
            rect.size.height = 254
            bottomView.frame = rect
    }
  }

This callback occurs when the view controller is rotating to a new orientation. It looks at the orientation the view controller has rotated to and resizes the views appropriately – in this case with hardcoded offsets based on the known screen dimensions of the iPhone. This callback occurs within an animation block, so the changes in size will animate.

Don’t run the app just yet. First you have to restore the autosizing masks of all three views to the following, or the autosizing mechanism will clash with the positions and sizes you set on the views in viewWillLayoutSubviews:

SwiftAutoLayoutUpdate12

That should do it. Run the app on the iPhone 5 simulator (the preview assistant doesn’t take your code into account!) and flip to landscape. Now the views line up nicely. Flip back to portrait and verify that everything looks good there as well.

It works, but that was a lot of code you had to write for a layout that is pretty simple. Imagine the effort it takes for layouts that are truly complex, especially dynamic ones where the individual views change size, or the number of subviews isn’t fixed.

Now try running the app on the 3.5-inch or 4.7-inch simulator. Whoops. The positions and sizes of the views are wrong because the hardcoded coordinates in viewWillLayoutSubviews are based on the dimensions of the 4-inch phone (320×568 instead of 320×480). You could add another if-statement that checks the screen size and uses a different set of coordinates, but you can see that this approach is becoming unworkable quickly.

Another approach you could take is to make separate nibs for the portrait and landscape orientations. When the device rotates you load the views from the other nib and swap out the existing ones. But this is still a lot of work and it adds the trouble of having to maintain two nibs instead of one. This approach is quite impractical when you’re using storyboards instead of nibs.

There must be another way

Auto Layout to the rescue!

You will now see how to accomplish this same effect with Auto Layout. First, remove viewWillLayoutSubviews from ViewController.swift, because you’re going to do this without writing any code!

Select Main.storyboard and in the File inspector panel, check both the Use Auto Layout and Use Size Classes options to enable Auto Layout and Size Classes for this storyboard file:
RW auto layout iOS9 2015-09-05 at 1.54.04 PM

A quick note on Size Classes

Size classes were introduced in iOS 8, and they make it really intuitive to only have one storyboard for universal apps. Nearly everything you see on screen can have size classes, including the screen (UIScreen), views, and your view controllers. There are two types of size classes: vertical and horizontal. Each vertical and horizontal size class can have one of three values: Regular, Compact, or Any.

The size classes correspond to the device and orientation your app is running in. For example, a portrait iPhone has a Regular height and a Compact width. The Any value is used as the generic size class value; think of it as the superclass of all the other layouts. If there is nothing defined for the size class relating to the current device and orientation, the storyboard will pull our layout from Any.

It’s easy to view and switch between size class configurations in Xcode 7. At the bottom of the storyboard, towards the middle you will see a label that says “wAny hAny“. Click on it to see the size class configuration grid:RW auto layout iOS9 2015-09-05 at 1.57.17 PM

You can move your cursor over the other boxes in the grid to see which set of squares corresponds to which size class configuration. By default you start on Any width and Any height. This is the default, generic size class configuration. Apple recommends doing all of your initial interface layout in this class for a universal app, since all size classes will pull from this one initially. Make sure “wAny hAny” is the selected size class in your storyboard.

You will notice the size of the scenes in your storyboard have changed to squares to reflect our generic size class configuration.

For more detail about size classes, check out our Beginning Adaptive Layout Tutorial. This tutorial will stick to the basics of Auto Layout.

Your First Auto Layout Constraints

Look at the landscape layout in the preview assistant. It now looks like this:

SwiftAutoLayoutUpdate14

Let’s put Auto Layout into action. Hold down the shift key while you click on the two views on the top (the green and yellow ones), so that both are selected. Now use Xcode’s Pin menu to create equal width constraints for both views by selecting the Equal Widths checkbox and then selecting Add 1 Constraint:

Screen Shot 2015-09-05 at 2.09.01 PM

Now select the just the top left view. Use the Pin menu again and create a horizontal constraint from the top left view to the top right view by selecting the red line to the selected view’s right side nearest neighbor (which is the top right view) and selecting Add 1 Constraint:

Screen Shot 2015-09-05 at 2.09.21 PM

The storyboard now looks like this:

RW auto layout ios9 2015-09-05 at 2.17.45 PM

The red “T-bar” shaped things represent the constraints between the views. So far you added two constraints: an Equal Widths constraint on both views (represented by the bars with the equals signs) and a Horizontal Space constraint that sits between the two views. Constraints express relationships between views and they are the primary tool you use to build layouts using Auto Layout. It might look a bit scary, but it is actually quite straightforward once you learn what it all means.

They are red because we have not added enough constraints yet for auto layout to establish what the final layout should be.

To continue building the layout for this screen, perform the following steps. Each step adds more red T-bars. Remember to re-select the view again after adding each constraint.

For the green view on the top left, choose from the Pin menu:

  • Pin to top nearest neighbor
  • Pin to left nearest neighbor

For the yellow view on the right, choose:

  • Pin to top nearest neighbor
  • Pin to right nearest neighbor

And for the big blue view at the bottom:

  • Pin to left nearest neighbor
  • Pin to right nearest neighbor
  • Pin to bottom nearest neighbor

If you select all three views, you should see following constraints:

RW auto layout ios9 2015-09-05 at 2.24.19 PM

Notice that some of the T-bars are blue. This means auto layout is ‘happy’ with this constraint relative the other constraints you have setup. But, you still have some red constraints, which means your layout is incomplete; Auto Layout does not have enough constraints to calculate the positions and sizes of the views. The solution is to add more constraints until they all turn blue.

Hold down shift and select all three views. From the Pin menu, put a check by Equal Heights and select Add 2 Constraints.

Now select the top-left corner view and the bottom view (using shift as before), and pin it to its nearest bottom neighbor (which is the bottom blue view).

Interface Builder should show something like this:

RW auto layout ios9 2015-09-05 at 2.27.40 PM

The T-bars have become blue: great success! Auto Layout now has enough information to calculate a valid layout. It’s not quite right yet, because there’s that big space at the right of the screen which was made when you converted to the generic size class. Select the bottom view’s trailing space constraint. To do this, select the bottom blue view and go to the Size Inspector tab. There is a section called Constraints which lists all the constraints attached to the currently selected view. From here, double click the Trailing Space constraint that you see with the large constant value:

Screen Shot 2015-09-05 at 2.41.49 PM

With the individual constraint now selected, change the Constant value to 20:

Screen Shot 2015-09-05 at 2.33.39 PM

Do the same for the top right view’s trailing constraint as well.

Look at the layout preview, and voila! Perfect in portrait and landscape. It also doesn’t matter which simulator you run this on; the layout works fine on 3.5-inch, 4 inch, 4.7 inch and 5.5 inch devices. Add a few devices to the preview assistant and check it out. You may also notice that “iPad” is now an option – add that as well and you’ll see that your single layout is supporting every type of device!

SwiftAutoLayout19

Cool, but what exactly did you do here? Rather than requiring you to hard-code how big your views are and where they are positioned, Auto Layout lets you express how the views in your layout relate to each other and their superview.

You have put the following relationships – what are known as constraints – into the layout:

  • The top-left and top-right views always have the same width (that was the first pin widths equally command).
  • There is a 20-point horizontal padding between the top-left and top-right views (that was the pin horizontal spacing).
  • All the views always have the same height (the pin heights equally command).
  • There is a 20-point vertical padding between the two views on top and the one at the bottom (the pin vertical spacing).
  • There is a 20-point margin between the views and the edges of the screen (the top, bottom, leading, and trailing space to superview constraints).

And that is enough to express to Auto Layout where it should place the views and how it should behave when the size of the screen changes.

Well done

You can see all your constraints in the Document Outline on the left. Xcode adds the section named Constraints when you enabled Auto Layout for the storyboard. If you don’t see the outline pane, then click the arrow button at the bottom of the Interface Builder window.

If you click on a constraint in the Document Outline, Interface Builder will highlight where it sits on the view by drawing a white outline around the constraint and adding a shadow to it so that it stands out:

RW auto layout ios9 2015-09-05 at 2.46.42 PM

Constraints are real objects (of class NSLayoutConstraint) and they also have attributes. For example, select the constraint that creates the padding between the two top views (it is named “Horizontal Space (20)” but be sure to pick the correct one) and then switch to the Attributes inspector. There you can change the size of the margin by editing the Constant field.

RW auto layout ios9 2015-09-05 at 2.56.35 PM

Set it to 100 and look in the Preview Assistant. Now the margin is a lot wider:

SwiftAutoLayoutUpdate22

Auto Layout is a lot more expressive than springs and struts when it comes to describing the views in your apps. In the rest of this tutorial, you will learn all about constraints and how to apply them in Interface Builder to make different kinds of layouts.

How Auto Layout Works

As you’ve seen in the test drive above, the basic tool in Auto Layout is the constraint. A constraint describes a geometric relationship between two views. For example, you might have a constraint that says:

“The right edge of label A is connected to the left edge of button B with 20 points of empty space between them.”

Auto Layout takes all of these constraints and does some mathematics to calculate the ideal positions and sizes of all your views. You no longer have to set the frames of your views yourself – Auto Layout does that for you, entirely based on the constraints you have set on those views.

Before Auto Layout, you always had to hard-code the frames of your views, either by placing them at specific coordinates in Interface Builder, by passing a rectangle into init(frame:), or by setting the view’s frame, bounds or center properties directly.

For the app that you just made, you specifically set the frames to:

Struts coordinates

You also set autosizing masks on each of these views:

Struts autosizing masks

That is no longer how you should think of your screen designs. With Auto Layout, all you need to do is this:

Auto Layout instead of struts

The sizes and positions of the views are no longer as important. Of course, when you drag a new button or label on to the canvas it will have a certain size and you will drop it at a certain position, but that is only a design aid that you use to tell Interface Builder where to put the constraints.

The idea behind auto layout is to simplify this where you set a few constants – such as the 20 point margin or maybe an exact width for an image – and then build the rest of the layout in a relative fashion.

Designing by Intent

The big advantage of using constraints is that you no longer have to fiddle with coordinates to get your views to appear in the proper places. Instead, you can describe to Auto Layout how the views are related to each other and Auto Layout will do all the hard work for you. This is called designing by intent.

When you design by intent, you’re expressing what you want to accomplish but not necessarily how it should be accomplished. Instead of saying: “the button’s top-left corner is at coordinates (20, 230)”, you now say:

“The button is centered vertically in its superview, and it is placed at a fixed distance from the left edge of the superview.”

Using this description, Auto Layout can automatically calculate where your button should appear, no matter how big or small that superview is.

Other examples of designing with intent (and Auto Layout can handle all of these instructions):

“These two text fields should always be the same size.”
“These two buttons should always move together.”
“These four labels should always be right-aligned.”

This makes the design of your user interfaces much more descriptive. You simply define the constraints, and the system calculates the frames for you automatically.

You saw in the first section that even a layout with just a few views needs quite a bit of work to layout properly in both orientations. With Auto Layout you can skip all that effort. If you set up your constraints properly, then the layout should work without any changes in both portrait and landscape.

Another important benefit of using Auto Layout is internationalization. Text in German, for example, is infamous for being very long and getting it to fit into your labels can be a headache. Again, Auto Layout takes all this work out of your hands, because it can automatically resize your labels based on the content they need to display – and have everything else adapt with constraints.

Adding support for German, French, or any other language is now simply a matter of setting up your constraints, translating the text, and… that’s it!

French

Auto Layout is not just useful for rotation; it can also easily scale your UI up and down to accommodate different screen sizes. It is no coincidence that this technology was added to iOS at the same time that the iPhone 5 and its taller screen came out, and now we have the even bigger iPhone 6 and 6 Plus!

Auto Layout makes it a lot easier to stretch your apps’ user interfaces to fill up that extra space on the larger phones. And with Dynamic Type introduced in iOS 7 Auto Layout has become even more important. Users can now change the global text size setting — with Auto Layout this is easy to support in your own apps.

The best way to get the hang of Auto Layout is to play with it, so that’s exactly what you will do in the rest of this tutorial.

Courting constraints

Close your current project and create a new iPhone project using the Single View Application template. Name it “Constraints”.

Any new projects that you create with Xcode 7 automatically assume that you will be using Auto Layout, so you do not need to do anything special to enable it. To keep things simple though, open Main.storyboard and disable Size Classes for this project.

To start off the interface, drag a new Button onto the canvas. Notice that while you’re dragging, dashed blue lines appear. These lines are known as the guides:RW auto layout ios9 2015-09-05 at 3.02.29 PM

There are guides around the margins of the screen, as well as in the center. If you have used Interface Builder before, then you have no doubt seen these guides. They are helpful hints that make it easier to align your views.

Note that when you add a new object to the view, there are no constraints! But how can this work? You just learned that Auto Layout always needs enough constraints to determine the size and position of all the views, but here you have no constraints at all. Surely this is an incomplete layout?

If you don’t supply any constraints at all, Xcode automatically assigns a set of default constraints, known as the automatic constraints. It does this at compile time when your app is built, not at design time. Auto Layout since Xcode 5 works hard to stay out of your way while you’re designing your user interfaces, and that’s just how we like it.

The automatic constraints give your views a fixed size and position. In other words, the view always has the same coordinates as you see in the storyboard. This is very handy because it means you can largely ignore Auto Layout. You simply don’t add constraints if the default ones are sufficient and only create constraints for those views that need special rules.

OK, let’s play around a bit with constraints and see what they can do. Right now, the button is in the top-left corner and has no constraints. Make sure the button is aligned with the two corner guides.

Add two new constraints to the button using the Pin menu, so that it looks like this:

RW auto layout ios9 2015-09-05 at 3.30.41 PM

If you hadn’t guessed already, that the button is pinned to its nearest left/leading and top neighbors, both being the button’s super view.

All the constraints are also listed in the Document Outline pane on the left-hand side of the Interface Builder window:

RW auto layout ios9 2015-09-05 at 3.32.25 PM

There are currently two constraints: a Horizontal Space between the button and the left edge of the main view, and a Vertical Space between the button and the top edge of the main view. The relationship that is expressed by these constraints is:

“The button always sits at 20 points from the top-left corner in its superview.”

Note: These aren’t actually very useful constraints to make because they’re the same as the automatic ones. If you always want your button to be relative to the top-left corner of its superview, then you might as well not provide any constraints at all and let Xcode make them for you.

Now pick up the button and place it in the scene’s top-right corner, again against the blue guides:

RW auto layout iOS9 2015-09-05 at 3.33.47 PM

Whoa, what has happened with all that angry orange? The problem here is that the size and position of the button in Interface Builder no longer correspond with the size and position that Auto Layout expects based on the constraints. This is called a misplaced view.

Run the app. The button will still appear in the top-left corner of the screen:

RW auto layout ios9 2015-09-05 at 3.34.41 PM

When it comes to Auto Layout, orange is bad. Interface Builder drew two orange boxes: one with a dashed border and one with a solid border. The dashed box displays the view’s frame according to Auto Layout. The solid orange box is the view’s frame according to how you placed it in the scene. These two should match up, but here they don’t.

How you fix this depends on what you want to achieve:

  • Do you want the button to be attached to the left edge of the screen at a distance of 254 points? In that case you need to make the existing Horizontal Space constraint 234 points bigger. That’s what the orange badge with “+234” means.
  • Do you want the button to be attached to the right edge of the screen instead? Then you need to remove the existing constraint and create a new one.

Delete the Horizontal Space constraint. First select it in the canvas or in the Document Outline, and then press the Delete key on your keyboard.

RW auto layout ios9 2015-09-05 at 3.35.53 PM

These red lines are telling you auto layout has a problem with your constraints. This is happening because there are not enough constraints left to determine the complete position of the button. You still need to add a constraint for the X-position.

You may be wondering why Xcode does not add an automatic constraint for the X-position. The rule is that Xcode only creates automatic constraints if you did not set any constraints of your own. As soon as you add a single constraint, you tell Xcode that you’re now taking responsibility for this view. Xcode will no longer make any automatic constraints and expects you to add any other constraints this view needs.

Use the Pin menu to add a constraint on the button to its nearest right neighbor . This puts a new constraint between the right edge of the button and the right edge of the screen. This expresses the relationship:

“The button always sits at 20 points from the top-right corner in its superview.”

Run the app and rotate to landscape. Notice how the button keeps the same distance from the right screen edge:

SwiftAutoLayoutUpdate30

When you place a button (or any other view) against the guides and make a constraint, you are actually making a constraint against the margin ,which is why the constant of the constraints are 0.

Now drag the button over to the left a little:

RW auto layout ios9 2015-09-05 at 3.43.51 PM

Again you get a dashed orange box because the view is misplaced. Let’s say this new button position is indeed what you want. It’s not uncommon to make a constraint and then nudge the view by a few pixels, making the orange boxes appear. One way to fix this is to remove the constraint and make a new one, but there is an easier solution.

The Editor menu has a Resolve Auto Layout Issues submenu. From that menu, choose Update Constraints. In my case, this tells Interface Builder it should make the constraint 29 points larger, as so:

SwiftAutoLayoutUpdate32

Great, the T-bars turn blue again and the layout is valid. In the Document Outline, you can see that the Horizontal Space constraint no longer has a standard space:

RW auto layout ios9 2015-09-05 at 3.45.06 PM

So far you’ve played with Horizontal Space and Vertical Space constraints. There is also a “center” constraint. Drag a new Button object to the bottom center of the canvas, so that it snaps into place with the guides:

Screen Shot 2015-09-05 at 3.49.40 PM

To keep the button always center-aligned with its superview, on the horizontal axis, you need to add a Center X Alignment constraint. From the Alignment menu put a checkmark in Horizontally in Container and select Add 1 Constraint:

RW auto layout ios9 2015-09-05 at 3.51.36 PM

This adds some red lines. The lines are red because you’ve only specified what happens to the X-coordinate of the button, not its Y-coordinate. Use the Pin menu to add a Vertical Space constraint between the button and the bottom of the view (its nearest bottom neighbor). It should look like this:

RW auto layout ios9 2015-09-05 at 3.53.38 PM

In case you didn’t know how, it is the Bottom Space to Superview option. The Vertical Space constraint keeps the button away from the bottom of the view (again, using the standard margin).

Run the app and rotate it to landscape. Even in landscape mode, the button stays at the bottom center of the screen:

SwiftAutoLayoutUpdate37

That’s how you express intent: “This button should always be at bottom center.” Notice that nowhere did you have to tell Interface Builder what the button’s coordinates are, only where you want it anchored in the view.

With Auto Layout, you’re no longer supposed to care about the exact coordinates of where you place your views on the canvas or what their size is. Instead, Auto Layout derives these two things from the constraints that you set.

You can see this paradigm shift in the Size inspector for the button, which is now quite different:

Screen Shot 2015-09-05 at 4.00.43 PM

With Auto Layout disabled, typing into the X, Y, Width or Height fields will change the position and size of the selected view. With Auto Layout enabled you can still type new values into these fields, but if you already have constraints set on the view it may now become misplaced. You also have to update the constraints to make them match the new values.

For example, change the Width value of the button to 100. The canvas turns into something like this:

RW auto layout ios9 2015-09-05 at 4.02.33 PM

Xcode simply says, “It’s fine with me if you want the width to be 100 points but just so you know, that’s not what the constraints say.”

In this case you do want the button to be 100 points wide. There is a special type of constraint for this: the Fixed Width constraint. First press Undo so that the button is centered again and the T-bars are all blue. Select the button and use the Pin menu to add a fixed width constraint to the button:

RW auto layout ios9 2015-09-05 at 4.03.41 PM

This puts an orange dotted box around the button, showing what the size of the button will be like at run time, which is different from what you currently see on storyboard:

 

RW auto layout ios9 2015-09-05 at 4.11.08 PM

You can also see this new Width constraint in the Document Outline on the left:

RW auto layout ios9 2015-09-05 at 4.11.52 PM

Unlike the other constraints, which are between the button and its superview, the Width constraint only applies to the button itself. You can think of this as a constraint between the button and… the button.

You may wonder why the button did not have a Width constraint before. How did Auto Layout know how wide to make the button without it?

Here’s the thing: the button itself knows how wide it must be. It calculates this based on its title text plus some padding. If you set a background image on the button, it also takes that into account.

This is known as the intrinsic content size. Not all controls have this, but many do (UILabel is another example). If a view can calculate its own preferred size, then you do not need to set specific Width or Height constraints on it. You will see more of this later.

I am not fat

To return the button to its optimal size, delete the Width constraint and the orange dotted box goes away. The button will now take its proper intrinsic content size at run time.

It takes two to tango

Guides do not appear only between a view and its superview, but also between views on the same level of the view hierarchy. To demonstrate this, drag a new button onto the canvas. If you drag this button close to the others, then their guides start to interact.

Put the new button next to the existing one so that it snaps into place:

RW auto layout ios9 2015-09-05 at 4.15.00 PM

There are quite a few dotted guidelines here. Interface Builder recognizes that these two buttons can align in different ways – at their tops, centers and baselines.

Xcode 4 would have turned one of these snapping guides into a new constraint. But since Xcode 5, if you want to have a constraint between these two buttons, you have to make it yourself. You’ve seen that you can use the Pin menu to make a constraint between two views, but there is an easier way too.

Select the new button and Ctrl-drag to the other button, like so:

RW auto layout ios9 2015-09-05 at 4.20.19 PM

When you let go of the mouse button, a popup appears. Choose the first option, Horizontal Spacing.

RW auto layout ios9 2015-09-05 at 4.21.14 PM

This creates a new constraint that looks like this:

RW auto layout ios9 2015-09-05 at 4.21.48 PM

It is red, meaning that this button needs at least one other constraint. The size of the button is known — it uses the intrinsic content size — and there is a constraint for the button’s X-position. That leaves only the Y-position without a constraint.

Here the missing constraint is pretty easy to determine but for more complicated designs it may not always be immediately obvious. Fortunately, you don’t have to guess. Xcode has been keeping score and can tell you exactly what is missing.

There is small a red arrow in the Document Outline, next to View Controller Scene. Click that arrow to see a list of all Auto Layout issues:

RW auto layout ios9 2015-09-05 at 4.25.56 PM

Sweet! Let’s add that missing Y-position constraint. Ctrl-drag from the new button downwards to the containing view, and choose Bottom Space to Bottom Layout Guide. You should now see all blue:

RW auto layout ios9 2015-09-05 at 4.27.27 PM

The new button now has a Vertical Space to the bottom of the screen, but also a Horizontal Space that links it with the other button. Because this space is small (only 8 points), the T-bar may be a bit hard to see, but it is definitely there.

Click on the Horizontal Space (8) constraint in the Document Outline to select it:

Screen Shot 2015-09-05 at 4.32.52 PM

This particular constraint sits between the two buttons. What you’ve done here is say:

“The second button always appears on the left of the first one, no matter where the first button is positioned or how big it is.”

Select the button on the right and type something long into its label like “A longer label”. Now select the button on the left, and you will see a dotted orange box where it will be at run time. After all, it is attached to the first button’s left edge, so that is exactly what you intended to happen:

Screen Shot 2015-09-05 at 4.35.17 PM

Anytime auto layout is telling you an object is going to be at a different location at run time, you can always update your storyboard so it is correctly matched up with your constraints. Select the Resolve Auto Layout Issues menu and choose Update Frames:

RW auto layout ios9 2015-09-05 at 4.38.50 PM

This will move the object’s frame to reflect exactly to where the constraints are telling it to go. Now your storyboard accurately conveys where the object will be at runtime, and the dotted orange box is gone.

Just to get a better feel for how this works, play with this some more. First give the longer label button a yellow background. Drag another button into the canvas and put it above the yellow one, so that they snap into place vertically (but don’t try to align the left edges of the two buttons):

RW auto layout ios9 2015-09-05 at 4.43.24 PM

Give the new button a background color (green) so you can more easily see its extents.

Because you snapped the two buttons together, there is now a standard space of 8 points between them that is recommended by the HIG. Turn this into a constraint by Ctrl-dragging between the two buttons. Select Vertical Spacing from the popup menu.

You are not limited to standard spacing between controls. Constraints are full-fledged objects, just like views, and therefore have attributes that you can change.

Select the Vertical Space constraint between the two buttons. You can do this in the canvas by clicking the T-bar, although that tends to be a bit finicky. By far the easiest method is to click on the constraint in the Document Outline. Once you have it selected, switch to the Attributes inspector:

RW auto layout ios9 2015-09-05 at 4.46.57 PM

Type 40 into the Constant field to change how big the constraint is.

Run the app and flip to landscape to see the effect, or add a landscape preview assistant like you did earlier:

RW autolayout ios9 2015-09-05 at 4.51.11 PM

The buttons certainly keep their vertical arrangement, but not their horizontal one! The reason should be obvious: the green button does not have a constraint for its X-position yet.

Adding a Horizontal Space from the green button to the left edge of the canvas won’t solve this problem. With such a constraint the green button always keeps the same X-coordinate, even in landscape. That doesn’t look very nice, so instead you are going to express the following intention:

“The yellow button will always be horizontally centered, and the green button will align its left edge with the left edge of the yellow button.”

You already have a constraint for the first condition, but not for the second. Interface Builder shows guides for alignment, so you can drag the top button until its left edge snaps with the yellow button:

RW auto layout ios9 2015-09-05 at 4.52.27 PM

Finally, Ctrl-drag between the two buttons and from the popup menu choose Leading. This creates an alignment constraint that says: “The left edges of these two views are always aligned”. In other words, the two buttons will always have the exact same X-position. Finally use the Resolve Auto Layout Issues menu to make the green button update its frame according to your constraints. That solves the layout problem and the T-bars turn blue:

RW autolayout ios9 2015-09-05 at 4.54.15 PM

Run the app and rotate to landscape or check the preview assistant to verify that it works:


RW autolayout ios9 2015-09-05 at 4.54.56 PM

Where To Go From Here?

Now that you’ve got your first taste of Auto Layout, how do you like it? It can take a bit of getting used to, but can make your life a lot easier and your apps much more flexible!

Want to learn more? Stay tuned for part 2 of this Auto Layout tutorial, where you’ll continue playing with the buttons in Interface Builder to get a better understanding of the possibilities Auto Layout offers — and the problems you may encounter.

And best of all – you will also use Auto Layout to create a realistic layout that you may find in a real app! :]

In the meantime, if you have any questions or comments please join the forum discussion below!

The post Auto Layout Tutorial in iOS 9 Part 1: Getting Started appeared first on Ray Wenderlich.

Auto Layout Tutorial in iOS 9 Part 2: Constraints

$
0
0
Start thinking in auto layout constraints!

Start thinking in auto layout constraints!

Update 9/22/15: This tutorial was updated to iOS 9, Xcode 7, and Swift 2 by Brad Johnson. Original post by tutorial team member Matthijs Hollemans.

In part 1 of this Auto Layout tutorial you saw that the old “springs-and-struts” model for making user interfaces cannot easily solve all layout problems. Auto Layout is the solution, but because this technology is so powerful it is also a bit more tricky to use.

In this second part and final part of the Auto Layout tutorial series, you’ll continue learning all about constraints and how to apply them!

A little runtime excursion

This Auto Layout tutorial begins with a very simple app that looks like this:
RW autolayout ios9 2015-09-06 at 6.59.12 PM

It has two buttons that have their background color set just so it’s easier to see their boundaries. The buttons have a number of constraints between them. If you’ve been following along with the previous part you can continue using your existing app. Simply remove the other two buttons from the canvas.

If you’re starting from scratch, create a new Swift iPhone application using the Single View Application template and disable Size Classes via the Storyboard’s File Inspector:

RW auto layout iOS9 2015-09-06 at 7.01.03 PM

Drag two buttons into the scene and give them a background color of yellow and green. Put the yellow button below the green button. Select the green button and use the Pin menu to make a constraint to its nearest bottom neighbor (40 points), and then select the lower button and do the same (8 points). Use the Align menu to center the yellow button horizontally in the container, and again to align the left edges of both buttons.

Playing with this in Interface Builder is all well and good, but let’s see how this works at runtime. Add the following method to ViewController.swift:

@IBAction func buttonTapped(sender: UIButton) {
  if sender.titleForState(.Normal) == "X" {
    sender.setTitle("A very long title for this button", forState: .Normal)
  } else {
    sender.setTitle("X", forState: .Normal)
  }
}

This toggles between a long title and a short title for the button that triggered the event. Connect this action method to both of the buttons in Interface Builder. Ctrl-drag from each button to the view controller and select buttonTapped: in the popup.

Run the app and tap the buttons to see how it behaves. Perform the test in both portrait and landscape orientations.

AutoLayoutSwiftUpdate2

Regardless of which button has the long title and which has the short title, the layout always satisfies the constraints you have given it:

  • The lower button is always center-aligned in the window, horizontally.
  • The lower button always sits 20 points from the bottom of the window.
  • The top button is always left-aligned with the lower button and 40 points above it.

That is the entire specification for your user interface.

For fun, remove the Leading Alignment constraint (select it in the outline pane and press Delete on your keyboard), then select both buttons in Interface Builder and from the Align menu select the Trailing Edges option. Now run the app again and notice the differences.

Repeat, but now choose Align\Horizontal Centers. That will always center the top button with respect to the bottom button. Run the app and see how the buttons act when you tap them. (Remember, if you get a dashed orange box when you change the constraints, you can use the Resolve Auto Layout Issues menu to update the button frames accordingly.)

Fixing the width

The Pin menu has an option for Widths Equally. If you set this constraint on two views, then Auto Layout will always make both views equally wide, based on which one is the largest. Let’s play with that for a minute.

Select both buttons and choose Pin\Equal Widths. This adds a new constraint to both buttons:

RW auto layout ios9 2015-09-06 at 7.17.35 PM

You have seen this type of constraint before, in the first part of this tutorial. It looks like the usual T-bar but in the middle it has a circle with an equals sign.

Even though there are two T-bars, in the Document Outline this shows up as a single Equal Widths constraint:

RW auto layout ios9 2015-09-06 at 7.18.23 PM

 

Run the app and tap the buttons. The buttons always have the same width, regardless of which one has the largest label:

AutoLayoutSwiftUpdate7

Of course, when both labels are very short, both buttons will shrink equally. After all, unless there is a constraint that prevents it, buttons will size themselves to fit their content exactly, no more, no less. What was that called again? Right, the intrinsic content size.

Intrinsic Content Size

Before Auto Layout, you always had to tell buttons and other controls how big they should be, either by setting their frame or bounds properties or by resizing them in Interface Builder. But it turns out that most controls are perfectly capable of determining how much space they need, based on their content.

A label knows how wide and tall it is because it knows the length of the text that has been set on it, as well as the font size for that text. Likewise for a button, which might combine the text with a background image and some padding.

The same is true for segmented controls, progress bars, and most other controls, although some may only have a predetermined height but an unknown width.

This is known as the intrinsic content size, and it is an important concept in Auto Layout. You have already seen it in action with the buttons. Auto Layout asks your controls how big they need to be and lays out the screen based on that information.

Usually you want to use the intrinsic content size, but there are some cases where you may not want to do that. You can prevent this by setting an explicit Width or Height constraint on a control.

Imagine what happens when you set an image on a UIImageView if that image is much larger than the screen. You usually want to give image views a fixed width and height and scale the content, unless you want the view to resize to the dimensions of the image.

So what happens when one of the buttons has a fixed Width constraint on it? Buttons calculate their own size, but you can override this by giving them a fixed width. Select the top button and choose Pin\Width from the menu. This adds a solid T-bar below the button:

RW auto layout ios9 2015-09-06 at 7.42.32 PM

Because this sort of constraint only applies to the button itself, not to its superview, it is listed in the Document Outline below the button object. In this case, you have fixed the button to a width of 46 points:

RW auto layout ios9 2015-09-06 at 7.44.02 PM

 

You cannot simply drag the button’s resize handles to make the button wider. If you do, you’ll end up with a whole bunch of orange boxes. Remember that Xcode 7 does not automatically update the constraints for you (unlike Xcode 4). So if you make a change to the button’s frame, it’s up to you to make the constraints match again. The alternative approach is to simply change the constraint instead.

Select the Width constraint and go to the Attributes inspector. Change Constant to 80:

Screen Shot 2015-09-06 at 7.47.44 PM

Run the app and tap the buttons. What happens? The button text does change, but it gets truncated because there is not enough room:

Screen Shot 2015-09-06 at 7.48.33 PM

Because the top button has a fixed-width constraint and both buttons are required to be the same size, they will never shrink or grow.

Note: You probably wouldn’t set a Width constraint on a button by design – it is best to let the button use its intrinsic size – but if you ever run into a layout problem where you expect your controls to change size and they don’t, then double check to make sure a fixed Width constraint didn’t sneak in there.

Play around with this stuff for a bit to get the hang of pinning and aligning views. Get a feel for it, because not everything is immediately obvious. Just remember that there must always be enough constraints so that Auto Layout can determine the position and size for all views.

Got enough constraints

Gallery example

You should now have an idea of what constraints are and how you can build up your layouts by forging relationships between the different views. In the following sections, you will see how to use Auto Layout and constraints to create layouts that meet real-world scenarios.

Let’s pretend you want to make an app that has a gallery of your favorite programmers. It looks like this in portrait and landscape:

The Gallery app

The screen is divided into four equal quarters. Each quarter has an image view and a label. How would you approach this?

Let’s start by setting up the basic app. Create a new Swift iPhone project using the Single View Application template and name it “Gallery”.

Open Main.storyboard. Disable Size classes, because for the purposes of this tutorial it’s better to see an iPhone-shaped layout in the storyboard editor. Select the view controller and in the Attributes inspector, choose a Size of iPhone 3.5-inch. From the Object Library, drag a plain View object onto the canvas. Resize the view so that it is 160 by 284 points, and change its background color to be something other than white (for example, green):

SwiftAutoLayout14

There are two main reasons why you would drop a plain UIView onto a storyboard: a) You’re going to use it as a container for other views, which helps with organizing the content of your scenes; or b) It is a placeholder for a custom view or control, and you will also set its Class attribute to the name of your own UIView or UIControl subclass.

 

Select the green view and open the Pin menu. The popup appears that lets you add a variety of constraints, and you will also see a checkbox labeled Constrain to margins:

RW auto layout ios9 2015-09-06 at 8.25.21 PM

 

Remember the blue guides that appear when you drag a view near to the edge of its superview? Instead of that creating a constraint of that distance from the very edge of the view, you can create a constraint of 0 distance from the view’s margin. A view can define its own margin sizes so this allows you to be more flexible about your layouts. For the purposes of this tutorial, we’ll stick to making constraints to the edges of the view. Go ahead and uncheck that box, and create 4 constraints to all four of the green view’s nearest neighbors in each direction:

RW auto layout ios9 2015-09-06 at 8.28.25 PM

This will create four new constraints between the green view and its superview, one for each side of the view. The actual spacing values may be different for you, depending on where you placed the view – you don’t have the change the values to match the ones above exactly. Click Add 4 Constraints to finish.

Your storyboard should now look something like this:

RW auto layout iOS 9 2015-09-06 at 8.29.14 PM

Maybe you wondered why the constraint at the top of the view doesn’t go all the way up to the top of the screen:

RW auto layout ios9 2015-09-06 at 8.29.43 PM

Instead it stops at the status bar. But since iOS 7 the status bar is always drawn on top of the view controller — it is no longer a separate bar — so what gives? When you created the constraint it didn’t actually attach to the top of the screen but to an invisible line called the Top Layout Guide.

On a regular view controller this guide sits at 20 points from the top of the screen, at least when the status bar is not hidden. In a navigation controller it sits below the navigation bar. Because the navigation bar has a different height in landscape, the Top Layout Guide moves with the bar when the device is rotated. That makes it easy to place views relative to the navigation bar. There is also a Bottom Layout Guide that is used for the tab bar and toolbars.

This view needs four constraints to keep it in place. Unlike a button or label, a plain UIView does not have an intrinsic content size. There must always be enough constraints to determine the position and size of each view, so this view also needs constraints to tell it what size it needs to be.

You may wonder, where are these size constraints? In this case, the size of the view is implied by the size of the superview. The constraints in this layout are two Horizontal Spaces and two Vertical Spaces, and these all have fixed lengths. You can see this in the Document Outline:

RW auto layout ios9 2015-09-06 at 8.31.17 PM

The width of the green view is calculated by the formula “width of superview minus (80 + 80)” and its height by the formula “height of superview minus (13 + 163)”. The space constraints are fixed, so the view has no choice but to resize. (Again, your values may be different depending on where you put the view.)

When you rotate the app, the dimensions of the superview change, so the formula changes with it.

 

You may not always want your UIView to resize when the device rotates, so you can use constraints to give the view a fixed width and/or height. Let’s do that now. Select the green view and click the Pin button; in the popup put checkmarks in front of Width and Height.

RW auto layout iOS 9 2015-09-06 at 8.34.44 PM

Click Add 2 Constraints to finish. You have now added two new constraints to the view, a 160 point Width constraint and a 284 point Height constraint:

RW autolayout iOS 9 2015-09-06 at 8.35.54 PM

Because Width and Height apply to just this view, they are located in the Document Outline under the View itself. Usually, constraints express a relationship between two different views – for example, the Horizontal and Vertical Space constraints are between the green view and its superview – but you can consider the Width and Height constraints to be a relationship between the green view and itself.

Run the app. Yup, looks good in portrait. Now flip over to landscape. Whoops! Not only does it not look like you wanted – the view has changed size again – but the Xcode debug pane has dumped a nasty error message that looks like this at the top:

Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: (1) look at each constraint and try to figure out which you don't expect; 
(2) find the code that added the unwanted constraint or constraints and fix it. 
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, 
refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    <NSLayoutConstraint:0x7f8deac8a910 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7f8deae26b50(667)]>"
)
 
Will attempt to recover by breaking constraint....

Remember when I said that there must be enough constraints so that Auto Layout can calculate the positions and sizes of all the views? Well, this is an example where there are too many constraints. Whenever you get the error “Unable to simultaneously satisfy constraints”, it means that your constraints are conflicting somewhere.

Let’s look at those constraints again:

RW auto layout ios9 2015-09-06 at 8.36.49 PM

There are six constraints set on the green view, the four Spacing constraints you saw earlier (1-4) and the new Width and Height constraints that you have just set on it (5 and 6). So where is the conflict?

In portrait mode there shouldn’t be a problem because the math adds up. If you add the lengths of the Horizontal Space and Width constraints, then you should also end up at the width of the super view. Like wise for the vertical constraints.

But when you rotate the device to landscape, the math is off and Auto Layout doesn’t know what to do.

The conflict here is that either the width of the view is fixed and one of the margins must be flexible, or the margins are fixed and the width must be flexible. You can’t have both. So one of these constraints has to go. In the above example, you want the view to have the same width in both portrait and landscape, so the trailing Horizontal Space has got to go.

Remove the Horizontal Space at the right and the Vertical Space at the bottom. The storyboard should look like this:

RW auto layout iOS 9 2015-09-06 at 8.39.33 PM

Now the view has just the right number of constraints to determine its size and position — no more, no less. Run the app and verify that the error message is gone and that the view stays the same size after rotating.

Even though Interface Builder does its best to warn you about invalid layouts, it cannot perform miracles. It will warn you when there are too few constraints but it doesn’t fare so well at detecting layouts with too many constraints.

At least at run time, Auto Layout spits out a detailed error message when something is wrong. You can learn more about analyzing these error messages and diagnosing layout problems in “Intermediate Auto Layout” in iOS 6 by Tutorials.

Painting the portraits

Drag a Label onto the green view. Notice that now the guides appear within that green view, because it will be the superview for the label.

RW auto layout iOS 92015-09-06 at 8.42.21 PM

Position the label against the bottom margin, horizontally centered against the guides. Add a space constraint to anchor the label against the bottom of the green view, at 20 points distance. The quickest way is to use the Pin button and just select the T-bar at the bottom:

RW iOS 9 auto layout 2015-09-06 at 8.43.14 PM

Now add a constraint to center the label horizontally with the Alignment menu. Put a checkbox in front of Horizontal Center in Container and then click Add 1 Constraint. The storyboard should now look like this:

RW auto layout iOS 9 2015-09-06 at 8.44.57 PM

Notice that these two new Horizontal and Vertical Space constraints are listed under the green view’s own Constraints section, not in the main view.

Drag a new Image View object onto the storyboard, and make the layout look like this:

RW auto layout iOS 9 2015-09-06 at 8.46.32 PM

The image view is pinned to the top, left, and right edges of its superview, but its bottom is connected to the top of the label with a standard spacing of 8 points. If you’re unsure of how to do this, then follow these steps.

1. Drag the image view into the green view but don’t worry too much about its size or position:

RW auto layout iOS 9 2015-09-06 at 8.47.17 PM

2. With the image view selected, press the Pin button and choose the following options:

RW auto layout iOS 9 2015-09-06 at 8.48.12 PM

The top, left, and right T-bars are set to 20 points but the bottom one is set to 8 points. Important: For Update Frames you should choose Items of New Constraints. If you had left this to the default of None, the storyboard would look something like this:

RW auto layout iOS 9 2015-09-06 at 8.49.01 PM

The constraints you chose result in a different frame than the image view’s current position and size. But if you choose Items of New Constraints, Interface Builder will automatically adjust the frame as it adds the constraints and everything looks dandy:

RW auto layout iOS 9 2015-09-06 at 8.49.49 PM

Of course, if you do end up with a misplaced frame, you can always use the Resolve Auto Layout Issues button to fix it:

Screen Shot 2015-09-06 at 8.50.42 PM

Adding Images

Next, download the resources for this tutorial and unzip the file. You will find an Images folder – add this folder into your project. Set Ray.png as the image for the image view, change the image view’s mode to Aspect Fit and set its background color to white. Change the label’s text to say “Ray”.

Your layout should now look like this:

RW auto layout iOS 9 2015-09-06 at 8.56.11 PM

Notice that the constraints inside the green view turned to orange. This happened the moment you set the image on the image view. How come your layout is suddenly invalid? Fortunately you can take the guesswork out of it and let Xcode tell you exactly what’s wrong.

Click the small red arrow next to View Controller Scene in the Document Outline to view the issues:

RW auto layout iOS 9 2015-09-06 at 8.57.27 PM

You have a Content Priority Ambiguity error. That’s quite the mouthful. This is what it means: If neither the image view nor the label has a fixed height, then Auto Layout doesn’t know by how much to scale each if the height of the green view should change. (Interface Builder seems to ignore for now that the green view actually has a fixed Height constraint set on it.)

Let’s say at some point in your app the green view becomes 100 points taller. How should Auto Layout distribute these new 100 points among the label and the image view? Does the image view become 100 points taller while the label stays the same size? Or does the label become taller while the image view stays the same? Do they both get 50 points extra, or is it split 25/75, 40/60, or in some other possible combination?

If you don’t solve this problem somehow then Auto Layout is going to have to guess and the results may be unpredictable.

The proper solution is to change the Content Compression Resistance Priority of the label. As the name suggests, this value determines how resistant a view is to being compressed, or shrunk. A higher value here means the view will be less likely to be compressed and more likely to stay the same.

Similarly, the Content Hugging Priority determines how resistant a view is to being expanded. You can imagine “hugging” here to mean “size to fit” – the bounds of the view will “hug” or be close to the intrinsic content size. A higher value here means the view will be less likely to grow and more likely to stay the same.

Go into the Size inspector for the label and set the vertical Content Compression Resistance Priority to 751. That makes it one higher than the priority of the image view. While you’re at it, set the vertical Content Hugging Priority to 252. When the superview changes size, that means the image view will be the one to resize, and the label will stay pretty much the same size.

RW auto layout iOS 9 2015-09-06 at 8.58.26 PM

Use the Resolve Auto Layout Issues button and choose Update Frames to fix any lingering issues with the labels placementThe T-bars should turn blue again and the Auto Layout warnings are gone.

Adding the other heads

Drag the green view into the main view’s top-left corner. Recall that the green view had Horizontal Space and Vertical Space constraints that determined its position in the parent view. It still has those and they cause the frame of the view to be misaligned.

RW autolayout iOS 9 2015-09-06 at 8.59.35 PM

To fix this, use the Resolve Auto Layout Issues button and choose Update Constraints. Previously you used Update Frames, which moved and resized the view the match the constraints. Here you want to do the opposite: you want the constraints to update to match the frame.

Note that the Vertical Space at the top is now negative. That happens because this constraint is connected to the Top Layout Guide. But there’s no reason why constraints cannot have negative values, so you can leave this as is. (If it bothers you, delete that “Vertical Space (-20)” constraint and pin the view to the top of the window.)

The Horizontal Space now has size 0 and is represented by a thick blue line at the left edge of the window. So even though the view sits completely in the corner, it still needs constraints to anchor it there:

RW auto layout iOS 9 2015-09-06 at 9.01.49 PM

We are now going to start adding more views to show more people. Before you do this, change the simulated size of the view controller to iPhone 4-inch. Then, select the green view and tap ⌘D to duplicate it. Move the duplicate into the top-right corner:

RW auto layout iOS 9  2015-09-06 at 9.03.59 PM

Notice that the T-bars are orange. When you made the duplicate, it apparently lost its constraints for the X and Y position. To fix that, pin the view to the top and the right edges of the window (make sure to uncheck Constrain to margins).

Duplicate two more times and put these copies in the bottom-left and bottom-right corners, respectively. Again, pin these views to their corners.

Change the screen design to the following:

RW auto layout iOS 9 2015-09-06 at 9.08.38 PM

Those are some good-looking programmers! :-)

Run the app on a 4-inch iPhone simulator (iPhone 5 or 5/s). It looks good in portrait, but not so much in landscape:
RW autolayout iOS 9 2015-09-06 at 9.11.34 PM

It should be pretty obvious what went wrong: you’ve set a fixed width and height on the four brightly-colored container views, so they will always have those sizes, regardless of the size of their superview.

Select the fixed Width and fixed Height constraints from all four views and delete them (this is easiest in the Document Outline). If you run the app now, you’ll get something like this:

RW autolayout iOS 9 2015-09-06 at 9.13.06 PM

Note: If you’re wondering why some of the views are larger than others, this is again related to the intrinsic content size. The size of the image determines how large the image view is; the size of the text determines how large the label is. Taken together with the constraints for the margins — 20 points on all sides — this determines the total size of each view.

This looks very much like the problem you solved in the introduction in part 1, so if you think back to how you solved that, you’ll recall that you gave the views equal widths and heights.

Select all four colored views. This is easiest in the Document Outline; hold and click on the four views. You can add the constraints in one go. In the Pin popup put checkmarks in front of Equal Widths and Equal Heights and then press Add 6 Constraints.

RW auto layout iOS 9 2015-09-06 at 9.17.54 PM

Run the app again and rotate the device. Hmm… still no good:

RW auto layout iOS 9 2015-09-06 at 9.16.40 PM

All the views do have the same height, and they also appear to have the same width, so your constraints are being met. It’s just not the width and height that you want them to have.

Just saying that all four views must have equal sizes is not enough to determine what those sizes should actually be, because Auto Layout does not know how these four views are connected to each other. They appear side-by-side in the design, but there are no actual constraints between them. Auto Layout does not know that it needs to split the window width between the “Ray” and “Matthijs” boxes.

If Auto Layout can’t figure this out by itself, you have to tell it.

To be related

Ctrl drag from the Ray box to the Matthijs box and choose Horizontal Spacing from the popup menu. Because the boxes are side-by-side, this adds a Horizontal Space constraint with size 0 between them, and that is enough to let Auto Layout know how these two views are related. Also put a Vertical Space between the Ray and Dennis Ritchie boxes using the same Ctrl drag technique.

Run the app again, and this time it looks all right:

Gallery app laid out correctly in landscape

Where To Go From Here?

If you’ve made it this far, congratulations – you now know what Auto Layout is all about, and have experimented with the basics! But there’s a lot left to learn…

To continue with more advanced topics, check out our Auto Layout video tutorial series, which covers everything you need to become an Auto Layout master. You might also be interested in our Adaptive Layout series as well.

If you have any questions or comments as you continue on you auto layout journey, please join the forum discussion below!

The post Auto Layout Tutorial in iOS 9 Part 2: Constraints appeared first on Ray Wenderlich.

Adaptive Layout Tutorial in iOS 9: Getting Started

$
0
0
Learn how to make your apps use Adaptive Layout!

Learn how to make your apps use Adaptive Layout!

Update 9/22/15: This tutorial was updated to iOS 9, Xcode 7, and Swift 2 by Sam Davies.

The introduction of Adaptive Layout caused a huge paradigm shift for iOS app designers. When designing your app, you can now create a single layout, which works on all current iOS devices – without crufty platform-specific code!

This tutorial serves as your introduction to Adaptive Layout. You’ll learn about universal storyboards, size classes, layout and font customizations and the ultra-useful Preview Assistant Editor.

You’ll create the user interface for a simple weather app – and you’ll build it completely from scratch. If you’re not a fan of Auto Layout, don’t worry; the first part of this tutorial provides you with a gentle step-by-step approach to build an interface using Auto Layout. You’ll be amazed at how much you can accomplish without writing a single line of code!

Universal Storyboards

Universal Storyboards are the first step on your journey towards Adaptive Layout. The same storyboard can now be used for both iPads and iPhones. There’s no need to keep per-device storyboards in sync with each other – a monotonous process which can be fraught with error.

Open Xcode and select Create a new Xcode project:

Select iOSApplicationSingle View Application, then click Next:

Set Product Name to AdaptiveWeather, Language to Swift and ensure that Devices is set to Universal:

Once the project opens, take a look at the Project Navigator and you’ll see just a single storyboard file:

Main.storyboard is the single storyboard for all devices, no matter their screen size. Open the storyboard and you’ll see that it contains a single view controller, but of an unfamiliar size:

That’s right – it’s now square! Storyboards in previous versions of Xcode had to match the screen size of the target device. This clearly isn’t possible with the “one storyboard to rule them all” approach, so the storyboard is given an abstract size instead.

The Use Size Classes option, found in the File Inspector, enables this new format for your project; select the storyboard, open the File Inspector and you’ll see the checkbox option as shown below:

This option is enabled by default for all new iOS projects. You can turn this option on yourself when upgrading your old projects to use the new storyboards.

Setting Up Your Storyboard

To start, open Main.storyboard and drag an Image View from the Object Library onto the view controller canvas. In the Size Inspector, set the X position to 150 and the Y position to 20. Set the Width to 300 and the Height to 265.

Next, drag a View from the Object Library and place it below the image view. In the Size Inspector, set the X position to 150 and the Y position to 315. Set the Width to 300 and the Height to 265.

Select the view you just added, open the Identity Inspector and enter TextContainer in the Label field. Note that the Document pane might be collapsed – if so press the Show button to reveal it. This gives the view a name and makes it easier to see in the Document Inspector. This view will eventually hold the city and temperature labels of your app.

It’s often hard to see views after dragging them in from the Object Library as their default background color is white, the same as the view controller’s view. To fix this, select the view controller’s view, open the Attributes Inspector and set its background color to #4AABF7.

Next, select the TextContainer view and set its background color to #3780BA.

Your view controller should now look like the screenshot below:

These two views are the only direct descendants of the view controller’s view; your task now is to give them some layout constraints.

Adding Layout Constraints

Select the image view and press the Align button in the auto layout toolbar. Check the Horizontal Center in Container, ensuring the value is set to 0, and click Add 1 Constraint.

Then press the Pin button and add a top spacing to nearest neighbor constraint of 0, as so:

The constraints you added above ensure that the image view has a fixed size margin from the top and that it is centered left to right. Now you need to configure the space between the image view and the text container view. Ctrl-drag from the image view down to the container view, like so:

This displays the constraint context menu again. Select Vertical Spacing:

This constraint determines the amount of vertical space between the bottom of the image view and the top of the TextContainer view.

Select your image view and open the Size Inspector to see how it looks now:

You’ll see the three constraints you just added to your layout; you can easily configure each constraint from within the size inspector. Press the Edit button in the Bottom Space To: TextContainer constraint; you’ll be presented with a dialog to configure the constraint properties. Set Constant equal to 20:

Click away from the dialog to close it.

You’ve already configured your TextContainer view to have a gap of 20 points from the bottom edge of the image view, but you also need to add constraints to the other three sides of the view.

Select your TextContainer view then click the Pin icon in the bottom bar to show the Add New Constraints dialog. In the Spacing to nearest neighbor section, set the left, right and bottom spacing to 0. Ensure that Constrain to margins is unchecked; this removes padding from around your view.

For reference, the dialog should look like the following:

Click Add 3 constraints to add the new constraints to your view. This pins the text container view to the left, right and bottom edges of the view controller’s view.

Your storyboard should now look like the screenshot below:

You’ll notice a few orange and a few red constraints on your view; this indicates there are issues with these constraints that need your attention. It’s possible to have the storyboard automatically update the frames of the contained views to satisfy these constraints, but if you do this right now your image view will shrink to zero size.

That’s because your image view doesn’t yet have any content – which means it has an intrinsic height and width of zero. Auto Layout relies on a view’s intrinsic size to determine its width and height if no physical width or height constraints are supplied.

In the Project Navigator, open Images.xcassets. Download cloud_images.zip and unzip it. Inside you’ll find three files. Select all three of them in Finder, and drag them to the empty pane on the right hand side of the asset catalog:

This will create a new image set, and assign the three images appropriately:

You can now use your image set to populate your image view. Head back over to Main.storyboard and select your image view. Switch to the Attributes Inspector, type the name cloud into the Image field, and select Aspect Fit in the ViewMode drop down list, like so:

Your storyboard now should look like the following:

There’s your image – but as you can tell from the orange constraints, you still have a bit of work to do. First select the view controller’s view in the Document Outline:

Then select the Resolve Auto Layout Issues icon in the bottom bar. Finally, select All ViewsUpdate Frames from the dialog as shown:

The view controller re-arranges the views to automatically resolve the unsatisfied constraints, which should leave your storyboard looking like this:

The Preview Assistant Editor

Normally you’d now build and run your project on the iPad, iPhone 4s, 5s, 6 and 6 Plus simulators – in both orientations – in order to test this new universal storyboard. This process is laborious at best, but Xcode 6 gives you a much better option with the new Preview Assistant Editor.

Open Main.storyboard then click ViewAssistant EditorShow Assistant Editor. This splits the screen into two panes. Click Automatic in the Jump Bar and choose Preview from the drop down menu. Finally select Main.storyboard:

The assistant editor now displays a preview of the storyboard on the 4-inch iPhone screen, as shown below:

Rotate this preview by clicking on the rotation icon at the bottom of the preview panel alongside the preview name, in this case iPhone 4-inch. The preview will now display in the landscape orientation:

This is a huge improvement over firing up multiple simulators – but wait! There’s more! Click the + icon at the bottom left of the preview editor to reveal the list of available previews:

Select iPhone 5.5-inch and then iPad from this list to add their previews into the mix as shown below:

Notice anything odd about the landscape iPhone preview? That’s right – the cloud image is far too big. To fix this you’ll add a new constraint to the image view.

Head back to the storyboard. Ctrl-drag from the image view to the view controller’s view to create a new constraint. From the context menu, select Equal Heights:

A number of constraints on the storyboard are now colored red. This is because the constraint you just added conflicts with the existing constraints, as it’s not possible for the image view to have the same height as the view controller’s view and maintain the vertical margins you created earlier.

Select the constraint you just added in the Document Outline and open the Attributes Inspector. If the First Item is not set to cloud_small.Height then select Reverse First and Second Item in the First Item dropdown as shown below:

Next, select the Relation to be Less Than or Equal set Multiplier to 0.40 as shown below:

This means that the cloud image will either be the intrinsic size of the image, or be 40% of the height of the screen, whichever is smaller.

You’ll notice that the preview pane automatically refreshes as soon as you update the constraint, as demonstrated below:

So you can get multiple device previews that also update automatically? Yep, the new Preview Assistant Editor is amazingly useful!

Since this is supposed to be a weather app, you should probably add some labels to show the city name and the current temperature.

Adding Content to the TextContainer

In Main.storyboard drag two Labels from the Object Library onto the TextContainer view, and arrange them roughly as shown below:

Select the topmost label and use the Align and Pin menus to center the label horizontally and add a top spacing to nearest neighbor of 10, as shown below:

Next, select the Attributes Inspector and set Text to Cupertino, Color to White, and the font to System, Thin with Size of 150.

You’ve probably noticed the text is currently illegible, and this is due to the frame of the label – you’ll resolve this shortly.

Now select the other label, and again use the Align and Pin menus to center it horizontally, and set a bottom space to nearest neighbor of 10. Check that the Size Inspector matches the following:

Use the Attributes Inspector to set the Text to 28C, the color to White, and the font to System, Thin with a size of 250.

Now you need to resolve the label frame issues mentioned earlier. Select the view controller’s view, click the Resolve Auto Layout Issues button at the bottom of the storyboard, and select All ViewsUpdate Frames. You’ll see the storyboard update:

The labels overlap a little in the storyboard, which isn’t the look you’re going for. However, take a look at the preview in the assistant editor before you fix anything. The iPad version is looking pretty good:

Predictably, the font size is far too big for the iPhone:

You’ll correct these sizing issues in the next section of this Adaptive Layout tutorial.

Size Classes

Universal storyboards are great – but you’ve already discovered that creating one single layout for all displays is a bit of a challenge. However, Adaptive Layout has a few more tools and tricks to solve these issues.

One of the core concepts behind adaptive layout is size classes. A size class is a property applied to any view or view controller that represents the amount of content that can be displayed in a given horizontal or vertical dimension.

Xcode provides two size classes: Regular and Compact. Although they are related to the physical dimensions of a view, they also represent the semantic size of the view.

The following table shows how the size classes apply to the different devices and orientations:

Screen Shot 2014-09-23 at 10.53.49 AM

These are the size classes that the device hands down to the app. However, you can override these size classes at any point in the view hierarchy. This can be useful when using a view controller in a container which is significantly smaller than the screen.

Size Classes and You

What does that mean for you and the design of your app? Although your app is aware of size classes, the layout you’ve built is size class agnostic – that is, your layout remains the same for all size classes.

This is an important point when it comes to the design phase of your adaptive layout. You should build a base layout first and then customize each specific size class based on the individual needs of that size class. Don’t treat each of the size classes as a completely separate design. Think of an adaptive layout as a hierarchy, in which you put all of the shared design into the parent and then make any necessary changes in the child size classes.

There’s been almost no mention of configuring layouts for specific devices so far. This is because a core concept of adaptive layout is that size classes are abstracted away from device-specific characteristics. This means that a view that supports adaptive layout could be used in a full-screen view controller as well as in a contained view controller, albeit with different appearances.

This also benefits Apple, as there is room to expand the range and characteristics of their devices without forcing developers and designers to re-engineer their apps.

You’ll use size classes to customize the landscape layout for iPhone since the current layout doesn’t cope well when restricted vertically.

Working with Class Sizes

Click on w Any h Any in the bottom bar in the Interface Builder; you’ll see the size class selector as shown below:

Here you can choose which size class to show in the storyboard by selecting cells within the grid. There are nine possible options: you have three choices for vertical (any, regular or compact) and three for horizontal, resulting in a total of 3×3 = 9 size class combinations.

Note: There is a slight discrepancy in nomenclature. Size classes are always referred to as horizontal and vertical. However, IB uses the terms width and height. There’s an obvious parallel (width = horizontal; height = vertical); just be aware that there are two terms for the same concept.

Your current layout doesn’t work properly for compact heights. To fix this, choose the cells shown below to select the Any Width | Compact Height size class:

You’ll immediately notice two changes in the editor:

  1. The shape of the canvas changes to represent the new size class
  2. The bottom bar turns a rather fetching shade of blue. This indicates that you’re now working on a size-class specific layout

In order to change the layout, you’ll need to temporarily change a few constraints. In Auto Layout terminology this is known as installing and uninstalling constraints. A constraint is installed if it is currently active, whereas an uninstalled constraint is not currently active within the current size class.

Click on the image view to select it, and open the Size Inspector. You can see a summary of the constraints which affect this view:

Select the Align Center X to: Superview constraint by single clicking it, and then press Delete to uninstall the constraint for the current size class. The constraint immediately disappears from the storyboard view and becomes grayed out in both the Document Outline and the view’s Size Inspector:

Note: You might have to toggle from This Size Class to All in the Size Inspector to see the uninstalled constraint.

Double click on the uninstalled constraint in the Size Inspector to select the constraint . There’s an additional line at the bottom as shown below:

This indicates the constraint is installed for the base layout, but not for the Any Width | Compact Height layout – that is, the one you’re currently editing.

Repeat the same procedure to uninstall the other three constraints associated with the image view. Your document outline and image view’s Size Inspector should resemble the following images:

Now you can add the constraints required for this size class. Use the Align and Pin menus to Vertically Center in the Container, and to set a left spacing to nearest neighbor of 10:

Ctrl-drag from the image view to the view controller’s view and then select Equal Widths in the popup menu.

Open the Size Inspector for the image view and double-click the Equal Width to: Superview constraint to reveal its properties. If the First Item isn’t cloud_small.Width, then use the drop down menu to Reverse First and Second Item. Then update the Multiplier to equal 0.45.

The constraints for the image view are now setup correctly for all size classes, but the text container still needs a bit of attention. You’ll need to alter the constraints for this size class to move the label to the right hand side.

The TextContainer view has internal constraints to position the labels, which work fine as-is. However, the three external constraints – pinning it to the left, right and bottom sides of the view – aren’t quite working properly. To pin the view to the bottom right hand side of the parent view you’ll need to uninstall the left-hand constraint.

Select the left-hand constraint – labeled TextContainer.leader = leading in the document outline:

Then press Cmd-Delete to uninstall the constraint. As before, any uninstalled constraints appear grayed out in the Document Outline.

You now need to add two constraints to your TextContainer to position it correctly. The view should be half the width of its superview, the view controller’s view, and pinned to the top.

In theory, you could simply Ctrl-drag from the TextContainer view to the view controller’s view as you’ve been doing all along. However, in practice it’s often difficult to grab just the view when there’s content in the way. It’s much easier to use the document outline to do your dirty work.

Ctrl-drag from TextContainer in the document outline up to the view controller’s view:

Shift-click on Top Space to Top Layout Guide and Equal Widths. Click Add Constraints to create the new constraints:

Open the Size Inspector for your TextContainer and update the two new constraints you’ve just created as follows:

  • Top Space to: Top Layout Guide should have a Constant of 0
  • Equal Width to: Superview should have a Multiplier of 0.5. Note that you might have to switch the first and second items in this constraint, as you have done before. Simply double click on the constraint and choose to Reverse First and Second Item.

Click the Resolve Auto Layout Issues icon in the bottom bar, and select All ViewsUpdate frames. The storyboard updates and displays the new layout as shown below:

The layout has changed completely; you’re closer to the finished product now. There are still a few issues to correct with the font sizes – you’ll address these in the next section.

Adaptive Fonts

The current font sizes in your TextContainer look pretty good in the iPad view using a container with regular size classes, but the font size is too large for compact size classes. Fear not – it’s also possible to override font sizes within your size classes!

Note: Unlike layout overrides, changing the font configuration will always affect the base layout. Changes to the font configuration don’t respect the current size class overrides in interface builder. Instead, use the method outlined below.

Click the size class button in the bottom tool bar and select the squares to choose the base Any Width | Any Height size class. The bottom bar of your view turns gray to indicate that you’re back to the base layout.

Select the Cupertino text label and open the Attributes Inspector. Click the small + to the left of the Font:

This reveals a menu for selecting the size class combination in which you’ll override the font size. Navigate to Compact Width > Any Height like so:

This creates a second font selector box that will apply to the specified size class combination. Update the font size in the new selector box to 90:

Now select the temperature text label and repeat the same process; this time, set a font size override of 150 for Compact Width > Any Height.

The preview updates automatically to show the effect of the changes you made:

Well, it’s looking a little better, but the Cupertino label is clipped. Fiddling with the font sizes until it fits perfectly isn’t a particularly scalable option. Cupertino is quite a long place name, but Washington, D.C. is longer – and Kleinfeltersville, PA is longer still! What’s a designer to do?

Once again, Auto Layout comes to the rescue! You simply need to limit the width of the two text labels to match the width of the TextContainer. Ctrl-drag from the Cupertino label to the TextContainer, and select Equal Widths.

Repeat the process with the temperature label. The preview updates to show the effects of your changes as follows:

Hmm, having the text truncate is not exactly what you want. This is the default behavior for labels that contain more text than will fit in the available space. However, there is an option to automatically adjust the font size to fit the content.

Select the Cupertino label and open the Attributes Inspector. Change the AutoShrink drop down to Minimum font scale and ensure that it is set to 0.5. Also update the Text Alignment to Centered. Your Attributes Inspector should look as follows:

Repeat exactly the same procedure for the temperature label.

Take a look at the preview pane; the iPhone layouts look much better now:

Working with the preview editor is great, but this is probably a good time to build and run your project to see that everything is still working correctly. The iPhone screens look like everything is sizing correctly:

Congrats, you have learned the basic techniques of Adaptive Layout!

Where To Go From Here?

Here is the finished example project from this Adaptive Layout tutorial.

Take a moment to think about the app you’ve built (or the completed project you’ve downloaded). In particular, consider that it looks really good on all devices, in both orientations, using only one storyboard file!

If nothing else convinces you that Adaptive Layout is the way of the future, consider the fact that this layout will also look good on future devices that haven’t even been released yet.

The takeaway from this tutorial is that, as a developer, you will need to change your approach to app design. Rather than working towards pixel-based layouts, you should be considering the relationship between the UI elements on the screen.

If you want to learn more about Adaptive Layout, check out our Adaptive Layout video tutorial series, which takes you all the way from the beginning to an Adaptive Layout master!

In the meantime, if you have any questions or comments about this tutorial or Adaptive Layout in general, please join the forum discussion below!

The post Adaptive Layout Tutorial in iOS 9: Getting Started appeared first on Ray Wenderlich.

iOS Core Concept Tutorials Updated for iOS 9

$
0
0

iOS Core Concept Tutorials Updated for iOS 9!

As you probably know, during the iOS 9 feast our goal is to:

  • Release a new book each Wednesday
  • Release a new video tutorial series each Thursday
  • Release a new written tutorial every Friday, Monday, and Tuesday

Today, you get not one new tutorial – but three! We teamed up to update our iOS Core Concept Tutorials to iOS 9, so that we now have a nice set of beginner iOS 9 tutorials available on the site for free.

Here’s what came out today:

  • Caroline Begbie updated her Storyboards tutorial to iOS 9: [Part 1] [Part 2]
  • Bradley Johnson updated his Auto Layout tutorial to iOS 9: [Part 1] [Part 2]
  • Sam Davies updated his Adaptive Layout tutorial to iOS 9: [Part 1]

We hope you enjoy these updated tutorials – and be sure to come back tomorrow, for the release of iOS Animations by Tutorials Second Edition!

The post iOS Core Concept Tutorials Updated for iOS 9 appeared first on Ray Wenderlich.

iOS Animations by Tutorials Second Edition Now Available!

$
0
0
iOS Animations by Tutorials Second Edition Now Available!

iOS Animations by Tutorials Second Edition Now Available!

It’s Wednesday during the iOS 9 Feast, and you know what that means – book release day! :]

Today, Marin Todorov and I are happy to announce that iOS Animations by Tutorials Second Edition is now available for download.

iOS Animations by Tutorials teaches you how to make delightful animations in your apps with Swift 2.

You start with basic view and layer animations, move all the way through Auto Layout animations, view controller transitions, and finally look into third party animation libraries like Facebook’s Pop.

In the second edition, Marin has updated the book for iOS 9, Xcode 7, and Swift 2, and added 3 brand new chapters to the book:

  • Layer Springs: A tour of the shiny new CASpringAnimation class in iOS 9, which allows you to easily create layer spring animations.
  • Replicating Animations: Introduces the little known but powerful CAReplicatorLayer class. This is one of Marin’s favorite animation classes so you’re in for some fun!
  • Easy Animation: Learn how to get started with a third party animation library written by Marin that makes building complex animations really easy.

This is a free update for existing PDF customers – you can download the update on your My Loot page.

If you don’t have a copy of the book yet, now’s the time; since the book has been fully updated, there’s no better time to level-up your iOS animation skills. Be sure to grab your copy and have some fun! :]

Marin and I hope you enjoy iOS Animations by Tutorials second edition, and look forward to seeing some amazing iOS animations in your apps.

The post iOS Animations by Tutorials Second Edition Now Available! appeared first on Ray Wenderlich.

New Video Tutorial Series: Intermediate Swift 2

$
0
0
A Swift 2 update for the iOS 9 Feast!

A Swift 2 update for the iOS 9 Feast!

As part of this year’s iOS 9 Feast, we are releasing a new video tutorial series every Thursday.

Last week, we updated our popular 12-part Beginning Swift video tutorial series to Swift 2. Many of you were able to guess what’s coming next! :]

This week, we are happy to release a Swift 2 update to our Intermediate Swift series by Greg Heo.

In this update, Greg has refactored the materials to use playgrounds. That means you can learn the Swift 2 language without having to worry about platform-specific stuff, and then make the move onto iOS or OS X development when you’re ready.

The intermediate series goes deeper into the language with more advanced use of properties and methods, the type system, and generics. You’ll also learn about some exciting new additions in Swift 2 such as protocol extensions and the new error handling system.

All 12 parts of the series are available today. You can check them out here:

Now that both the Beginner and Intermediate Swift series have been freshly updated for Swift 2, there’s no excuse not to get up-to-speed with Swift if you haven’t already! :]

So what are you waiting for – check out the videos and dive into Swift 2! And be sure to come back next Thursday for another new video tutorial series.

The post New Video Tutorial Series: Intermediate Swift 2 appeared first on Ray Wenderlich.

iOS Animation Tutorial: Getting Started

$
0
0
Note from Ray: This is an abbreviated version of a chapter from iOS Animations by Tutorials Second Edition to give you a sneak peek of what’s inside the book, released as part of the iOS 9 Feast. This tutorial is fully up-to-date for iOS 9, Xcode 7, and Swift 2. We hope you enjoy!

An iOS animation tutorial for the iOS 9 Feast!

Animation is a critical part of your iOS user interfaces. Animation draws the user’s attention toward things that change, and adds a ton of fun and polish to your apps UI.

Even more importantly, in an era of “flat design”, animation is one of the key ways to make your app stand apart from others.

In this tutorial, you’ll learn how to use UIView animation to do the following:

  • Set the stage for a cool animation.
  • Create move and fade animations.
  • Adjust the animation easing.
  • Reverse and repeat animations.

There’s a fair bit of material to get through, but I promise it will be a lot of fun. Are you up for the challenge?

001_ChallengeAccepted

All right – time to get animating!

Getting Started

Start by downloading the BahamaAir Starter for this tutorial, which represents the login screen for a fictional airline – “Bahama Air”.

Build and run your project in Xcode and you’ll see the following:

002_LoginScreen

The app doesn’t do much right now – it just shows a login form with a title, two text fields, and a big friendly button at the bottom.

There’s also a nice background picture and four clouds. The clouds are already connected to outlet variables in the code.

Open ViewController.swift and have a look inside. At the top of the file you’ll see all the connected outlets and class variables. Further down, there’s a bit of code in viewDidLoad(), which initializes some of the UI. The project is ready for you to jump in and shake things up a bit!

Enough with the introductions – you’re undoubtedly ready to try out some code!

Your First Animation

Your first task is to animate the form elements onto the screen when the user opens the application. Since the form is now visible when the app starts, you’ll have to move it off of the screen just before your view controller makes an appearance.

Add the following code to viewWillAppear():

heading.center.x  -= view.bounds.width
username.center.x -= view.bounds.width
password.center.x -= view.bounds.width

This moves each of the form elements outside the visible bounds of the screen, like so:

003_FormElements

Since the code above executes before the view controller appears, it will look like those text fields were never there in the first place.

Build and run your project to make sure your fields truly appear off-screen just as you had planned:

004_MovedElements

Perfect – now you can animate those form elements back to their original locations via a delightful animation.

Add the following code to the end of viewDidAppear():

UIView.animateWithDuration(0.5, animations: {
  self.heading.center.x += self.view.bounds.width
})

To animate the title in you call the UIView class method animateWithDuration(_:animations:). The animation starts immediately and animates over half a second; you set the duration via the first method parameter in the code.

It’s as easy as that; all the changes you make to the view in the animations closure will be animated by UIKit.

Build and run your project; you should see the title slide neatly into place like so:

005_TitleSlide

That sets the stage for you to animate in the rest of the form elements.

Since animateWithDuration(_:animations:) is a class method, you aren’t limited to animate just one specific view; in fact you can animate as many views as you want in your animations closure.

Add the following line to the animations closure:

self.username.center.x += self.view.bounds.width

Build and run your project again; watch as the username field slides into place:

006_UsernameSlide

Delayed Animations

Seeing both views animate together is quite cool, but you probably noticed that animating the two views over the same distance and with the same duration looks a bit stiff. Only kill-bots move with such absolute synchronization! :]

Wouldn’t it be cool if each of the elements moved independently of the others, possibly with a little bit of delay in between the animations?

First remove the commented out line of code below that animates username:

UIView.animateWithDuration(0.5, animations: {
  self.heading.center.x += self.view.bounds.width
  // self.username.center.x += self.view.bounds.width
})

Then add the following code to the bottom of viewDidAppear():

UIView.animateWithDuration(0.5, delay: 0.3, options: [], animations: {
  self.username.center.x += self.view.bounds.width
}, completion: nil)

The class method you use this time looks familiar, but it has a few more parameters to let you customize your animation:

  1. duration: The duration of the animation.
  2. delay: The amount of seconds UIKit will wait before it starts the animation.
  3. options: A set of animation options allowing you to customize a number of aspects about your animation. You’ll learn more about this parameter later on, but for now you can pass [] to mean “no options.”
  4. animations: The closure expression to provide your animations.
  5. completion: A code closure to execute when the animation completes; this parameter often comes in handy when you want to perform some final cleanup tasks or chain animations one after the other.

In the code you added above you set delay to 0.3 to make the animation start just a hair later than the title animation.

Build and run your project; how does the combined animation look now?

007_Combined

Ahh – that looks much better. Now all you need to do is animate in the password field.

Add the following code to the bottom of viewDidAppear():

UIView.animateWithDuration(0.5, delay: 0.4, options: [], animations: {
  self.password.center.x += self.view.bounds.width
}, completion: nil)

Here you’ve mostly mimicked the animation of the username field, just with a slightly longer delay.

Build and run your project again to see the complete animation sequence:

008_CompleteAnimation

That’s all you need to do to animate views across the screen with a UIKit animation!

That’s just the start of it – you’ll be learning a few more awesome animation techniques in the remainder of this tutorial!

Animatable Properties

Now that you’ve seen how easy animations can be, you’re probably keen to learn how else you can animate your views.

This section will give you an overview of the animatable properties of a UIView, and then guide you through exploring these animations in your project.

Not all view properties can be animated, but all view animations, from the simplest to the most complex, can be built by animating the subset of properties on a view that do lend themselves to animation, as outlined in the section below.

Position and Size

009_PositionAndSize

You can animate a view’s position and frame in order to make it grow, shrink, or move around as you did in the previous section. Here are the properties you can use to modify a view’s position and size:

  • bounds: Animate this property to reposition the view’s content within the view’s frame.
  • frame: Animate this property to move and/or scale the view.
  • center: Animate this property when you want to move the view to a new location on screen.

Don’t forget that Swift lets you adjust single members of structures as well. This means you can move a view vertically by changing center.y or you can shrink a view by decreasing frame.size.width.

Appearance

010_Appearance

You can change the appearance of the view’s content by either tinting its background or making the view fully or semi-transparent.

  • backgroundColor: Change this property of a view to have UIKit gradually change the tint of the background color over time.
  • alpha: Change this property to create fade-in and fade-out effects.

Transformation

011_Transformation

Transforms modify views in much the same way as above, since you can also adjust size and position.

  • transform: Modify this property within an animation block to animate the rotation, scale, and/or position of a view.

These are affine transformations under the hood, which are much more powerful and allow you to describe the scale factor or rotation angle rather than needing to provide a specific bounds or center point.

These look like pretty basic building blocks, but you’ll be surprised at the complex animation effects you’re about to encounter!

Animation Options

Looking back to your animation code, you were always passing in [] to the options parameter.

options lets you customize how UIKit creates your animation. You’ve only adjusted the duration and delay of your animations, but you can have a lot more control over your animation parameters than just that.

There’s a list of options declared in the UIViewAnimationOptions set type that you can combine in different ways for use in your animations.

Repeating

You’ll first take a look at the following two animation options:

  • .Repeat: Include this option to makes your animation loop forever.
  • .Autoreverse: Include this option only in conjunction with .Repeat; this option repeatedly plays your animation in forward then in reverse.

Modify the code that animates the password field viewDidAppear() to use the .Repeat option as follows:

UIView.animateWithDuration(0.5, delay: 0.4,
  options: .Repeat, animations: {
  self.password.center.x += self.view.bounds.width
}, completion: nil)

Build and run your project to see the effect of your change:

012_Repeat

The form title and username field fly in and settle down in the center of the screen, but the password field keeps animating forever from its position off-screen.

Modify the same code you changed above to use both .Repeat and .Autoreverse in the options parameter as follows:

UIView.animateWithDuration(0.5, delay: 0.4,
  options: [.Repeat, .Autoreverse], animations: {
    self.password.center.x += self.view.bounds.width
}, completion: nil)

Note how if you want to enable more than one option you need to use the set syntax and list all options separated with a comma and enclose the list in square brackets.

If you only need a single option, Swift allows you to omit the square brackets as a convenience. However, you can still include them in case you add more options in the future – that means [] for no options, [.Repeat] for a single option, and [.Repeat, .Autorepeat] for multiple options.

Build and run your project again; this time the password field just can’t make up its mind about staying on the screen!

Animation Easing

In real life things don’t just suddenly start or stop moving. Physical objects like cars or trains slowly accelerate until they reach their target speed, and unless they hit a brick wall, they gradually slow down until they come to a complete stop at their final destination.

The image below illustrates this concept in detail:

013_Easing

To make your animations look more realistic, you can apply the same effect of building momentum at the beginning and slowing down before the end, known in general terms as ease-in and ease-out.

You can choose from four different easing options:

  • .Linear: This option applies no acceleration or deceleration to the animation.
  • .CurveEaseIn: This option applies acceleration to the start of your animation.
  • .CurveEaseOut: This option applies deceleration to the end of your animation.
  • .CurveEaseInOut: This option applies acceleration to the start of your animation and applies deceleration to the end of your animation.

To better understand how these options add visual impact to your animation, you’ll try a few of the options in your project.

Modify the animation code for your password field once again with a new option as follows:

UIView.animateWithDuration(0.5, delay: 0.4,
  options: [.Repeat, .Autoreverse, .CurveEaseOut], animations: {
    self.password.center.x += self.view.bounds.width
}, completion: nil)

Build and run your project; notice how smoothly the field decelerates until it reaches its rightmost position, before returning to the left side of the screen:

014_Deceleration

This looks much more natural since that’s how you expect things to move in the real world.

Now try the opposite: ease-in the animation when the field is still outside of the screen by modifying the same code as above to change the .CurveEaseOut option to .CurveEaseIn as follows:

UIView.animateWithDuration(0.5, delay: 0.4,
  options: [.Repeat, .Autoreverse, .CurveEaseIn], animations: {
    self.password.center.x += self.view.bounds.width
}, completion: nil)

Build and run your project; observe how the field jumps back from its rightmost with robotic vigor. This looks unnatural and isn’t as visually pleasing as the previous animation.

Finally give .CurveEaseInOut a try – it combines the two options you already know into one very natural looking easing. .CurveEaseInOut is also the default easing function UIKit applies to your animations.
You’ve seen how the various animation options affect your project and how to make movements look smooth and natural.

Now that you have some experience with animation curves, change the options on the piece of code you’ve been playing with back to nil:

UIView.animateWithDuration(0.5, delay: 0.4,
 options: [], animations: {
  self.password.center.x += self.view.bounds.width
}, completion: nil)

And that’s it – you now understand how to use UIView animation API – so go forth and add some cool animations in your apps!

Where To Go From Here?

Now that you know how basic animations work, you’re ready to tackle some more dazzling animation techniques.

Animating views from point A to point B? Pshaw – that’s so easy! :]

If you want to learn more, check out our book iOS Animations by Tutorials. In the book, which is fully updated for Swift 2.0 and iOS 9, you’ll learn how to animate with springs, transitions, keyframe animations, CALayer animations, Auto Layout constraint animations, view controller transition animations, and more.

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

The post iOS Animation Tutorial: Getting Started appeared first on Ray Wenderlich.


Readers’ App Reviews – September 2015

$
0
0
swipefight

Apps from rocking Readers!

While Apple was busy announcing tons of new products, you all were hard at work on tons of new apps!

September is once again stuffed with amazing apps from the RW Community.

I’ve installed them all and picked a few to share with you. There are so many I don’t have time to write about them all, so don’t miss out on the Honorable Mentions below as well.

This months community showcase includes:

  • An app to keep track of chores
  • A rock star manager simulator
  • A game made by yours truly
  • And of course, much more!

Keep reading to see the apps readers like you released this month!

Ciphers

Ciphers
Sometimes you just can’t rely on others to encrypt your important messages. Sometimes you’ve got to take matters into your own hands. Ciphers is here to help.

Ciphers supports more than ten common methods of encryption. Its got Enigma Machine, Caesar Cipher, Simple Reversals, and many more. May of the methods even have extensive options to adjust the cipher. Ciphers gives you a preview of your encrypted message live.

Ciphers also decrypts messages of the same type. The app makes it easy to download or share your encrypted message with a friend. Then they merely need to decrypt the message on the other side.

Air Hockey Dreams

Airhockey
Air Hockey Dreams is a great 3D air hockey game for your iPhone or iPad. The 3D really adds to the immersion factor.

Air Hockey Dreams lets you play 1 or 2 player. Single player is a campaign of sorts as you play against others for coins to level up your paddle or unlock new contenders. Multiplayer moves into a top down approach so two players can use the same device.

Air Hockey Dreams has 10 different opponents each with their own skills and table. There are 20 different paddles to buy that have special stats and abilities. In game power-ups will give you a boost when you need it.

Mouse Times – Florida

mousetimes
Mouse Times is a must have companion for your next trip to Disney World!

Mouse Times will help you plan out your whole trip to Disney World. It has ride images and descriptions for all four parks. You can see them on a map to find the closest ones or get directions to exactly the ride you’re looking for. Best of all, Mouse Times has up-to-date wait times and statuses for all rides throughout the park.

Each ride has information like height requirements, disability access, single rider line info, and fastpass availability. Build your favorites list before you arrive and use it as your hit list for the trip.

Get park tips and tricks to show you all the best places to eat and ride. And the best time of day to avoid the crowds. Apple Watch support makes sure you can leave your phone and your pocket and enjoy the day!

Swipefight

swipefight
Swipefight is a mix between of fighting game and a puzzle game. Solving the puzzle drives your fighter.

The puzzle you have to solve is a simple match-three style game. Each simple is a different attack. Match the right symbols to get just the right attacks. Stuff like four in a row, or shields offer special advantages. Combos have a whole new meaning as they unleash a fury of attacks on your opponent.

There are four fighters to choose from each with their own attacks. You can play title mode for glory or survival mode for the highscores.

WaveAmp

wavamp
WaveAmp lets you test your hearing with your iPhone. It also simulates hearing loss and hearing aids so you can see what its like.

WaveAmp lets you take the sensorineural hearing test and see your personal audiogram. You can compare it to normal, mild, and severe hearing loss audiograms to see how you do.

Once you know how good your hearing is, you can simulate hearing loss to see what it would be like. You can also simulate hearing aid effects on sound files to see how they help the situation. Live audiowaves give a visual of how these simulations affect the sounds. Its very cool to get an idea what hearing loss is like for those affected.

WaveAmp works best with a good pair of stereo headphones.

Rock Star Manager

rockstarmanager
Rock Star Manager puts you in charge of taking a musician from punk to rocker. Run the music business in the palm of your hand.

Rock Star Manager lets you hire a musician, produce their first album, and tour them around. If you play your cards right you’ll profit and be able to keep the gravy train rolling.

Its up to you how to best spend your money to build a rock legend. You can choose to practice, tour, record new stuff, advertise, or give lavish gifts. The right combination will make your musician a rock star! Earn money by touring and selling records. Make it into the top charts. Avoid news scandals. Managing a rock star has never been so fun.

Pinbox

pinbox
Pinbox will help you map everything. You can plan your next vacation, pinpoint your favorite spots around town, or anything.

Pinbox lets you store up to 2,500 pins! Pinbox lets you divide your pins into separate maps so you don’t have to weed through all your pins. You can also color code them, give them metadata, and take pictures. Great for documenting a trip as you go.

You can open your pins in Maps, Google Maps, or Waze for directions. Share your pins with others. And of course Pinbox supports Standard, Satellite, and Hybrid map views.

Best of all, Pinbox isn’t a social network. Its just an app for you to manage your own geobookmarks. No signup, Facebook, or account required.

PrizePal

prizepal
Is keeping track of your kid’s chores a hassle? PrizePal is here to help.

PrizePal is a chore tracker for both kids and parents. PrizePal lets parents assign & schedule tasks for children. Each chore can have a due date as well as points associated with it. Kids & Parents can get notifications when its time to do their chores. Parents can check on chore statuses, pending prizes, and more in their private dashboard protected by passcode.

As children complete tasks assigned to them they will collect points. Points can be traded in for prizes set by the Parents. Such a great idea to put kids in control of their own allowance by earning it daily.

W-O-R-D-S

Words
W-O-R-D-S is an all new word game with its own unique twist.

W-O-R-D-S gives you a new grid of letters each time you select one. This makes it challenging to plan your words and instead leaves you building words on the fly. Its a lot of fun because it makes each word a journey. The first letter you pick you have no idea what the final word will be. But by the 3rd or 4th letter you’re hoping for a certain letter in the next set!

W-O-R-D-S also has four powerups to help you craft the perfect words. Reset clears the current word and resets the grid. Double-Letter lets you choose 2 letters from the same grid. Skip gives you the next letter grid to choose from. And Swap generates a completely new grid of letters.

Speech Timer 4 Toastmasters

toastmasters
Speech Timer 4 Toastmasters is the perfect app for those keeping time at toastmaster’s clubs or conferences or anywhere speeches are timed.

Speech Timer 4 Toastmasters will let you time individual speeches and keep track of how well its going. By inputting a range of expected time, Speech Timer 4 Toastmasters can automatically change the screen from green to yellow to red to indicate things are taking a little too long. You can use just the cards on your own to help speakers know when its time to wrap up. Speech Timer 4 Toastmasters will vibrate the device or ring a bell incase you’re too enthralled in the speech to keep track of time.

Speech Timer 4 Toastmasters also has a round robin setting for small groups. Using it will make sure each speaker gets their fair share of talking time. Just set the time, and the app will buzz when its time for the next person entirely on its own.

Hand Prix

HandPrix_15

Note from Ray: This app is by Mr. App Reviewer Ryan Poolos himself! It wouldn’t be fair if he reviewed his own app, so I’m standing in to share my thoughts of the app :]

Hand Prix is a fun multiplayer racing game for the iPad where you race by swiping furiously at the screen.

As you race, the roads twist and turn, and you have to take care to swipe in the correct direction while avoiding obstacles and staying ahead of your enemies.

You can either play locally (against AI with clever names like Han Solo or Knuckleberry Finger) or play with friends on GameCenter. It has support for iOS 9’s brand new ReplayKit too!

With fun friendly art and music, Hand Prix is some frantic fun that is sure to make you smile. It’s free, so be sure to download it and try it out – and take a chance to review Ryan for a change! :]

Honorable Mentions

Every month I get way more submissions than I have time to write about. I download every app to try them out, but only have time to review a select few. I love seeing our readers through the apps submitted to me every month. Its not a popularity contest or even a favorite picking contest. I just try to share a snapshot of the community through your submissions. Please take a moment and try these other fantastic apps from readers like you.

Hidden – Touch-ID Notepad
Things to Learn – School Edition
Guess The Color – addictive puzzle game
Gravity Cube – Quickness is Key
Proxima R
Bubble Puff
Dans Dino Dilemma
Run Mati Run
Magnetismm
Intellie List
VoiceMe
Doodler
FunFlics
Color Worlds

Where To Go From Here?

Each month, I really enjoy seeing what our community of readers comes up with. The apps you build are the reason we keep writing tutorials. Make sure you tell me about your next one, submit here!

If you saw an app your liked, hop to the App Store and leave a review! A good review always makes a dev’s day. And make sure you tell them you’re from raywenderlich.com; this is a community of makers!

If you’ve never made an app, this is the month! Check out our free tutorials to become an iOS star. What are you waiting for – I want to see your app next month!

The post Readers’ App Reviews – September 2015 appeared first on Ray Wenderlich.

iOS Animation Tutorial: Custom View Controller Presentation Transitions

$
0
0
Note from Ray: This is an abbreviated version of a chapter from iOS Animations by Tutorials Second Edition to give you a sneak peek of what’s inside the book, released as part of the iOS 9 Feast. This tutorial is fully up-to-date for iOS 9, Xcode 7, and Swift 2. We hope you enjoy!

An iOS animation tutorial for the iOS 9 Feast!

Whether you’re presenting the camera view controller, the address book, or one of your own custom-designed modal screens, you call the same UIKit method every time: presentViewController(_: animated:completion:). This method “gives up” the current screen to another view controller.

The default presentation animation simply slides the new view up to cover the current one. The illustration below shows a “New Contact” view controller sliding up over the list of contacts:

01_iAT_addressbook

In this tutorial, you’ll create your own custom presentation transitions controller to replace the default animations and liven up tutorial’s starter project.

Getting Started

Download the starter project: BeginnerCook Starter, unarchive the zip file and open the Beginner Cook starter project. Select Main.storyboard to begin the tour:

image003

The first view controller (ViewController) contains the app’s title and main description as well as a scroll view at the bottom, which shows the list of available herbs.

The main view controller presents HerbDetailsViewController whenever the user taps one of the images in the list; this view controller sports a background, a title, a description and some buttons to credit the image owner.

There’s already enough code in ViewController.swift and HerbDetailsViewController.swift to support the basic application. Build and run the project to see how the app looks and feels:

image005

Tap on one of the herb images, and the details screen comes up via the standard vertical cover transition. That might be OK for your garden-variety app, but your herbs deserve better!

Your job is to add some custom presentation transitions to your app to make it blossom! You’ll replace the current stock animation with one that expands the tapped herb image to a full-screen view like so:

image007

Roll up your sleeves, put your developer apron on and get ready for the inner workings of custom presentation controllers!

Behind the Scenes of Custom Transitions

UIKit lets you customize your view controller’s presentation via the delegate pattern; you simply make your main view controller (or another class you create specifically for that purpose) adopt UIViewControllerTransitioningDelegate.

Every time you present a new view controller, UIKit asks its delegate whether or not it should use a custom transition. Here’s what the first step of the custom transitioning dance looks like:

image009

UIKit calls animationControllerForPresentedController(:_ presentingController:sourceController:); if that method returns nil UIKit uses the built-in transition. If UIKit receives an object instead, then UIKit uses that object as the animation controller for the transition.

There are a few more steps in the dance before UIKit can use the custom animation controller:

image011

UIKit first asks your animation controller (simply known as the animator) for the transition duration in seconds, then calls animateTransition() on it. This is when your custom animation gets to take center stage.

In animateTransition(), you have access to both the current view controller on the screen as well as the new view controller to be presented. You can fade, scale, rotate and manipulate the existing view and the new view however you like.

Now that you’ve learned a bit about how custom presentation controllers work, you can start to create your own.

Implementing Transition Delegates

Since the delegate’s task is to manage the animator object that performs the actual animations, you’ll first have to create a stub for the animator class before you can write the delegate code.

From Xcode’s main menu select File\New\File… and choose the template iOS\Source\Cocoa Touch Class.

Set the new class name to PopAnimator and make it a subclass of NSObject.

Open PopAnimator.swift and update the class definition to make it conform to the UIViewControllerAnimatedTransitioning protocol as follows:

class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {
 
}

You’ll see some complaints from Xcode since you haven’t implemented the required delegate methods yet, so you’ll stub those out next.

Add the following method to the class:

func transitionDuration(transitionContext: UIViewControllerContextTransitioning?)-> NSTimeInterval {
  return 0
}

The 0 value above is just a placeholder value for the duration; you’ll replace this later with a real value as you work through the project.

Now add the following method stub to the class:

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
 
}

The above stub will hold your animation code; adding it should have cleared the remaining errors in Xcode.

Now that you have the basic animator class, you can move on to implementing the delegate methods on the view controller side.

Open ViewController.swift and add the following extension to the end of the file:

extension ViewController: UIViewControllerTransitioningDelegate {
 
}

This code indicates the view controller conforms to the transitioning delegate protocol. You’ll add some methods here in a moment.

Find didTapImageView() in the main body of the class. Near the bottom of that method you’ll see the code that presents the details view controller. herbDetails is the instance of the new view controller; you’ll need to set its transitioning delegate to the main controller.

Add the following code right before the last line of the method that calls presentViewController:

herbDetails.transitioningDelegate = self

Now UIKit will ask ViewController for an animator object every time you present the details view controller on the screen. However, you still haven’t implemented any of UIViewControllerTransitioningDelegate’s methods, so UIKit will still use the default transition.

The next step is to actually create your animator object and return it to UIKit when requested.

Add the following new property to ViewController:

let transition = PopAnimator()

This is the instance of PopAnimator that will drive your animated view controller transitions. You only need one instance of PopAnimator since you can continue to use the same object each time you present a view controller, as the transitions are the same every time.

Now add the first delegate method to the extension in ViewController:

func animationControllerForPresentedController(
  presented: UIViewController,
  presentingController presenting: UIViewController,
  sourceController source: UIViewController) ->
  UIViewControllerAnimatedTransitioning? {
 
    return transition
}

This method takes a few parameters that let you make an informed decision whether or not you want to return a custom animation. In this tutorial you’ll always return your single instance of PopAnimator since you have only one presentation transition.

You’ve already added the delegate method for presenting view controllers, but how will you deal with dismissing one?

Add the following delegate method to handle this:

func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
  return nil
}

The above method does essentially the same thing as the previous one: you check which view controller was dismissed and decide whether to return nil and use the default animation, or return a custom transition animator and use that instead. At the moment you return nil, as you aren’t going to implement the dismissal animation until later.

You finally have a custom animator to take care of your custom transitions. But does it work?

Build and run your project and tap one of the herb images:

image013

Nothing happens. Why? You have a custom animator to drive the transition, but… oh, wait, you haven’t added any code to the animator class! :] You’ll take care of that in the next section.

Creating your Transition Animator

Open PopAnimator.swift; this is where you’ll add the code to transition between the two view controllers.

First, add the following properties to this class:

let duration    = 1.0
var presenting  = true
var originFrame = CGRect.zero

You’ll use duration in several places, such as when you tell UIKit how long the transition will take and when you create the constituent animations.

You also define presenting to tell the animator class whether you are presenting or dismissing a view controller. You want to keep track of this because typically, you’ll run the animation forward to present and in reverse to dismiss.

Finally, you will use originFrame to store the original frame rect of the image the user taps – you will need that to animate from the original frame to a full screen image and vice versa. Keep an eye out for originFrame later on when you fetch the currently selected image and pass its frame to the animator instance.

Now you can move on to the UIViewControllerAnimatedTransitioning methods.

Replace the code inside transitionDuration() with the following:

return duration

Reusing the duration property lets you easily experiment with the transition animation; you can simply modify the value of the property to make the transition run faster or slower.

Setting your Transition’s Context

It’s time to add some magic to animateTransition. This method has one parameter of type UIViewControllerContextTransitioning, which gives you access to the parameters and view controllers of the transition.

Before you start working on the code itself, it’s important to understand what the animation context actually is.

When the transition between the two view controllers begins, the existing view is added to a transition container view and the new view controller’s view is created but not yet visible, as illustrated below:

image015

Therefore your task is to add the new view to the transition container within animateTransition(), “animate in” its appearance, and “animate out” the old view if required.

By default, the old view is removed from the transition container when the transition animation is done:

image017

Adding a Pop Transition

To create the animations for your custom transition you need to work with the so called “container” view of the transition context. This is a temporary view (much like a scratchpad) that gets added to the screen only for the time while the transition takes place. You will create all your animations in this view.

Insert into animateTransition():

let containerView = transitionContext.containerView()!
 
let toView =
  transitionContext.viewForKey(UITransitionContextToViewKey)!
 
let herbView = presenting ? toView : transitionContext.viewForKey(UITransitionContextFromViewKey)!

containerView is where your animations will live, while toView is the new view to present. If you’re presenting, herbView is just the toView; otherwise it will be fetched from the context. For both presenting and dismissing, herbView will always be the view that you animate.

When you present the details controller view, it will grow to take up the entire screen. When dismissed, it will shrink to the image’s original frame.

Add the following to animateTransition():

let initialFrame = presenting ? originFrame : herbView.frame
let finalFrame = presenting ? herbView.frame : originFrame
 
let xScaleFactor = presenting ?
  initialFrame.width / finalFrame.width :
  finalFrame.width / initialFrame.width
 
let yScaleFactor = presenting ?
  initialFrame.height / finalFrame.height :
  finalFrame.height / initialFrame.height

In the code above, you detect the initial and final animation frames and then calculate the scale factor you need to apply on each axis as you animate between each view.

Now you need to carefully position the new view so it appears exactly above the tapped image; this will make it look like the tapped image expands to fill the screen.

Add the following to animateTransition():

let scaleTransform = CGAffineTransformMakeScale(xScaleFactor, yScaleFactor)
 
if presenting {
  herbView.transform = scaleTransform
  herbView.center = CGPoint(
    x: CGRectGetMidX(initialFrame),
    y: CGRectGetMidY(initialFrame))
  herbView.clipsToBounds = true
}

When presenting the new view, you set its scale and position so it exactly matches the size and location of the initial frame.

Now add the final bits of code to animateTransition():

containerView.addSubview(toView)
containerView.bringSubviewToFront(herbView)
 
UIView.animateWithDuration(duration, delay:0.0,
  usingSpringWithDamping: 0.4,
  initialSpringVelocity: 0.0,
  options: [],
  animations: {
    herbView.transform = self.presenting ?
     CGAffineTransformIdentity : scaleTransform
 
    herbView.center = CGPoint(x: CGRectGetMidX(finalFrame),
                              y: CGRectGetMidY(finalFrame))
 
  }, completion:{_ in
    transitionContext.completeTransition(true)
})

This will first add toView to the container. Next, you need to make sure the herbView is on top since that’s the only view you’re animating. Remember that when dismissing, toView is the original view so in the first line, you’ll be adding it on top of everything else and your animation will be hidden away unless you bring herbView to the front.

Then, you can kick off the animations – using a spring animation here will give it a bit of bounce.

Inside the animations expression, you change the transform and position of herbView. When presenting, you’re going from the small size at the bottom to the full screen so the target transform is just the identity transform. When dismissing, you animate it to scale down to match the original image size.

At this point, you’ve set the stage by positioning the new view controller over the tapped image; you’ve animated between the initial and final frames; and finally, you’ve called completeTransition() to hand things back to UIKit. It’s time to see your code in action!

Build and run your project; tap the first herb image to see your view controller transition in action:

image019

Currently your animation starts from the top-left corner; that’s because the default value of originFrame has the origin at (0, 0) – and you never set it to any other value.

Open ViewController.swift and add the following code to the top of animationControllerForPresentedController():

transition.originFrame = 
  selectedImage!.superview!.convertRect(selectedImage!.frame, toView: nil)
 
transition.presenting = true

This sets the originFrame of the transition to the frame of selectedImage, which is the image view you last tapped. Then you set presenting to true and hide the tapped image during the animation.

Build and run your project again; tap different herbs in the list and see how your transition looks for each:

image023

Adding a Dismiss Transition

All that’s left to do is dismiss the details controller. You’ve actually done most of the work in the animator already – the transition animation code does the logic juggling to set the proper initial and final frames, so you’re most of the way to playing the animation both forwards and backwards. Sweet! :]

Open ViewController.swift and replace the body of animationControllerForDismissedController() with the following:

transition.presenting = false
return transition

This tells your animator object that you’re dismissing a view controller so the animation code will run in the correct direction.

Build and run the project to see the result. Tap on an herb and then tap anywhere on screen to dismiss it and enjoy!

image027

A final touch to make the transition a bit smoother – you will animate the corner radius of the presented view controller so it really falls back in place when you dismiss it.

Add at the bottom of animateTransition:

let round = CABasicAnimation(keyPath: "cornerRadius")
round.fromValue = presenting ? 20.0/xScaleFactor : 0.0
round.toValue = presenting ? 0.0 : 20.0/xScaleFactor
round.duration = duration / 2
herbView.layer.addAnimation(round, forKey: nil)
herbView.layer.cornerRadius = presenting ? 0.0 : 20.0/xScaleFactor

When you are presenting the view controller you animate the radius from 20.0 to 0.0 and vice versa when you are dismissing the view controller. A nice touch to finish this tutorial on a high note!

Where To Go From Here?

You can grab the completed project from this tutorial here: BeginnerCook Completed.

From here, you can do a lot more to polish this transition even further. For example, here are some ideas to develop:

  • hide tapped images during transitions so it really looks like they grow to take up the full screen
  • fade in and out each herb’s description text so the transition animation is smoother
  • test and adjust the transition for landscape orientation

All of these and more are tackled in the presentation transition animations chapter in iOS Animations by Tutorials. In the book, you’ll learn in a bit more detail about view controller presentation transitions, orientation change animations, navigation controllers and interactive transitions, and more.

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

The post iOS Animation Tutorial: Custom View Controller Presentation Transitions appeared first on Ray Wenderlich.

Unite 2015 Boston Conference Highlights

$
0
0

logo_boston_ff0066_00ccccUnite is a conference that is run by Unity Technologies to promote their key product, the Unity gaming engine.

It isn’t so much as a conference, but rather an experience. With a keynote being presented to an audience of over a thousand attendees to an expo with all the latest gaming tech, it was hard not be swept up in the festival like atmosphere of the convention.

As the Unity team lead at raywenderlich.com, I had a unique opportunity to attend the convention in my home town of Boston. Not only was great to be back again to watch some Red Sox games with my fellow New Englanders, but it was simply awesome to hang out in crowd of passionate gamers eager to produce and play content made in Unity.

In this article, I’ll give you a brief of tour of Unite, providing you a rundown of the various announcements and sessions, and ultimately give you the skinny of whether it is worth your time to attend any future Unite conferences.

Training Day

The training session was packed from start to finish.

The training session was packed from start to finish.

Like many conferences, Unite started with an optional training day. I assumed the training session would be in a small room with sixty or so other developers, but I turned out to be quite wrong. The room was quite big with around two hundred developers, sitting squeezed behind tables in front of two large screens.

The session was lead by Will Goldstone and and James Bouckley. The pair worked well each other, bouncing off jokes off each other as well as filling in details that the other may have forgot. It was a nice balance especially during the later half of the day when the content grew technical and the coffee grew sparse.

The training day focused on building a local multiplayer game called Tanks whereby two players battled each other in several rounds. With all the art assets in place, the session walked users through the process of making a game from it.

While Tanks turned out be a complicated project, the multiplayer match at the end was a great way to finish.

Tanks was complex, but a fun way to finish.

This turned out to be quite an ambitious exercise. Because the game was being built from scratch, complicated topics such writing the camera tracking as well as managing the game state had to be covered in a short a time as possible.

There was also extensive coding involved with some advanced topics covered as such coroutines. The trainers did their best to explain each line of code, but it was clear that some were lost.

The training day may have been better served with a less complex game, but the game itself really shined at the end of training. After a typical session of Q & A, the trainers randomly selected four attendees on stage where they played the game in bracket style competition with the grand prize winner taking home a Unity Pro license. It was a great way to end the session.

Keynote

John Riccitiello lays out Unity's vision.

John Riccitiello lays out Unity’s vision.

The keynote started with John Riccitiello, CEO of Unity Technologies, taking the stage and laying out the vision behind Unity. Unity’s goal is to “democratize” game development. Instead of having a select few companies controlling the direction of game development, they want everyone to create games.

You can see this approach throughout the engine from the emphasis on visual design to the asset store that allows other developers to sell plugins. For Unity, game development is not island, but a thriving community that benefits when everyone participates.

Unfortunately, making a game, nevermind a good game, is not enough. Thus, Riccitiello announced that Unity would be assisting developers with not just the game tools, but the marketing as well.

Unity is offering a new service to help games get discovered. They are calling Made with Unty and you can investigate it but heading over the madewith.unity.com web site. The idea behind this site is to not only promote games that are doing awesome things with Unity, but also to allow developers to share their “stories” with the community. Unity is trying to find a way for developers to discover new fans.

You can think of it as a social network for game developers connecting with gamers. It’s a way for you to build your audience while building your game. After all, what’s the use of making a game if no one knows about it?

Made with Unity is way for game developers to connect with their audience.

Made with Unity is way for game developers to connect with their audience.

While this is a nice tool, I was disappointed that they haven’t appeared to address the real Made with Unity elephant in the room. That is, games made with the Personal version of Unity.

If you produce a game using the Personal version, your game gets a Made with Unity splash screen. Unfortunately, a lot of games produced with the free version haven’t been, shall we say, produced with high quality standards. Or in some cases, there’s been no quality standards at all. Some developers are just packaging assets with little care for design or quality, and pushing the product on Steam

Gamers are noticing this and there is some push back against Unity games. This is like readers blaming Microsoft Word for producing badly written Twilight fan fiction, but is a real problem in the Unity community and it would be nice to see Unity Technologies address it. Somehow.

Feature Announcements

And then came the announcements. Those glorious, glorious announcements.

Creating advanced platforms will soon done entirely inside of Unity.

Creating advanced platforms will soon done entirely inside of Unity.

First up, Unity 2D is getting a whole bunch of new awesome features features. It’s getting something Unity is calling procedural textures. It’s way of take sprites, nine slicing them, then creating landscapes by simple dragging on handles.

Unity 2D is also getting tile maps with the ability to write custom brushes. This will allow you to create “smart tiles”.

For instance, if you were drawing a road using horizontal road tile and that road intersected with a vertical road tile, your brush will automatically select an intersection tile or maybe a curve tile. In other words, based on your brush, the tiles are context aware. Brushes are simply C# scripts that you write, allowing you to designate the layout rules of your tile map. You can also layer multiple tile maps as well as incorporate them with 3D elements.

In regards to building your game, Unity is now offering a free plan for their Cloud Build service. This service automates your builds for you.

Unity Cloud Build now offers a free tier.

Unity Cloud Build now offers a free tier.

Once you fire off a build, Unity does the grunt work then deploys your build to a device for testing. It’s now offering custom targets so that you can configure your build for various platforms. Cloud Build is now supporting PC, Mac, and Linux platforms.

For those of you with a heavy data bent can now integrate analytics into you game with a click of button. The SDK is now apart of Unity itself. This allows you to track how your players are actually playing your game, and give you the knowledge on how to tune it. One killer feature of analytics is the inclusion of heat maps. These maps visually show how your players are moving in your game. This allows up to determine bottlenecks in your level design as well as provide incentives to get players out of their comfort zone.

Unity is also providing a unified solution for in app purchases (IAP). Previously, to incorporate IAP required you to learn the target platform’s SDK then integrate the solution into your codebase. For one platform, this wasn’t a big deal but several platforms, it created a spaghetti monster of proprietary calls. Now, Unity will take care of it for you using a streamlined API. This is a huge win for developers!

IAP under one roof!

IAP under one roof!

That said, all in all, it was a great keynote with some awesome features that will keep us busy in the coming years. But don’t take my word for it, you can view it online over here.

Sessions

Unity's Pete Moss demonstrating the HoloLens during Unite's keynote.

Unity’s Pete Moss demonstrating the HoloLens during Unite’s keynote.

Like all conferences, Unite had plenty of sessions. These sessions spanned the range of Unity development, but if you took an “eagle eyed” view of all the sessions, you’d notice a common theme. That is, virtual reality (VR). Virtual reality was represented all over the place and if you were unfamiliar with VR, then the expo had you covered. More on that later.

Unity obviously sees VR as the future and they want all their developers on board. From the few VR related sessions that I did attend, the messaging was consistent. Developers determine the future and those developers are you!

Needless to say, Unity is taking advantage of this opportunity. Since the release of Unity 5.1, it now provides native support for VR. All you have to do is click a checkbox and you can work in a VR world. In fact, not too long ago, we produced a video tutorial series on that very subject.

That said, Unite provided plenty of other sessions besides VR. One session called “Writing Shaders: YOU can do it” talked about the basics of writing simple shaders to a standing room crowd. Never have I seen an audience held at rapt attention for a code heavy session at three in the afternoon. While the session was deep, the overall message was that it’s possible to understand.

Another session titled, “Best Practices for Multiplatform Distribution” covered the process used to port the game “School of Dragons” to several different platforms. The most impressive part of this talk was how the developers configured Unity to enable or disable assets and shaders for the build depending on the platform. It goes to show that not only can you write scripts for your game, but Unity provides deep scripting control of the actual editor itself to suit the needs of your business requirements.

School of Dragons shows that build process can be just as complex as the game itself.

School of Dragons shows that build process can be just as complex as the game itself.

Finally, one of the interesting sessions was a Q & A session with all the managers of the various subsystems of Unity. Each manager provided a roadmap, and then opened the floor. Being that this site is iOS based, I had to ask a burning question since WWDC 2015. That is, will we see an open source Swift be ported to Unity. The answer, unfortunately, is no. Cue sad trombone here.

Unity's roadmap shows an exciting future!

Unity’s roadmap shows an exciting future!

Expo

Project Morpeus at Gamescom 2015, Photo by Sergey Galyonkin

Project Morpeus at Gamescom 2015, Photo by Sergey Galyonkin

If the sessions were the brains of the conference, then the conference floor was the beating heart. Through a pair of double doors presented a large showroom floor with scores of vendors showing off their gaming tech.

The first booth in the floor was, not surprisingly, a Playstation booth. Unity has just announced native support for Sony’s VR headset also known as Project Morpheus.

Sony was demoing a couple of headsets in a cops and robbers game that combined the VR headset with the Playstation Move. Players held glowing controls in place of guns and took part in high speed shootout. It was an excellent use of the technology that was never short of willing participants. Throughout the entire conference, long lines snaked around the Playstation booth just to get a chance to try it out.

Sony wasn’t the only vendor demoing VR tech. There was lots booths featuring different perspectives of the technology. One booth had participants ride stationary bikes to power their craft. Another demo featured a strange suit coupled with fake gun. In short, VR was everywhere and if you had not experienced it before, you had plenty of opportunities to do so.

That said, there can be no doubt that Google stole the show. They had a small booth to feature their Project Tango initiative. Project Tango is a tablet that does augmented reality. In their booth, they had attendees aim nerf guns with Project Tango tablets attached to them. The tablets projected a shooting gallery at the far wall with targets to hit.

Project Tango was a big hit at the conference!

Project Tango was a big hit at the conference!

When a attendee beat a certain score, they won a prize – a $600 dev kit for Project Tango itself. This wasn’t known at first. Half way through the first day, people started walking around the conference with boxes. When word got out they were toting actual hardware, the Project Tango booth got very busy.

The second day before the start of the sessions, a massive line formed outside the expo. There looked to be hundreds of attendees. Later in the day, when Google ran out of dev kits in their booth, they announced they would be giving out more dev kits to the first five attendees to show up at any of the Project Tango sessions. I didn’t bother to look, but I imagine that there were attendees camping outside the session rooms like an iPhone launch day.

That said, the expo also allowed you to sign up for one on one sessions with Unity’s developers. And of course, if the convention wore you out, there were free back rubs to ease your sufferings.

All in all, it was really great conference with lots of good times.

Me, with a perpetual grin, throughout the entire conference.

Me, with a perpetual grin, throughout the entire conference.

Where to Go From Here?

Unite is a really fun conference. If you are Unity developer, you have to go at least once. It’s the equivalent of going to WWDC. Besides the things I mentioned, Unite also featured an awards ceremony, lots of floating parties, and game stations placed through the conference floor.

Unite, at its core, isn’t just about one thing. The total experience is far greater than each individual part.

What’s truly great about the conference is that everyone you meet has a passion for games. People use Unity because they love it, and being in a conference full of them will infect you with enthusiasm that will make you want to get out there and make your own games.

Where you at Unite and if so, what was your favorite part of the conference? Let me know the comments! See you at the next conference! :]

The post Unite 2015 Boston Conference Highlights appeared first on Ray Wenderlich.

iOS Animation Tutorial: Introduction to Easy Animation

$
0
0
Note from Ray: This is an abbreviated version of a brand new chapter from iOS Animations by Tutorials Second Edition to give you a sneak peek of what’s inside the book, released as part of the iOS 9 Feast. This tutorial is fully up-to-date for iOS 9, Xcode 7, and Swift 2. We hope you enjoy!

An iOS animation tutorial for the iOS 9 Feast!

While I worked on creating the iOS Animations by Tutorials video series and the three editions of the iOS Animations by Tutorials book I got a lot of ideas how the existing animation APIs can be improved – I felt they could be a bit more flexible and not as verbose.

That’s why after iOS Animations by Tutorials was released I started working and soon after released a lightweight animation library called Easy Animation.

This tutorial shows you how to use Easy Animation to build powerful view and layer animations quickly and easily. Easy Animation builds upon the solid foundation of Core Animation and gives you more mileage from the same APIs you’ve been always using.

Let’s get started! :]

What is Easy Animation?

Easy Animation extends the existing animateWithDuration(_:animations:) APIs and introduces a couple of new ones that follow the same naming pattern. And since Easy Animation is an open source library written in Swift, you can always peek inside and see how it works.

Just to give you a taste what Easy Animation is all about here’s an example: if you want to round the corners of a layer called myLayer in the span of 1.0 second with 0.5 seconds delay and ease the beginning of the animation you need all these lines of code:

let round = CABasicAnimation(keyPath: "cornerRadius")
round.fromValue = 0.0
round.toValue = 50.0
round.duration = 1.0
round.beginTime = CACurrentMediaTime() + 0.5
round.fillMode = kCAFillModeBackwards
round.timingFunction = CAMediaTimingFunction(name: 
  kCAMediaTimingFunctionEaseIn)
myLayer.addAnimation(round, key: nil)
myLayer.cornerRadius = 50.0

More to write means more possibility to introduce a bug and less time to experiment and polish. What Easy Animation allows you to do is to animate layers by using the standard UIKit animation methods.

To create precisely the same animation from the example above with Easy Animation, you only need to write this:

UIView.animateWithDuration(1.0, delay: 0.5, options: .CurveEaseIn, animations: {
  myLayer.cornerRadius = 50.0
}, completion: nil)

You can freely mix and match view and layer animations in the same animations block, use the completion blocks, create joint view-layer spring animations, and much more.

Without further ado let me show you few animation tricks you can do too with Easy Animation on your side.

Getting Started

For this tutorial, you’re going to make a little app called Random Pack List.

Download the RandomPackList Starter project, unzip it and open in Xcode. Select Main.storyboard and have a look inside.

The storyboard features a table view in the lower half of the initial view controller and a big + button in the upper half. The table view is already wired to the ViewController class so you can run up the project immediately check out what the app looks like:

image001

This packing app is more fun than other similar apps: when the app’s finished, the big plus button on top will add a random item to the packing list no matter what the user actually wants in their luggage! :]

image002

Installation

First and foremost, you need a copy of Easy Animation. Open the GitHub page for version 1.0:
https://github.com/icanzilb/EasyAnimation/releases/tag/1.0.0 and click the Source code (zip) button on the right to download the library source code.

Note: If you are familiar with CocoaPods you can get Easy Animation by adding pod ‘EasyAnimation’, ‘1.0.0’ to your Podfile. If you don’t know what CocoaPods is, the next chapter covers this in more detail.

Unzip the downloaded archive file and drag the sub-folder called EasyAnimation onto Xcode’s project navigator. In the dialog that pops up, check the Copy Items if Needed checkbox:

image003

Verify that you see the EasyAnimation folder and the files in your project like so:

image004

As soon as you’ve imported the files, you can start creating Easy Animation powered animations.

Note: If you use CocoaPods with frameworks enabled, don’t forget to import the framework by adding import EasyAnimation to the top of your source files.

Easy Layer Animations

First, you’ll add a playful rotation animation to the plus button. Open ViewController.swift and add the following to actionAddItem():

UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.33, initialSpringVelocity: 0.0, options: [], animations: {
  self.plusButton.transform = CGAffineTransformRotate(
    self.plusButton.transform, CGFloat(-M_PI_2))
}, completion: nil)

You’ve probably created animations just like this one before, so you probably aren’t terribly impressed. Nevertheless, run the project and make sure the button rotates playfully when you tap it:

image005

Next, you’ll animate the corner radius of the button’s layer. To liven up the animation, you’ll combine the rotation and the corner radius animations in a group so it looks like the button transforms into a circle as it rotates.

The usual approach for a combo animation like this would be to create a CAAnimationGroup and two separate CABasicAnimation instances: one to animate the layer’s transform property and the other its corner radius.

However, you have Easy Animation on your side. Simply add a second code of line in the animations closure to adjust the cornerRadius property from within the animations closure, just as you would for any view property:

UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.33, initialSpringVelocity: 0.0, options: [], animations: {
  self.plusButton.transform = CGAffineTransformRotate(
    self.plusButton.transform, CGFloat(-M_PI_2))
    self.plusButton.layer.cornerRadius = 62.5
}, completion: nil)

You’ve appended a single line, which simply sets the button’s corner radius to half the length of its side. This ought to round its corners to the point where the square becomes a circle.

Run the project again and check the resulting animation:

image006

The square smoothly animates into a circle, and not only that — it does so with a spring animation. The corners jiggle around a bit and then come to rest.

But… how? So far you’ve only used using CABasicAnimation and CASpringAnimation for layer animations. How can a view animation include a change to a layer property?

Easy Animation extends the existing UIKit animation APIs just a little and picks up any changes to animatable layer properties. It creates the respective animations automatically for you.

Even more – Easy Animation groups the view and layer animations together in a group so you don’t need to create a group yourself.

Add two more lines to the animations closure:

self.plusButton.layer.borderWidth = 2.0
self.plusButton.layer.borderColor = UIColor.grayColor().CGColor

These two lines animate the button border width and color respectively, just as you would for your view animations:

image007

Easy Animation Sequences

Now you’ll add a second animation that runs after the button has rotated to animate all changed properties back to their initial values.

So far you’ve probably used the completion block to chain animations in a sequence – but everyone who has tried to make a sequence of two or more animations knows that this approach leads to a very messy code.

Easy Animation introduces two new methods you can use to start a sequence of animations:

image008

animateAndChainWithDuration(_:delay:options:animations:completion:) lets you create an animation sequence by providing exactly the same initial parameters as you would for its original UIKit counterpart.

animateAndChainWithDuration(_:delay:usingSpringWithDamping:
initialSpringVelocity:options:animations:completion:)
starts an animation chain with a spring animation as the first animation. It takes the same parameters as its original UIKit counterpart.

Starting an animation sequence is very simple – replace the method name in your code:

UIView.animateWithDuration(

with:

UIView.animateAndChainWithDuration(

Since the parameter count and names are the same, you can run and test your project right away. Nothing has changed though – you’ve created an animation sequence consisting of a single animation.

To add a second animation to the sequence, just place the cursor after the closing parenthesis of the first animation call, and type a “.” (a period or full stop); Xcode’s autocomplete feature will offer more animations for you to add:

image009

Choose the third animateWithDuration option from the suggested list – the one that lets you specify animation options — and set cornerRadius and borderWidth to their initial values. Use 0.15 seconds for the animation duration, 0.5 seconds of delay, and .CurveEaseOut for the options.

The completed code should now look like this:

UIView.animateAndChainWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.33, initialSpringVelocity: 0.0, options: [], animations: {
  self.plusButton.transform = CGAffineTransformRotate(
    self.plusButton.transform, CGFloat(-M_PI_2))
  self.plusButton.layer.cornerRadius = 62.5
  self.plusButton.layer.borderWidth = 2.0
  self.plusButton.layer.borderColor = 
    UIColor.grayColor().CGColor
}, completion: nil)
  .animateWithDuration(0.15, delay: 0.5, options: .CurveEaseOut, animations: {
  self.plusButton.layer.cornerRadius = 0.0
  self.plusButton.layer.borderWidth = 0.0
}, completion: nil)

Run your project again, tap the button and you’ll see it rotate, pause for half a second, then grow its corners back. At the end of the animation, the button will look the same as it did before you tapped it:

image010

You can tap the button again and again and it will run the animation sequence each time.

Note: To have a little fun at the button’s expense, add the .Repeat option to the second animation call. Run the project, tap the button and watch it go! :] Don’t forget to remove .Repeat option before continuing working trough the tutorial.

Completion closures

With Easy Animation, chaining animations is easy and doesn’t require the use of the completion parameter. However, they’re still available; you can use the completion closures to execute custom code at the end of the animation or in between the different animations of a sequence.

In this section, you are going to disable the button while it animates so the user cannot tap it again during the current animation.

Insert the following to the top of actionAddItem():

plusButton.enabled = false

Then replace the nil value of the completion block of the second animation with:

{_ in
  self.plusButton.enabled = true
}

This code will disable the button and re-enable it when the second animation in the sequence has finished running. If you tap repeatedly on the button it won’t react while it’s animating.

Finally replace the nil value of the completion parameter of the first animation with the following:

{_ in
  self.addRandomItem()
}

This code will add a random item to the list once the first leg of the button animation has completed. Run and enjoy the finished app:

image011

And that’s a wrap! You now know the basics of Easy Animation – a tiny animations library, which saves you lots of code lines and development time.

Where To Go From Here?

Now that you know the basics, you’re ready to try some of Easy Animation’s more advanced features.

Ever wanted to stop a repeating view animation? Easy Animation lets you do even that!

If you want to learn more, check out our book iOS Animations by Tutorials. In the book, which is fully updated for Swift 2.0 and iOS 9, you’ll learn how to animate with springs, transitions, keyframe animations, CALayer animations, Auto Layout constraint animations, view controller transition animations, and more.

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

The post iOS Animation Tutorial: Introduction to Easy Animation appeared first on Ray Wenderlich.

Core Data by Tutorials Second Edition Now Available!

$
0
0
Core Data by Tutorials Second Edition Now Available!

Core Data by Tutorials Second Edition Now Available!

Happy Wednesday – it’s time for our 3rd new book release as part of the iOS 9 Feast!

Today, Aaron, Saul, Matthew, Pietro and I are happy to announce that Core Data by Tutorials Second Edition is now available for download.

Core Data by Tutorials teaches you everything you need to know about Core Data, starting with the basics like setting up your own Core Data Stack and moving all the way to advanced topics like syncing with iCloud, migration, performance, multithreading, and more.

Here are some of the changes in this update:

  • Swift 2. All code has now been updated to Swift 2. This includes using the new error handling model — moving away from the complexities of NSError.
  • New uniqueness model constraint. iOS 9 introduces the ability to specify that an attribute should be unique across all objects.
  • Bugfixes. All chapters have been fully re-tested and tech edited and are verified as 100% working.

This is a free update for existing PDF customers – you can download the update on your My Loot page.

If you don’t have a copy of the book yet, now’s the time; since the book has been fully updated, there’s no better time to begin your journey to become a Core Data master.

Aaron, Saul, Matthew, Pietro and I hope you enjoy Core Data by Tutorials Second Edition!

The post Core Data by Tutorials Second Edition Now Available! appeared first on Ray Wenderlich.

New Video Tutorial Series: iOS 101

$
0
0
An iOS 9 and Swift 2 update for the iOS 9 Feast!

An iOS 9 and Swift 2 update for the iOS 9 Feast!

As part of this year’s iOS 9 Feast, we are releasing a new video tutorial series every Thursday.

This week, we are happy to release an iOS 9 and Swift 2 update to our iOS 101 series by Brian Moakley.

iOS 101 is our beginner series for iOS development. We recommend you watch it after completing our Beginning Swift 2 and Intermediate Swift 2 video tutorial series.

In this update, Brian has added coverage for new topics like iOS 9 Stack Views, Gesture Recognizers, Table Views, and how to incorporate the camera into apps. Brian also updated all the original iOS 101 projects to work with iOS 9, Swift 2, and Xcode 7.

All 12 parts of the series are available today. You can check them out here:

Now that all three of our beginner series have been updated for iOS 9 and Swift 2, now is the perfect time to get into iOS development with Swift 2 or brush up on your core skills! :]

So what are you waiting for – check out the videos and dive into iOS 9 and Swift 2! And be sure to come back next Thursday for another new video tutorial series.

The post New Video Tutorial Series: iOS 101 appeared first on Ray Wenderlich.

Getting Started with Core Data Tutorial

$
0
0
Note from Ray: This is an abbreviated version of a chapter from Core Data by Tutorials Second Edition to give you a sneak peek of what’s inside the book, released as part of the iOS 9 Feast. This tutorial is fully up-to-date for iOS 9, Xcode 7, and Swift 2. We hope you enjoy!

iOS9_feast_CoreData

In this getting started with Core Data tutorial, you’ll write your very first Core Data app using Swift 2.0. You’ll see how easy it is to get started with all the resources provided in Xcode, from the starter code templates to the data model editor. By the end of the tutorial you’ll know how to:

  • model data you want to store in Core Data using Xcode’s model editor;
  • add new records to Core Data;
  • fetch a set of records from Core Data;
  • display the fetched results to the user in a table view.

You’ll also get a sense of what Core Data is doing behind the scenes, and how you can interact with the various moving pieces there. We’re getting ahead of ourselves though – it’s time to build an app!

Getting started

Open Xcode and create a new iPhone project based on the Single View Application template. Call the app HitList and make sure Use Core Data is checked:

CoreData2-2

Checking the Use Core Data box will cause Xcode to generate boilerplate code for what’s known as a Core Data stack in AppDelegate.swift.

The Core Data stack consists of a set of objects that facilitate saving and retrieving information from Core Data. There’s an object to manage the Core Data state as a whole, an object representing the data model, and so on.

Note: Not all Xcode templates under iOS/Application have the option to start with Core Data. In Xcode 7, only the Master-Detail Application and the Single View Application templates have the Use Core Data checkbox.

The idea for this sample app is simple. There will be a table view with a list of names for your very own “hit list”. You’ll be able to add names to this list and eventually, you’ll use Core Data to make sure the data is stored between sessions. We don’t condone violence on this site so you can think of this app as a “favorites list” to keep track of your friends too, of course! ;]

Click on Main.storyboard to open it in Interface Builder. Next, embed the initial view controller in a navigation controller. From Xcode’s Editor menu, select Embed In…\ Navigation Controller.

CoreData2-4

Back in Interface Builder, drag a Table View from the object library into the view controller so that it covers the view controller’s entire view.

If not already open, open Interface Builder’s document outline by selecting the icon located in the lower left corner of your canvas. Ctrl-drag from the Table View in the document outline to its parent view and select the Leading Space to Container Margin constraint:

CoreData2-5

Do this three more times, selecting the constraints Trailing Space to Container Margin, Vertical Spacing to Top Layout Guide and finally, Vertical Spacing to Bottom Layout Guide. If you’re familiar with Auto Layout, you’ll recognize that selecting those four constraints will constrain the size of the table view to the size of its parent view.

Next, drag a Bar Button Item and place it on the view controller’s navigation bar. Finally, double-click the bar button item to change its text to Add. Your canvas should now look similar to the following screenshot:

CoreData2-7

Every time you tap Add on the top-right, an alert containing a text field will appear on the screen. From there you’ll be able to type someone’s name into the text field. Dismissing the alert will save the name and refresh the table view with all the names you’ve saved up to that point.

Before you can do that, you need to make the view controller the table view’s data source. In the canvas, Ctrl-drag from the table view to the yellow view controller icon above the navigation bar, as shown below, and click on dataSource:

CoreData2-9

In case you were wondering, you don’t need to set up the table view’s delegate since tapping on the cells won’t trigger any action. It doesn’t get simpler than this!

Open the Assistant Editor by hitting Command-Option-Enter or by selecting the middle button on the Editor toolset on the Xcode bar. Ctrl-drag from the table view onto ViewController.swift, inside the class definition to insert an outlet:

CoreData2-10

Name the new IBOutlet property tableView, resulting in the following line:

@IBOutlet weak var tableView: UITableView!

Ctrl-drag from the Add bar button item onto ViewController.swift, but this time, create an action instead of an outlet and name the method addName:

@IBAction func addName(sender: AnyObject) {
 
}

You can now refer to the table view and the bar button item’s action in code. Next, set up the model for the table view. Add the following property to ViewController.swift:

//Insert below the tableView IBOutlet
var names = [String]()

names is a mutable Array to hold the strings for the table view to display.
Replace the implementation of viewDidLoad() with the following:

override func viewDidLoad() {
  super.viewDidLoad()
  title = "\"The List\""
  tableView.registerClass(UITableViewCell.self,
    forCellReuseIdentifier: "Cell")
}

This will set a title and register the UITableViewCell class with the table view. You do this so that when you dequeue a cell, the table view will return a cell of the correct type.

Still in ViewController.swift, declare that ViewController will conform to the UITableViewDataSource protocol by editing the class declaration:

//Add UITableViewDataSource to class declaration
class ViewController: UIViewController, UITableViewDataSource {

Immediately, Xcode will complain about ViewController not conforming to the protocol. Below viewDidLoad(), implement the following data source methods to fix the error:

// MARK: UITableViewDataSource
func tableView(tableView: UITableView,
  numberOfRowsInSection section: Int) -> Int {
  return names.count
}
 
func tableView(tableView: UITableView,
  cellForRowAtIndexPath
  indexPath: NSIndexPath) -> UITableViewCell {
 
  let cell =
  tableView.dequeueReusableCellWithIdentifier("Cell")
 
  cell!.textLabel!.text = names[indexPath.row]
 
  return cell!
}

If you’ve ever worked with UITableView, this code should look very familiar. The first method says that the table view will have as many rows as the names array has strings.

The second method, tableView(_:cellForRowAtIndexPath:), dequeues table view cells and populates them with the corresponding string in the names array.

Don’t run the app just yet. First, you need a way to input names so the table view can display them.

Implement the addName IBAction method you Ctrl-dragged into your code earlier:

//Implement the addName IBAction
@IBAction func addName(sender: AnyObject) {
 
  let alert = UIAlertController(title: "New Name",
    message: "Add a new name",
    preferredStyle: .Alert)
 
  let saveAction = UIAlertAction(title: "Save",
    style: .Default,
    handler: { (action:UIAlertAction) -> Void in
 
      let textField = alert.textFields!.first
      self.names.append(textField!.text!)
      self.tableView.reloadData()
  })
 
  let cancelAction = UIAlertAction(title: "Cancel",
    style: .Default) { (action: UIAlertAction) -> Void in
  }
 
  alert.addTextFieldWithConfigurationHandler {
    (textField: UITextField) -> Void in
  }
 
  alert.addAction(saveAction)
  alert.addAction(cancelAction)
 
  presentViewController(alert,
    animated: true,
    completion: nil)
}

Every time you tap the Add bar button item, this method presents an UIAlertController with a text field and two buttons, Save and Cancel.

Save takes whatever text is currently in the text field, inserts it into the name array and reloads the table view. Since the names array is the model backing the table view, whatever you typed into the text field will appear in the table view.

Finally it’s time to build and run your app for the first time. Tap the Add bar button item. The alert controller will look like this:

CoreData2-12

Add four or five names to the list. You should wind up with something like this:

CoreData2-14

Your table view will display the data and your array will store the names, but the big thing missing here is persistence. The array is in memory but if you force quit the app or reboot your device, your hit list will be wiped out.

Core Data provides persistence, meaning it can store data in a more durable state so that it can outlive an app re-launch or a device reboot.

You haven’t added any Core Data yet, so nothing should persist after you navigate away from the app. Let’s test this out. Press the Home button if you’re using a physical device or the equivalent (Shift+⌘+H) if you’re on the Simulator. This will take you back to the familiar app grid on the home screen:

CoreData2-15

From the home screen, tap the HitList icon to bring the app back to the foreground. The names are still on the screen. What happened?

When you tap the Home button, the app that’s currently in the foreground goes to the background. When this happens, the operating system flash-freezes everything currently in memory, including the strings in the names array. Similarly, when it’s time to wake up and return to the foreground, the operating system restores what used to be in memory as if you’d never left.

Apple introduced these advances in multitasking back in iOS 4. They create a seamless experience for iOS users but add a wrinkle to the definition of persistence for iOS developers. Are the names really persisted?

No, not really. If you had completely killed the app in the fast app switcher or turned off your phone, those names would be gone. You can verify this, as well. With the app in the foreground, double tap the Home button to enter the fast app switcher, like so:

CoreData2-17

From here, flick the HitList app snapshot upwards to terminate the app. There should be no trace of HitList in living memory (no pun intended). Verify that the names are gone by returning to the home screen and tapping on the HitList icon to trigger a fresh launch.

This difference between flash-freezing and persistence may be obvious if you’ve been working with iOS for some time and are familiar with the way multitasking works. In a user’s mind, however, there is no difference. The user doesn’t care if the names are “still there” because the app went into the background and came back, or because the app saved and reloaded them.

All that matters is that the names are still there when she comes back!

So the real test of persistence, the one you will use in this tutorial, is whether your data is still there after a fresh app launch.

Modeling your data

Now that you know how to check for persistence, let’s get started with Core Data. Your goal for the HitList app is simple: to persist the names you enter so they’re available for viewing after a fresh app launch.

Up to this point, you’ve been using plain old Swift strings to store the names in memory. In this section, you’ll replace these strings with Core Data objects.

The first step is to create a managed object model, which spells out the way Core Data represents data on disk. By default, Core Data uses an SQLite database as the persistent store, so you can think of the data model as the database schema.

Note: You’ll come across the word “managed” quite a bit in this tutorial. If you see “managed” in the name of a class, such as in NSManagedObjectContext, chances are you are dealing with a Core Data class. “Managed” refers to Core Data’s management of the life cycle of Core Data objects.

However, don’t assume that all Core Data classes contain the word “managed”—actually, most don’t. For a comprehensive list of Core Data classes, check out the Objective-C umbrella header CoreData/CoreData.h.

Since you elected to use Core Data when you created the HitList project, Xcode automatically created a data model file for you and named it HitList.xcdatamodeld.

CoreData2-19

Click on HitList.xcdatamodeld to open it. As you can see, Xcode has a powerful data model editor that looks like this:

CoreData2-20

The data model editor has a lot of features. For now, let’s focus on creating a single Core Data entity.

Click on Add Entity on the lower-left to create a new entity. Double-click on the new entity and change its name to Person, like so:

CoreData2-21

You may be wondering why the model editor uses the term “Entity.” Weren’t you simply defining a new class? As you’ll see shortly, Core Data comes with its own vocabulary. Here’s a quick rundown of some of the terms you’ll commonly encounter:

    • An entity is a class definition in Core Data. The classic example is an Employee or a Company. In a relational database, an entity corresponds to a table.
    • An attribute is a piece of information attached to a particular entity. For example, an Employee entity could have attributes for the employee’s name, position and salary. In a database, an attribute corresponds to a particular field in a table.
    • A relationship is a link between multiple entities. In Core Data, relationships between two entities are called to-one relationships, while those between one and many entities are called to-many relationships. For example, a Manager can have a to-many relationship with a set of employees, whereas an individual Employee will have a to-one relationship with his manager.

Note: As you’ve probably noticed, entities sound a lot like a classes. Likewise, attributes/relationships sound a lot like properties. What’s the difference? You can think of a Core Data entity as a class “definition” and the managed object as an instance of that class.

Now that you know what an attribute is, go back to the model editor and add an attribute to Person. Select Person on the left-hand side and click the plus sign (+) under Attributes.

Set the new attribute’s name to, well, name and change its type to String:

CoreData2-22

In Core Data, an attribute can be of one of several data types — one of them is the string type.

Saving to Core Data

Import the Core Data module at the top of ViewController.swift:

//Add below "import UIKit"
import CoreData

You may have had to link frameworks manually in your project’s Build Phases if you’ve worked with Objective-C frameworks. In Swift, a simple import statement is all you need to start using Core Data APIs in your code.

Next, replace the table view’s model with the following:

//Change “names” to “people” and [String] to [NSManagedObject]
var people = [NSManagedObject]()

You’ll be storing Person entities rather than just names, so you rename the Array that serves as the table view’s data model to people. It now holds instances of NSManagedObject rather than simple Swift strings.

NSManagedObject represents a single object stored in Core Data—you must use it to create, edit, save and delete from your Core Data persistent store. As you’ll see shortly, NSManagedObject is a shape-shifter. It can take the form of any entity in your data model, appropriating whatever attributes and relationships you defined.

Since you’re changing the table view’s model, you must also replace both data source methods you implemented earlier with the following to reflect these changes:

//Replace both UITableViewDataSource methods
func tableView(tableView: UITableView,
  numberOfRowsInSection section: Int) -> Int {
    return people.count
}
 
func tableView(tableView: UITableView,
  cellForRowAtIndexPath
  indexPath: NSIndexPath) -> UITableViewCell {
 
    let cell =
    tableView.dequeueReusableCellWithIdentifier("Cell")
 
    let person = people[indexPath.row]
 
    cell!.textLabel!.text =
      person.valueForKey("name") as? String
 
    return cell!
}

The most significant change to these methods occurs in cellForRowAtIndexPath. Instead of matching cells with the corresponding string in the model array, you now match cells with the corresponding NSManagedObject.

Note how you grab the name attribute from the NSManagedObject. It happens here:

cell!.textLabel!.text = person.valueForKey("name") as? String

Why do you have to do this? As it turns out, NSManagedObject doesn’t know about the name attribute you defined in your data model, so there’s no way of accessing it directly with a property. The only way Core Data provides to read the value is key-value coding, commonly referred to as KVC.

Note: If you’re new to iOS development, you may not be familiar with key-value coding or KVC.

KVC is a mechanism in Cocoa and Cocoa Touch for accessing an object’s properties indirectly using strings to identify properties. In this case, KVC makes NSMangedObject behave more or less like a dictionary.

Key-value coding is available to all classes that descend from NSObject, including NSMangedObject. You wouldn’t be able to access properties using KVC on a Swift object that doesn’t descend from NSObject.

Next, replace the save action in the addName @IBAction method with the following:

let saveAction = UIAlertAction(title: "Save",
  style: .Default,
  handler: { (action:UIAlertAction) -> Void in
 
    let textField = alert.textFields!.first
    self.saveName(textField!.text!)
    self.tableView.reloadData()
})

This takes the text in the text field and passes it over to a new method called saveName. Add saveName to ViewController.swift, as shown below:

func saveName(name: String) {
  //1
  let appDelegate =
  UIApplication.sharedApplication().delegate as! AppDelegate
 
  let managedContext = appDelegate.managedObjectContext
 
  //2
  let entity =  NSEntityDescription.entityForName("Person",
    inManagedObjectContext:managedContext)
 
  let person = NSManagedObject(entity: entity!,
    insertIntoManagedObjectContext: managedContext)
 
  //3
  person.setValue(name, forKey: "name")
 
  //4
  do {
    try managedContext.save()
  //5
    people.append(person)
  } catch let error as NSError  {
      print("Could not save \(error), \(error.userInfo)")
  }
}

This is where Core Data kicks in! Here’s what the code does:

  1. Before you can save or retrieve anything from your Core Data store, you first need to get your hands on an NSManagedObjectContext. You can think of a managed object context as an in-memory “scratchpad” for working with managed objects.Think of saving a new managed object to Core Data as a two-step process: first, you insert a new managed object into a managed object context; then, after you’re happy with your shiny new managed object, you “commit” the changes in your managed object context to save it to disk.Xcode has already generated a managed object context as part of the new project’s template – remember, this only happens if you check the Use Core Data checkbox at the beginning. This default managed object context lives as a property of the application delegate. To access it, you first get a reference to the app delegate.
  2. You create a new managed object and insert it into the managed object context. You can do this in one step with NSManagedObject’s designated initializer: init(entity:insertIntoManagedObjectContext:).You may be wondering what an NSEntityDescription is all about. Recall that earlier, I called NSManagedObject a “shape-shifter” class because it can represent any entity. An entity description is the piece that links the entity definition from your data model with an instance of NSManagedObject at runtime.
  3. With an NSManagedObject in hand, you set the name attribute using key-value coding. You have to spell the KVC key (“name” in this case) exactly as it appears on your data model, otherwise your app will crash at runtime.
  4. You commit your changes to person and save to disk by calling save on the managed object context. Note that save can throw an error, which is why you call it using the try keyword and within a do block.
  5. Congratulations! Your new managed object is now safely ensconced in your Core Data persistent store. Still within the do block, insert the new managed object into the people array so that it shows up in the table view when it reloads.

That’s a little more complicated than an array of strings, but not too bad. Some of the code here—getting the managed object context and entity¬—could be done just once in your own init() or viewDidLoad() and then reused later. For simplicity, you’re doing it all at once in one method.

Build and run the app, and add a few names to the table view:

CoreData2-24

If the names are actually stored in Core Data, the HitList app should pass the persistence test. Double-tap the Home button to bring up the fast app switcher. Terminate the HitList app by flicking it upwards.

From Springboard, tap the HitList app to trigger a fresh launch. Wait, what happened? The table view is empty:

CoreData2-26

You saved to Core Data, but after a fresh app launch, the people array is empty! The data is actually sitting there waiting, but you’re not showing it yet.

Fetching from Core Data

To get data from your persistent store and into the managed object context, you have to fetch it. Add the following method to ViewController.swift:

override func viewWillAppear(animated: Bool) {
  super.viewWillAppear(animated)
 
  //1
  let appDelegate =
  UIApplication.sharedApplication().delegate as! AppDelegate
 
  let managedContext = appDelegate.managedObjectContext
 
  //2
  let fetchRequest = NSFetchRequest(entityName: "Person")
 
  //3
  do {
    let results =
      try managedContext.executeFetchRequest(fetchRequest)
    people = results as! [NSManagedObject]
  } catch let error as NSError {
    print("Could not fetch \(error), \(error.userInfo)")
  }
}

Step by step, this is what the code does:

  1. As mentioned in the previous section, before you can do anything with Core Data, you need a managed object context. Fetching is no different! You pull up the application delegate and grab a reference to its managed object context.
  2. As the name suggests, NSFetchRequest is the class responsible for fetching from Core Data. Fetch requests are both powerful and flexible. You can use requests to fetch a set of objects that meet particular criteria (e.g., “give me all employees that live in Wisconsin and have been with the company at least three years”), individual values (e.g., “give me the longest name in the database”) and more.Fetch requests have several qualifiers that refine the set of results they return. For now, you should know that NSEntityDescription is one of these qualifiers (one that is required!).Setting a fetch request’s entity property, or alternatively initializing it with init(entityName:), fetches all objects of a particular entity. This is what you do here to fetch all Person entities.
  3. You hand the fetch request over to the managed object context to do the heavy lifting. executeFetchRequest() returns an array of managed objects that meets the criteria specified by the fetch request.

Note: Like save(), executeFetchRequest() also throws an error so you have to use it within a do block. If an error occurred during the fetch, you can inspect the NSError inside the catch block and respond appropriately.

Build and run the application once again. Immediately, you should see the list of names you added earlier:

CoreData2-28

Great! They’re back from the dead. Add a few more names to the list and restart the app to verify that saving and fetching are working properly. Short of deleting the app, resetting the Simulator or throwing your phone off a tall building, the names will appear in the table view no matter what.

CoreData2-29

Where to go from here?

In this getting started with Core Data tutorial you experienced several fundamental Core Data concepts: data models, entities, attributes, managed objects, managed object contexts and fetch requests. Here is the completed HitList project, fully integrated with Core Data.

There were a few rough edges in HitList: you had to get the managed object context from the app delegate each time, and you used KVC to access the attributes rather than a more natural object-style person.name. As you can see, Core Data is large and extensive topic. There’s a lot more to learn!

If you’d like to learn more about creating Core Data applications using Swift 2.0, check out our the second version of our book Core Data by Tutorials where you can go deeper into more advanced Core Data topics such as iCloud/Core Data integration, versioning and migration. The second version of the book is up to date with iOS 9 and Swift 2.0 in mind.

If you have any questions or comments on this tutorial, please join the forum discussion below!

The post Getting Started with Core Data Tutorial appeared first on Ray Wenderlich.


Core Data Tutorial: Multiple Managed Object Contexts

$
0
0
Note from Ray: This is an abbreviated version of a chapter from Core Data by Tutorials Second Edition to give you a sneak peek of what’s inside the book, released as part of the iOS 9 Feast. This tutorial is fully up-to-date for iOS 9, Xcode 7, and Swift 2. We hope you enjoy!

A managed object context is an in-memory scratchpad that you use to work with your managed objects.

Most apps need but a single managed object context. A single managed object context with a main queue, the default behavior, is simple to manage and understand. Apps with multiple managed object contexts are harder to debug. For that reason, you should avoid them, if possible.

That being said, certain situations do warrant the use of more than one managed object context. For example, long-running tasks such as exporting data will block the main thread of apps that use only a single main-queue managed object context, causing the UI to stutter.

In other situations, such as when temporarily editing user data, it’s helpful to treat a managed object context as a set of changes that the app can just throw away if it no longer needs them. Using child contexts makes this possible.

In this Core Data tutorial, you’ll learn about multiple managed object contexts by taking a journaling app for surfers and improving it in several ways by adding multiple contexts.

Note: This is an advanced tutorial, and assumes prior knowledge of Swift, Core Data, and iOS app development in general. If common Core Data phrases such as managed object subclass and persistent store coordinator don’t ring any bells, or if you’re unsure what a Core Data stack is supposed to do, you may want to read some of our other Core Data tutorials first.

Getting Started

This tutorial’s starter project is a simple journal app for surfers. After each surf session, a surfer can use the app to create a new journal entry that records marine parameters, such as swell height or period, and rate the session from 1 to 5. Dude, if you’re not fond of hanging ten and getting barreled, no worries, brah. Just replace the surfing terminology with your favorite hobby of choice!

Introducing Surf Journal

Open the SurfJournal starter project, then build and run the app.

On startup, the application lists all previous surf session journal entries. Tapping on a row in the list brings up the detail view of a surf session with the ability to make edits.

screenshot01 screenshot02

As you can see, the sample app works and has data. Tapping the Export button on the top-left exports the data to a comma-separated values (CSV) file. Tapping the plus (+) button on the top-right adds a new journal entry. Tapping a row in the list opens the entry in edit mode, letting you make changes or view the details of a surf session.

Although the sample project appears simple, it actually does a lot and will serve as a good base to add multi-context support. First, make sure you have a good understanding of the various classes in the project.

Open the project navigator and take a look at the full list of files in the starter project:

screenshot03

Before jumping into the code, let’s briefly go over what each class does for you out of the box.

  • AppDelegate: On first launch, the app delegate creates the Core Data stack and sets the coreDataStack property on the primary view controller JournalListViewController.
  • CoreDataStack: This object contains the cadre of Core Data objects known as the “stack”: the context, the model, the persistent store and the persistent store coordinator. The stack installs a database that already has data in it on first launch. No need to worry about this just yet; you’ll see how it works shortly.
  • JournalListViewController: The sample project is a one-page table-based application. This file represents that table. If you’re curious about its UI elements, head over to Main.storyboard. There’s a table embedded in a navigation controller and a single prototype cell of type SurfEntryTableViewCell.
  • JournalEntryViewController: This class handles creating and editing surf journal entries. You can see its UI in Main.storyboard.
  • JournalEntry: This class represents a surf journal entry. It is an NSManagedObject subclass with six properties for attributes: date, height, location, period, rating and wind. It also includes the CSV export function csv. If you’re curious about this class’s entity definition, head over to SurfJournalModel.xcdatamodel.

screenshot04

When you first launched the app, it already had a significant amount of data. While it is common to import seed data from a JSON file, this sample project comes with a seeded Core Data database. Let’s see how it works.

The Core Data Stack

Open CoreDataStack.swift and find the following code:

// 1
let bundle = NSBundle.mainBundle()
let seededDatabaseURL = bundle
  .URLForResource(self.seedName, withExtension: "sqlite")!
 
// 2
let didCopyDatabase: Bool
do {
  try NSFileManager.defaultManager()
    .copyItemAtURL(seededDatabaseURL, toURL: url)
  didCopyDatabase = true
} catch {
  didCopyDatabase = false
}
 
// 3
if didCopyDatabase {

Let’s go through the code step by step:

  1. The app bundle comes with a pre-populated Core Data database named SurfJournalDatabase.sqlite. To make use of this database, first you have to find it and create a URL reference to it using URLForResource(_:withExtension:).
  2. copyItemAtURL(_:toURL:error:) attempts to copy the seeded database file to the app’s documents directory. If the database file already exists in the documents directory, the copy operation fails. This behavior allows the seeding operation to happen only once, on first launch.
  3. On subsequent app launches, the database will already exist and the copy will fail. When the copy operation fails, the variable didCopyDatabase will be false and the code in the if-statement will never execute.

Assume that the app is launching for the first time and therefore didCopyDatabase is true. Let’s see how the rest of the seeding operation works:

// 4
let seededSHMURL = bundle
  .URLForResource(self.seedName, withExtension: "sqlite-shm")!
let shmURL = self.applicationDocumentsDirectory
  .URLByAppendingPathComponent(self.seedName + ".sqlite-shm")
do {
  try NSFileManager.defaultManager()
    .copyItemAtURL(seededSHMURL, toURL: shmURL)
} catch {
  let nserror = error as NSError
  print("Error: \(nserror.localizedDescription)")
  abort()
}
 
// 5
let seededWALURL = bundle
  .URLForResource(self.seedName, withExtension: "sqlite-wal")!
let walURL = self.applicationDocumentsDirectory
  .URLByAppendingPathComponent(self.seedName + ".sqlite-wal")
do {
  try NSFileManager.defaultManager()
    .copyItemAtURL(seededWALURL, toURL: walURL)
} catch {
  let nserror = error as NSError
  print("Error: \(nserror.localizedDescription)")
  abort()
}

To support concurrent reads and writes, SQLite, the persistent store in use by this sample app, utilizes SHM (shared memory file) and WAL (write-ahead logging) files. You don’t need to know how these extra files work, but you do need to be aware that they exist and that you need to copy them over when seeding the database. If you fail to copy over these files, the app will work, but it will be missing data.

  1. Once SurfJournalDatabase.sqlite has been successfully copied, the support file SurfJournalDatabase.sqlite-shm is copied over.
  2. Finally, the remaining support file SurfJournalDatabase.sqlite-wal, is copied over.

The only reason SurfJournalDatabase.sqlite, SurfJournalDatabase.sqlite-shm or SurfJournalDatabase.sqlite-wal would fail to copy over on first launch is if something really bad happened, such as disk corruption from cosmic radiation. In that case the device, including any apps, would likely also fail. If the app won’t work, there’s no point in continuing, so the initializer calls abort().

Note: We developers often frown upon using abort, as it confuses users by causing the app to quit suddenly and without explanation. But this is one example where abort is acceptable, since the app needs Core Data to work.

If an app requires Core Data to be useful and Core Data isn’t working, there’s no point in letting the app continue on, only to fail sometime later in a non-deterministic way. Calling abort at least generates a stack trace, which can be helpful when trying to fix the problem.

If your app has support for remote logging or crash reporting, you should log any relevant information that might be helpful for debugging before calling abort.

Once the pre-populated database and support files are copied over, the final step is to add the seeded database store to the persistent store coordinator.

// 6
do {
  try coordinator.addPersistentStoreWithType(
    NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
} catch {
  // 7
    let nserror = error as NSError
    print("Error: \(nserror.localizedDescription)")
    abort()
}
  1. addPersistentStoreWithType(_:configuration:URL:options:) is called on the NSPersistentStoreCoordinator to add the store (NSSQLiteStoreType in this case) at the given URL.
  2. Finally, if the store wasn’t successfully created, the app won’t work so abort is called.

Now that you know something about beginning with a seeded database, let’s start learning about multiple managed object contexts by adding a second context with a private queue to the Surf Journal app.

Doing Work in the Background

If you haven’t done so already, tap the Export button at the top-left and then immediately try to scroll the list of surf session journal entries. Notice anything? The export operation will take several seconds and it will prevent the UI from responding to touch events, such as scrolling.

The UI is blocked during the export operation because both the export operation and UI are using the main queue to perform their work. This is the default behavior.

How can you fix this? The traditional way would be to use Grand Central Dispatch to run the export operation on a background queue. However, Core Data managed object contexts are not thread-safe. That means you can’t just dispatch to a background queue and use the same Core Data stack.

The solution is easy: just add another context for the export operation that uses a private queue rather than the main queue, so the export operation can do its work in the background. This will keep the main queue free for the UI to use.

But before you jump in and fix the problem, you need to understand how the export operation works.

Exporting Data

Start by viewing how the app creates the CSV strings for the Core Data entity. Open JournalEntry.swift and find csv():

func csv() -> String {
  let coalescedHeight = height ?? ""
  let coalescedPeriod = period ?? ""
  let coalescedWind = wind ?? ""
  let coalescedLocation = location ?? ""
  var coalescedRating:String
  if let rating = rating?.intValue {
    coalescedRating = String(rating)
  } else {
    coalescedRating = ""
  }
 
  return "\(stringForDate()),\(coalescedHeight)," +
    "\(coalescedPeriod),\(coalescedWind)," +
    "\(coalescedLocation),\(coalescedRating)\n"
}

As you can see, this JournalEntry function returns a comma-separated string of the entity’s attributes. Because the JournalEntry attributes are allowed to be nil, the function uses the nil coalescing operator (??) so that it exports an empty string instead of an unhelpful debug message that the attribute is nil.

Note: The nil coalescing operator (??) unwraps an optional if it contains a value; otherwise it returns a default value. For example, the following:

let coalescedHeight = height != nil ? height! : ""

Can be shortened by using the nil coalescing operator:

let coalescedHeight = height ?? ""

Now that you know how the app creates the CSV strings for an individual journal entry, take a look at how the app saves the CSV file to disk. Switch to JournalListViewController.swift and find the following code in exportCSVFile:

// 1
let results: [AnyObject]
do {
  results = try coreDataStack.context.executeFetchRequest(
        self.surfJournalFetchRequest())
} catch {
  let nserror = error as NSError
  print("ERROR: \(nserror)")
  results = []
}
 
// 2
let exportFilePath =
  NSTemporaryDirectory() + "export.csv"
let exportFileURL = NSURL(fileURLWithPath: exportFilePath)
NSFileManager.defaultManager().createFileAtPath(
  exportFilePath, contents: NSData(), attributes: nil)

Let’s go through the CSV export code step by step:

  1. First, the code retrieves all JournalEntry entities by executing a fetch request. The fetch request is the same one used by the fetched results controller and therefore the code uses surfJournalFetchRequest to create it, avoiding duplication.
  2. The code creates the URL for the exported CSV file by appending the file name (“export.csv”) to the output of NSTemporaryDirectory. The path returned by NSTemporaryDirectory is a unique directory for temporary file storage. This a good place for files that can easily be generated again and don’t need to be backed up by iTunes or to iCloud. After creating the export URL, the code calls createFileAtPath(_:contents:attributes:) to create the empty file to store the exported data. If a file already exists at the specified file path, then the code removes it first.

Once the app has the empty file, it can write the CSV data to disk:

// 3
let fileHandle: NSFileHandle?
do {
  fileHandle = try NSFileHandle(forWritingToURL: exportFileURL)
} catch {
  let nserror = error as NSError
  print("ERROR: \(nserror)")
  fileHandle = nil
}
 
if let fileHandle = fileHandle {
  // 4
  for object in results {
    let journalEntry = object as! JournalEntry
 
    fileHandle.seekToEndOfFile()
    let csvData = journalEntry.csv().dataUsingEncoding(
      NSUTF8StringEncoding, allowLossyConversion: false)
    fileHandle.writeData(csvData!)
  }
 
  // 5
  fileHandle.closeFile()
  1. First, the app needs to create a file handler for writing, which is simply an object that handles the low-level disk operations necessary for writing data. To create a file handler for writing, the code calls fileHandleForWritingToURL(_:error:).
  2. Using a for-in statement, the code iterates over all JournalEntry entities. During each iteration, the code creates a UTF8-encoded string using csv and dataUsingEncoding(_:allowLossyConversion:). It then writes the UTF8 string to disk using writeData.
  3. Finally, the code closes the export file-writing file handler, since it’s no longer needed.

Once the app has written all the data to disk, it shows an alert dialog with the exported file path:

screenshot05

Note: This alert view with the export path is fine for learning purposes, but for a real app, you’ll need to provide the user with a way to retrieve the exported CSV file. Attaching the export file to an email is a popular method.

To open the exported CSV file, use Excel, Numbers or your favorite text editor to navigate to and open the file specified in the alert dialog. If you open the file in Numbers you will see the following:

screenshot06

Now that you’ve seen how the app currently exports data, it’s time to make some improvements.

Exporting on a Private Queue

You want the UI to continue to work while the export is happening. To fix the UI problem, you’ll perform the export operation on a private background queue instead of on the main queue.

Open JournalListViewController.swift and find the following code in exportCSVFile:

// 1
let results: [AnyObject]
do {
  results = try coreDataStack.context.executeFetchRequest(
        self.surfJournalFetchRequest())
} catch {
  let nserror = error as NSError
  print("ERROR: \(nserror)")
  results = []
}

As you saw earlier, this code retrieves all of the journal entries by calling executeFetchRequest on the managed object context.

Now replace it with the following:

// 1
let privateContext = NSManagedObjectContext(
  concurrencyType: .PrivateQueueConcurrencyType)
privateContext.persistentStoreCoordinator =
  coreDataStack.context.persistentStoreCoordinator
 
// 2
privateContext.performBlock { () -> Void in
  // 3
  let results: [AnyObject]
  do {
    results = try self.coreDataStack.context
      .executeFetchRequest(self.surfJournalFetchRequest())
  } catch {
    let nserror = error as NSError
    print("ERROR: \(nserror)")
    results = []
  }

Let’s go through the new code, which utilizes a new managed object context, step by step:

  1. First, you create a new managed object context called privateContext with a concurrency type of PrivateQueueConcurrencyType, which specifies that the context will be associated with a private dispatch queue. Once you’ve created the new context, you assign it the same persistent store coordinator as the main managed object context.
  2. Next, you call performBlock. This function asynchronously performs the given block on the context’s queue. In this case, the queue is private.
  3. Just as before, you retrieve all JournalEntry entities by executing a fetch request. But this time, you use the private context to execute the fetch request.

Next, find the following code in the same function:

  print("Export Path: \(exportFilePath)")
  self.navigationItem.leftBarButtonItem =
    self.exportBarButtonItem()
  self.showExportFinishedAlertView(exportFilePath)
} else {
  self.navigationItem.leftBarButtonItem =
    self.exportBarButtonItem()
}

Now replace it with the following:

    // 4
    dispatch_async(dispatch_get_main_queue(), { () -> Void in
      self.navigationItem.leftBarButtonItem =
        self.exportBarButtonItem()
      print("Export Path: \(exportFilePath)")
      self.showExportFinishedAlertView(exportFilePath)
    })
  } else {
    dispatch_async(dispatch_get_main_queue(), { () -> Void in
      self.navigationItem.leftBarButtonItem =
        self.exportBarButtonItem()
    })
  }    
 
} // 5 closing brace for performBlock()
  1. You should always perform all operations related to the UI, such as showing an alert view when the export operation is finished, on the main queue; otherwise unpredictable things will happen. You use the dispatch_async and dispatch_get_main_queue to show the final alert view message on the main queue.
  2. Finally, the block you opened earlier in step 2 via the performBlock call now needs to be closed with a closing curly brace.

Note: There are three concurrency types a managed object context can use:

ConfinementConcurrencyType specifies that the context will use the thread confinement pattern and that the developer will be responsible for managing all thread access. You should consider this type deprecated and never use it, as the next two types will cover all use cases.

PrivateQueueConcurrencyType specifies that the context will be associated with a private dispatch queue instead of the main queue. This is the type of queue you just used to move the export operation off of the main queue so that it no longer interferes with the UI.

MainQueueConcurrencyType, the default type, specifies that the context will be associated with the main queue. This type is what the main context (coreDataStack.context) uses. Any UI operation, such as creating the fetched results controller for the table view, must use a context of this type.

Now that you’ve moved the export operation to a new context with a private queue, it’s time to build and run and see if it works! Give it a go.

You should see exactly what you saw before:

screenshot07

Tap the Export button in the top-left and then immediately try to scroll the list of surf session journal entries. Notice anything different this time? The export operation still takes several seconds to complete, but now the table view continues to scroll during this time. The export operation is no longer blocking the UI.

Cowabunga, dude! Gnarly job making the UI more responsive.

You’ve just witnessed how creating a new managed object context with a private queue can improve a user’s experience with your app.

Where To Go From Here?

Here is the SurfJournal final project for this tutorial.

If you followed this Core Data tutorial all the way through, you’ve turned an app with a single managed object context into an app with multiple contexts. You improved UI responsiveness by performing the export operation on a managed object context with a private queue.

If you want to learn more about multiple managed object contexts, check out the full chapter in Core Data by Tutorials, where I go a bit further and cover parent and child contexts.

In the meantime, if you have any questions or comments, please join the forum discussion below!

The post Core Data Tutorial: Multiple Managed Object Contexts appeared first on Ray Wenderlich.

Core Data Migrations Tutorial: Lightweight Migrations

$
0
0
Note from Ray: This is an abbreviated version of a chapter from the second edition of  Core Data by Tutorials released as part of the iOS 9 Feast to give you a sneak peek of what’s inside the book. It has been fully updated to Swift 2.0 and iOS 9. We hope you enjoy!

When you create a Core Data app, you design an initial data model for your app. However, after you ship your app inevitably you’ll want to make changes to your data model. What do you do then – you don’t want to break the app for existing users!

You can’t predict the future, but with Core Data, you can migrate toward the future with every new release of your app. The migration process will update data created with a previous version of the data model to match the current data model.

This Core Data migrations tutorial discusses the many aspects of Core Data migrations by walking you through the evolution of a note-taking app’s data model. You’ll start with a simple app with only a single entity in its data model, and use a lightweight migration to update the user’s data model. The full chapter in the Core Data by Tutorials book will guide you through more complex migration examples, as you add more features and data to the app.

Let the journey begin!

Note: This tutorial assumes some basic knowledge of Core Data and Swift.

 

When to migrate

When is a migration necessary? The easiest answer to this common question is “when you need to make changes to the data model.”

However, there are some cases in which you can avoid a migration. If an app is using Core Data merely as an offline cache, then when you update the app, you can simply delete and rebuild the data store. This is only possible if the source of truth for your user’s data isn’t in the data store. In all other cases, you’ll need to safeguard your user’s data.

That said, any time it’s impossible to implement a design change or feature request without changing the data model, you’ll need to create a new version of the data model and provide a migration path.

The migration process

When you initialize a Core Data stack, one of the steps involved is adding a store to the persistent store coordinator. When you encounter this step, Core Data does a few things prior to adding the store to the coordinator.

First, Core Data analyzes the store’s model version. Next, it compares this version to the coordinator’s configured data model. If the store’s model version and the coordinator’s model version don’t match, then Core Data will perform a migration, when enabled.

Note: If migrations aren’t enabled, and the store is incompatible with the model, Core Data will simply not attach the store to the coordinator and specify an error with an appropriate reason code.

To start the migration process, Core Data needs the original data model and the destination model. It uses these two versions to load or create a mapping model for the migration, which it uses to convert data in the original store to data that it can store in the new store. Once Core Data determines the mapping model, the migration process can start in earnest.

Migrations happen in three steps:

  1. First, Core Data copies over all the objects from one data store to the next.
  2. Next, Core Data connects and relates all the objects according to the relationship mapping.
  3. Enforce any data validations in the destination model. Core Data disables destination model validations during the data copy.

You might ask, “If something goes wrong, what happens to the original source data store?” With nearly all types of Core Data migrations, nothing happens to the original store unless the migration completes without error. Only when a migration is successful, will Core Data remove the original data store.

Types of migrations

In my own experience using Core Data, I’ve found there are a few more migration variants than the simple distinction between lightweight and heavyweight. Below, I’ve provided the more subtle variants of migration names, but these names are not official categories by any means. I’ll start with the least complex form of migration and end with the most complex form.

Lightweight migrations

A lightweight migration is Apple’s term for the migration with the least amount of work involved on your part. Simply enable a couple of flags when setting up a Core Data stack, and the migration happens automatically. There are some limitations on how much you can change the data model, but because of the small amount of work required to enable this option, it is the ideal setting.

Manual migrations

Manual migrations involve a little more work on your part. You need to specify how to map the old set of data onto the new set, but you get the benefit of a more explicit mapping model file to configure. Setting up a mapping model in Xcode is much like setting up a data model, with similar GUI tools and some automation.

Custom manual migrations

This is level 3 of the migration complexity index. You still use a mapping model, but add to that custom code with the ability to also specify custom transformation logic on data. In this case, custom entity transformation logic involves creating an NSEntityMigrationPolicy subclass and performing custom transformations there.

Fully manual migrations

Fully manual migrations are for those times when even specifying custom transformation logic isn’t enough to fully migrate data from one model version to another. In this case, custom version detection logic and custom handling of the migration process are necessary. In this chapter, you’ll use set up a fully manual migration to update data across non-sequential versions, such as jumping from version 1 to 4.

In this tutorial you’ll focus on lightweight migrations because they are the easiest and most common type of migrations. Let’s get started!

Getting started

First, download the starter project for this tutorial and open it in the latest public version of Xcode.

Build and run the app in the iPhone simulator. You’ll see an empty list of notes:

Starter project

Tap the plus (+) button in the top-right corner to add a new note. Add a title (there is default text in the note body to make the process faster) and tap Create to save the new note to the data store. Repeat this a few times so that you have some sample data to migrate.

Back in Xcode, open the UnCloudNotesDatamodel.xcdatamodeld file to show the entity modeling tool in Xcode. The data model is simple—just one entity, a Note, with a few attributes.

Modeling tool in Xcode

You’re going to add a new feature to the app: the ability to attach a photo to a note. The data model doesn’t have any place to persist this kind of information, so you’ll need to add a place in the data model to hold onto the photo. But you already added a few test notes in the app—how can you change the model without breaking the existing notes? It’s time for your first migration!

A lightweight migration

With the entity modeler open, open the Editor menu and select Add Model Version…. Call the new version UnCloudNotesDataModel v2 and select UnCloudNotesDataModel in the “Based on Model” field. Xcode will now create a copy of the data model.

Note:You can give this file any name you want. The sequential v2, v3, v4, et cetera naming helps you tell the versions apart easily.

This step will create a second version of the data model, but you still need to tell Xcode to use the new version as the current model. In the File Inspector pane on the right, find the option toward the bottom called Model Version. Change that selection to match the name of the new data model, UnCloudNotesDataModel v2:

Core_Data_by_Tutorials_v_1_2

Once you’ve made that change, notice in the project navigator that the little green check mark icon has moved from the previous data model to the v2 data model:

003_Migration

Core Data will load the ticked version when setting up the stack. The older version is there to support migration—it’s hard to migrate to a new model without knowing the old one!

Make sure you have the v2 data model selected and add an image attribute to the Note entity. Set the attribute’s name to image and the attribute’s type to Transformable.

Since this attribute is going to contain the actual binary bits of the image, you’ll use a custom NSValueTransformer to convert from binary bits to a UIImage and back again. Just such a transformer has been provided for you as ImageTransformer. In the Value Transformer Name field in the Data Model Inspector on the right of the screen, enter UnCloudNotes.ImageTransformer.

004_Transformer

The new model is now ready for some code! Open Note.swift and add a property to match the new attribute:

@NSManaged var image: UIImage?

Build and run, and you’ll see your notes have disappeared! In the Xcode console, you’ll see some error text related to the CoreDataStack object:

context: 
modelName: UnCloudNotesDataModelmodel: [Note: ]
coordinator: 
storeURL: file:///Users/YOURNAME/Library/Developer/CoreSimulator/Devices/A24A4E68-D616-4F63-8946-652164EE5E53/data/Containers/Data/Application/9921B2DD-D0FD-4330-90F9-A2F44CC9899A/Library/Application%20Support/UnCloudNotes.sqlite
store: nil

The store file is still around (storeURL in the log above), but since it’s incompatible with the new v2 model, Core Data couldn’t attach it to the persistent store, so store is still nil.

Core Data can automatically update your store if all you’ve done is add a new property like this. These are called lightweight migrations.

Enabling lightweight migrations

To enable lightweight migrations, you need to set two flags on initialization. The stack in this app lives in an object imaginatively titled CoreDataStack, which you’ll modify to do this.

Open CoreDataStack.swift and add a property to the class:

var options: NSDictionary?

Right now, you’re setting up the persistent store with no options for default behavior. You’ll use the options dictionary to set the necessary flags.

Next, update the initializer to match the following:

init(modelName: String, storeName: String,
 options: NSDictionary? = nil) {
  self.modelName = modelName
  self.storeName = storeName
  self.options = options
}

In setting the default value of options to nil, the old method signature remains valid and you have the additional choice of passing in the options dictionary.

Find the coordinator computed property and change the initialization of the persistent store as follows:

store = coordinator.addPersistentStoreWithType(
  NSSQLiteStoreType,
  configuration: nil,
  URL: storeURL,
  options: self.options)

There’s just a small change here to pass in the extra options when creating the stack.

Open NotesListViewController.swift and change the CoreDataStack lazy initialization statement to use the lightweight migration options:

lazy var stack : CoreDataStack = CoreDataStack(
  modelName:"UnCloudNotesDataModel",
  storeName:"UnCloudNotes",
  options:[NSMigratePersistentStoresAutomaticallyOption: true,
           NSInferMappingModelAutomaticallyOption: true])

The NSMigratePersistentStoresAutomaticallyOption is what tells Core Data (the NSPersistentStoreCoordinator, actually) to start a migration if the persistent store model isn’t compatible with the current data model. Core Data will handle all the details, from finding the source model to creating the destination store file, and all the steps in between.

The NSInferMappingModelAutomaticallyOption is the other half of what makes a lightweight migration possible. Every migration requires a mapping model. Here’s an analogy: If you’re traveling from a known place on Earth to somewhere unknown, you’ll want a map to tell you where to go. The mapping model is that guide.

It just so happens that Core Data can infer a mapping model in many cases. That is, Core Data can automatically look at the differences in two data models and create a mapping model between them. For entities and attributes that are identical between model versions, this is a straightforward data pass through mapping. For other changes, just follow a few simple rules for Core Data to create a mapping model. In the new model, changes must fit an obvious migration pattern, such as:

  1. Deleting entities, attributes or relationships;
  2. Renaming entities, attributes or relationships using the renamingIdentifier;
  3. Adding a new, optional attribute;
  4. Adding a new, required attribute with a default value;
  5. Changing an optional attribute to non-optional and specifying a default value;
  6. Changing a non-optional attribute to optional;
  7. Changing the entity hierarchy;
  8. Adding a new parent entity and moving attributes up or down the hierarchy;
  9. Changing a relationship from to-one to to-many;
  10. Changing a relationship from non-ordered to-many to ordered to-many (and vice versa).
Note: Check out Apple’s documentation for more information on how Core Data infers a lightweight migration mapping:
https://developer.apple.com/library/Mac/DOCUMENTATION/Cocoa/Conceptual/CoreDataVersioning/Articles/vmLightweightMigration.html

As you can see from this list, Core Data can detect, and more importantly, automatically react to, a wide variety of common changes between data models. As a rule of thumb, all migrations, if necessary, should start as lightweight migrations and only move to more complex mappings when the need arises.

As for the migration from UnCloudNotes to UnCloudNotes v2, the image property has a default value of nil since it’s an optional property. This means Core Data can easily migrate the old data store to a new one, since this change follows item 3 in the list of lightweight migration patterns.

Build and run, and your old notes have returned! Core Data has migrated the data store automatically using an inferred mapping model.

The non-nil value for the store: entry in the logs is a nice confirmation that the migration actually happened and that there’s now an NSPersistentStore object representing the store.

005_Output

Congratulations—you’ve completed your first data migration!

Image attachments

Now that data is migrated, you need to update the UI to allow image attachments to new notes. Luckily, most of this work has been done for you, so you can quickly get to the interesting part. :]

Open Main.storyboard and go to the Create Note scene. Just underneath, you’ll see a Create Note With Images scene that includes the interface to attach an image.

The Create Note scene is attached to a navigation controller with a root view controller relationship. Control-drag from the navigation controller to the Create Note With Images scene and select the root view controller relationship segue. This will disconnect the old Create Note scene and connect the new, image-powered one instead:

006_Storyboard

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

func imagePickerController(picker: UIImagePickerController,
 didFinishPickingMediaWithInfo info: [String: AnyObject]) {
  if let note = note {
    note.image = info[UIImagePickerControllerOriginalImage] as? UIImage
  }
  self.navigationController?.popViewControllerAnimated(true)
}

This will populate the new image property of the note once the user selects something from the standard image picker.

Open CreateNoteViewController.swift and replace viewDidAppear with the following:

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)
  if let image = note?.image {
    attachedPhoto.image = image
    view.endEditing(true)
  } else {
    titleField.becomeFirstResponder()
  }
}

This implementation will display the new image if the user has added one.
Next, open NotesListViewController.swift and update tableView(_:cellForRowAtIndexPath) with the following:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let note = notes.fetchedObjects?[indexPath.row] as? Note
  let identifier = note.image == nil ? "NoteCell” : "NoteCellImage"
 
  if let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as NoteTableViewCell {
    cell.note = note
    return cell
  }
  return UITableViewCell()
}

This will set the correct cell identifier based on whether an image is present in the note. If there is an image, you also need to populate the image view in the cell. Open NoteTableViewCell.swift and add the following lines after the code that sets the creation date label’s text in updateNoteInfo():

if let image = note?.image {
  noteImage.image = image
}

Build and run, and choose to add a new note:

007_New_Note

Tap the Attach Image button to add an image to the note. Choose an image and you’ll see it in your new note:

008_Attach_Image

The app uses a standard UIImagePickerController to add photos as attachments to notes.

Note: To add your own images to the Simulator’s photo album, drag an image file onto the open Simulator window. Thankfully, the iOS Device Simulator now comes with a library of photos ready for your use. :]

If you’re using a device, go to AttachPhotoViewController.swift and set the sourceType attribute on the image picker controller to .Camera to take photos with the device camera. The existing code uses the photo album, since there is no camera in the Simulator.

Where To Go From Here?

Here is the final example project from the above tutorial.

The full chapter in the Core Data by Tutorials book follows up this Core Data migrations tutorial with a series of more complex migrations. In the full book chapter, you’ll walk through creating a mapping model with entity and attribute mappings from one version to the next. You’ll also learn about custom migration policies, and how to migrate non-sequential data models.

I hope you enjoyed this Core Data migrations tutorial, and if you have any questions or comments, please going the discussion thread below.

The post Core Data Migrations Tutorial: Lightweight Migrations appeared first on Ray Wenderlich.

iOS 9 by Tutorials Now Available!

$
0
0

iOS 9 by Tutorials

Note from Ray: Happy Wednesday – it’s book release day during the iOS 9 Feast!

iOS is growing up fast — gone are the days when every 3rd-party developer knew everything there is to know about the OS.

The sheer size of iOS can make new releases seem daunting. This past year alone, iOS 9 introduced UIStackViews, 3D Touch, iPad multitasking, app search, and more. It’s a lot for a single developer to learn on their own.

That’s why the iOS Tutorial Team has been working ever since WWDC to extract the important parts of the new APIs, and to present this information in an easy-to-understand tutorial format.

So today, we are happy to release the 5th book in our popular iOS by Tutorials series: iOS 9 by Tutorials!

About iOS 9 by Tutorials

iOS 9 by Tutorials is for intermediate developers, who already know the basics of iOS and Swift development but want to learn the new APIs introduced in iOS 9.

10 members of the Tutorial Team on this site have teamed up to divide and conquer all of this new material, distilling it into practical examples and an easy-to-learn form.

This way, rather than banging your head against the wall trying to learn these topics by scouring through boring API references, you can learn iOS 9 the easy way via tutorials, in the style you know and love from this site!

iOS 9 by Tutorials is 15 chapters and 297 pages. Let’s take a quick look at what’s inside.

  1. Chapter 1, Swift 2.0: Swift is only a year old, but a lot has changed since it was born. Discover the new error handling model, protocol extensions, new control flow features and much more! You’ll be ready to tackle the rest of the book, and to upgrade your existing code to Swift 2.0!
  2. Chapter 2, Introducing App Search: With iOS 9, users can search inside your apps using the system Spotlight utility. Learn how to adopt this new functionality, and make the content inside your apps more discoverable.

introduction01

  1. Chapter 3, Your App on the Web: Deep linking will allow you to direct users browsing your website directly to the correct point inside your iOS app. Find out how to integrate this functionality in your own site and app as you follow along with updating the RWDevCon website and app!
  2. Chapter 4, App Thinning: App Thinning describes a collection of new App Store technologies that ensure that users downloading your app only download exactly what their specific device requires. Discover what you need to do to adopt App Thinning, together with how you can split your app’s resources up into chunks that are only downloaded from the App Store when they are required.
  3. Chapter 5, Multitasking: Completely new to iOS 9 is the ability to run two apps side-by-side on iPads. Get up to speed on what you need to do to ensure your apps are ready for multitasking.

introduction02

  1. Chapter 6, 3D Touch: The iPhone 6s and iPhone 6s Plus introduced a new technology called 3D Touch. Discover how to detect the force of a touch in iOS 9, and the new interaction paradigms—such as pop and peek—that come along with it.
  2. Chapter 7, UIStackView & Auto Layout changes: Stack views will change the way you build your interfaces. They remove a lot of the boilerplate Auto Layout and make it really easy to construct simple layouts. Learn the basics of stack views and many of the other new Auto Layout features.
  3. Chapter 8, Intermediate UIStackView: Dive deeper into stack views — covering nesting, animation and working with them in code.

introduction03

  1. Chapter 9, What’s New in Storyboards?: Ever faced the problem of unmanageably large storyboards? Discover how to refactor your huge storyboard into smaller segments with new storyboard references.
  2. Chapter 10, Custom Segues: Creating custom view controller transitions has never been an easy task, but iOS 9 chips away at some of the complexity. Learn how to create your own custom segues to make custom transitions between view controllers far easier to understand.

introduction04

  1. Chapter 11, UIKit Dynamics: iOS 9 includes loads of great improvements to UIKit Dynamics — making it really easy to model complex physics animations within your apps. Learn all about the new behaviors and debugging improvements as you build a real-world example.
  2. Chapter 12, Contacts: Accessing the on-device contacts has been challenging in the past — with a C-level API. iOS 9 introduces two new frameworks to ease interaction with the contacts. Discover how to integrate with the device address book in a much simpler manner.
  3. Chapter 13, Testing: Xcode 7 now includes a fully-featured solution for creating UI tests for your application. Discover how to add UI tests to your own apps, and review some of the other improvements to testing.
  4. Chapter 14, Location & Mapping: It’s finally possible to choose your favorite color for a map pin! Learn about this, transit directions, Core Location enhancements and more!
  5. Chapter 15, What’s New in Xcode?: There’s loads of cool new stuff in iOS 9, but don’t forget about your friendly IDE! Learn how the improved gauges make optimizing your app really easy, along with many other new features.

introduction05

Where To Go From Here?

If you have preordered iOS 9 by Tutorials, you can download it now on your My Loot page.

Otherwise, be sure to grab your copy now!

Get ready for your own private tour through the amazing new features of iOS 9. By the time you’re done, your iOS knowledge will be completely up-to-date and you’ll be able to benefit from the amazing new opportunities in iOS 9.

The Tutorial Team and I hope you enjoy iOS 9 by Tutorials, and we hope to see you use some of these amazing new APIs in your own apps!

The post iOS 9 by Tutorials Now Available! appeared first on Ray Wenderlich.

New Video Tutorial Series: Introducing Custom Controls

$
0
0

As part of this year’s iOS 9 Feast, we are releasing a new video tutorial series every Thursday.

This week, we are happy to release a brand new video tutorial series – Introducing Custom Controls by Sam Davies!

Custom Controls Video Tutorial Series

Custom controls are an important part of developing apps for iOS, allowing you to develop engaging user experiences, while achieving great code separability and reuse.

In this series Sam demonstrates how to create custom controls for iOS, via three different approaches:

  1. First you’ll discover how you can compose UIKit components to build your own control.
  2. Then you’ll learn how to use Core Animation to create controls with much more visual complexity.
  3. Finally, you’ll see how you can use Core Graphics to “draw” the appearance of controls yourself.

All 11 parts of the series are available today. You can check them out here:

We think you’re really going to love this series – it’s unique content you won’t find on any other site.

Enjoy, and thanks for subscribing! :]

The post New Video Tutorial Series: Introducing Custom Controls appeared first on Ray Wenderlich.

iOS 9 App Search Tutorial: Introduction to App Search

$
0
0
Note from Ray: This is an abridged version of a chapter from iOS 9 by Tutorials, to give you a sneak peek of what’s inside the book, released as part of the iOS 9 Feast. We hope you enjoy!

iOS9_feast_AppSearchThere’s been a big hole in Spotlight on iOS for a long time. Although users can use it to find you app, they can’t see inside it — to all the content that they really care about. Currently, when users want to get to content in an app, they have to flip through pages on their home screen to find it, open it and then search for what they’re looking for — assuming you actually implemented a search feature in the first place!

An especially savvy user could launch your app by using Siri or searching for it in Spotlight, but neither of these tools help the user find what they want inside a non-Apple app. Meanwhile, Apple makes things like contacts, notes, messages, email and apps directly searchable within Spotlight. The user simply taps on the search result and goes straight to the content. No fair!

Sometimes it seems like Apple keeps all the fun features to itself, like using Spotlight. The good news is that after Apple developers finish playing around with a feature and feel it’s ready for showtime, it often lets the masses play too, like it did with app extensions in iOS 8.

With iOS 9, Apple is passing a very exciting feature off to the rest of us; third party developers now have the ability to make their content searchable through Spotlight!

In this iOS 9 app search tutorial you’ll discover the power of app searching, and learn how easy it is to integrate into your own apps.

App search APIs

App search in iOS 9 comprises three main aspects. Each is broken into separate APIs that achieve distinct results, but they also work in concert with one another:

  • NSUserActivity
  • Core Spotlight
  • Web markup

NSUserActivity

Being a clever little feature, this aspect of app search makes use of the same NSUserActivity API that enables Handoff in iOS 8.

In iOS 9, NSUserActivity has been augmented with some new properties to enable search. Theoretically speaking, if a task can be represented as an NSUserActivity to be handed off to a different device, it can be stored in a search index and later continued on the same device. This enables you to index activities, states and navigation points within your app, allowing the user to find them later via Spotlight.

For example, a travel app might index hotels the user has viewed, or a news app might index the topics the user browsed.

Note: This tutorial will not specifically cover Handoff, but you’ll learn how to make content searchable once it’s viewed. To get up to speed at Handoff you can check out the Getting Started with Handoff tutorial.

Core Spotlight

The second, and perhaps most “conventional” aspect of app search is Core Spotlight, which is what the stock iOS apps like Mail and Notes use to index content. While it’s nice to allow users to search for previously accessed content, you might take it a step further by making a large set of content searchable in one go.

You can think of Core Spotlight as a database for search information. It provides you with fine-grained control of what, when and how content is added to the search index. You can index all kinds of content, from files to videos to messages and beyond, as well as updating and removing search index entries.

Core Spotlight is the best way to provide full search capabilities of your app’s private content.

This tutorial will focus on what is required to get Spotlight results by using NSUserActivity described above. The full version of this tutorial in iOS 9 by Tutorials shows how to fully index your content by using Core Spotlight.

Web markup

The third aspect of app search is web markup, which is tailored towards apps that mirror their public content from a web site. A good example is Amazon, where you can search the millions of products it sells, or even raywenderlich.com. Using open standards for marking up web content, you can show it in Spotlight and Safari search results and even create deep links within your app.

This tutorial will not cover web markup, but you can learn all about it in Chapter 3, “Your App On The Web” of iOS 9 by Tutorials.

Getting started

You’ll work with a sample app named Colleagues that simulates a company address book. It provides an alternative to adding every coworker to your contacts, instead providing you with a directory of your colleagues. To keep things simple, it uses a local dataset, comprising of a folder of avatar images and a JSON file that contains employee information. In the real world, you would have a networking component that fetches contact data from a web-service. This is a tutorial, so JSON it is. Download and open the starter project, and before you do anything, build and run the app.

intro-app-search-app-preview

You’ll immediately see a list of employees. It’s a small startup, so there’s only 25 staff. Select Brent Reid from the list to see all of his details. You’ll also see a list of employees who are in the same department. And that is the extent of the app’s features — it’s very simple!

Search would make this app infinitely better. As it stands, you can’t even search while you’re in the app. You won’t add in-app search, but instead add the ability to search from outside the app with Spotlight!

Sample project

Take a moment to familiarize yourself with the project’s codebase. There are two targets: Colleagues which is the app itself, and EmployeeKit which is a framework to facilitate interactions with the employee database.

From the EmployeeKit group in Xcode, open Employee.swift. This is the model for an employee that has all the properties you might expect. Employee instances are initialized using a JSON model, which are stored under the Database group in a file named employees.json.

Moving on, open EmployeeService.swift. At the top of the file is an extension declaration, and there is a method named destroyEmployeeIndexing() marked with TODO. You’ll fill out this method’s implementation later. This method will destroy any indexing that has occurred.

There’s more to the EmployeeKit target, but it’s not related to app searches, so there’s no need to go into it now. By all means though, feel free to poke around!

Open AppDelegate.swift in the Colleagues group. Notice there is only one method in here: application(_:didFinishLaunchingWithOptions:). This implementation checks if Setting.searchIndexingPreference is set to .Disabled, if so it will destroy any search indexing that has occurred.

You don’t need to do anything other than just be aware of this setting. You can change the setting in the iOS system Settings app under Colleagues”.

That concludes your tour. The rest of the code is view controller logic that you’ll modify, but you don’t need to know all of it to work with app search.

Searching previously viewed records

When implementing app search, NSUserActivity is the first thing to work with because:

  1. It’s dead simple. Creating an NSUserActivity instance is as easy as setting a few properties.
  2. When you use NSUserActivity to flag user activities, iOS will rank that content so that search results prioritize frequently accessed content.
  3. You’re one step closer to providing Handoff support.

Time to prove how simple NSUserActivity can be to implement!

Implement NSUserActivity

With the EmployeeKit group selected, go to File \ New \ File…. Choose the iOS \ Source \ Swift File template and click Next. Name your new file EmployeeSearch.swift and verify that the target is set to EmployeeKit.

Within the new file, first import CoreSpotlight:

import CoreSpotlight

Next, still in EmployeeSearch.swift, add the following extension to the Employee struct:

extension Employee {
  public static let domainIdentifier = "com.raywenderlich.colleagues.employee"
}

This reverse-DNS formatted string identifies the type of NSUserActivity created for employees. Next, add the following computed property below the domainIdentifier declaration:

public var userActivityUserInfo: [NSObject: AnyObject] {
  return ["id": objectId]
}

This dictionary will serve as an attribute for your NSUserActivity to identify the activity. Now add another computed property named userActivity:

public var userActivity: NSUserActivity {
  let activity = NSUserActivity(activityType: Employee.domainIdentifier)
  activity.title = name
  activity.userInfo = userActivityUserInfo
  activity.keywords = [email, department]
  return activity
}

This property will come into play later to conveniently obtain an NSUserActivity instance for an employee. It creates new NSUserActivity and sets a few properties:

  • activityType: The type of activity that this represents. You’ll use this later to identify NSUserActivity instances that iOS provides to you. Apple suggests using reverse DNS formatted strings.
  • title: The name of the activity — this will also appear as the primary name in a search result.
  • userInfo: A dictionary of values for you to use however you wish. When the activity is passed to your app, such as when the user taps a search result in Spotlight, you’ll receive this dictionary. You’ll use it to store the unique employee ID, allowing you to display the correct record when the app starts.
  • keywords: A set of localized keywords that help the user find the record when searching.

Next up, you are going to use this new userActivity property to make employee records searchable when the user views them. Since you added these definitions in the EmployeeKit framework, you’ll need to build the framework so that Xcode is aware they can be used from the Colleagues app.

Press Command-B to build the project.

Open EmployeeViewController.swift and add the following to the bottom of viewDidLoad():

let activity = employee.userActivity
 
switch Setting.searchIndexingPreference {
case .Disabled:
  activity.eligibleForSearch = false
case .ViewedRecords:
  activity.eligibleForSearch = true
}
 
userActivity = activity

This retrieves userActivity — the property you just created in the Employee extension. Then it checks the app’s search setting.

  • If search is disabled, you mark the activity as ineligible for search.
  • If the search setting is set to ViewedRecords, then you mark the activity as eligible for search.
  • Finally, you set the view controller’s userActivity property to your employee’s activity.
NOTE: The userActivity property on the view controller is inherited from UIResponder. It’s one of those things Apple added with iOS 8 to enable Handoff.

The last step is to override updateUserActivityState(). This ensures that when a search result is selected you’ll have the information necessary.

Add the following method after viewDidLoad():

override func updateUserActivityState(activity: NSUserActivity) {
  activity.addUserInfoEntriesFromDictionary(
    employee.userActivityUserInfo)
}

During the lifecycle of UIResponder, the system calls this method at various times and you’re responsible for keeping the activity up to date. In this case, you simply provide the employee.userActivityUserInfo dictionary that contains the employee’s objectId.

Great! Now when you pull up an employee, that bit of history will be tracked and become searchable, provided the setting is turned on.

In the simulator or on your device, open the Settings app and scroll down to Colleagues. Change the Indexing setting to Viewed Records.

intro-app-search-app-screen-3

Now, build and run the app and select Brent Reid.

Okay, so it doesn’t look like anything spectacular happened, but behind the scenes, Brent’s activity is being added to the search index. Exit to the home screen (⇧⌘H) and bring up Spotlight by either swiping down from the middle of the screen or swiping all the way to the left of your home screen pages. Type brent reid into the search.

intro-app-search-app-screen-4

And there’s Brent Reid! If you don’t see him, you may need to scroll past other results. And if you tap on it, it should move up the list next time you perform the same search.

intro-app-search-whoa-meme

Now, of course this is awesome, but the result is a little…bland.

Surely you can do more than give a name? Time to crack open the Core Spotlight framework and discover how.

Adding more information to search results

NSUserActivity has a property named contentAttributeSet. It is of the type CSSearchableItemAttributeSet, which allows you to describe your content with as many attributes as necessary. Review the CSSearchableItemAttributeSet class reference to see the many ways to describe your content with these attributes.

Below is the desired result, complete with each component’s property name called out:

intro-app-search-properties-diagram-1

You’ve already set title on NSUserActivity, and at the moment it’s all you see. The other three, thumbnailData, supportsPhoneCall and contentDescription are all properties of CSSearchableItemAttributeSet.

Open EmployeeSearch.swift. At the top, import MobileCoreServices:

import MobileCoreServices

MobileCoreServices is required for a special identifier that you’ll use to create the CSSearchableItemAttributeSet instance. You’ve already imported CoreSpotlight, which is required for all of the APIs prefixed with CS.

Still in EmployeeSearch.swift, add a new computed property named attributeSet to the Employee extension:

public var attributeSet: CSSearchableItemAttributeSet {
  let attributeSet = CSSearchableItemAttributeSet(
    itemContentType: kUTTypeContact as String)
  attributeSet.title = name
  attributeSet.contentDescription = "\(department), \(title)\n\(phone)"
  attributeSet.thumbnailData = UIImageJPEGRepresentation(
    loadPicture(), 0.9)
  attributeSet.supportsPhoneCall = true
 
  attributeSet.phoneNumbers = [phone]
  attributeSet.emailAddresses = [email]
  attributeSet.keywords = skills
 
  return attributeSet
}

When initializing CSSearchableItemAttributeSet, an itemContentType parameter is required. You then pass in kUTTypeContact from the MobileCoreServices framework. (Read about these types on Apple’s UTType Reference page.)

The attribute set contains the relevant search metadata for the current employee: title is the same as the title from NSUserActivity, contentDescription contains the employee’s department, title and phone number, and thumbnailData is the result of loadPicture() converted to NSData.

To get the call button to appear, you must set supportsPhoneCall to true and provide a set of phoneNumbers. Finally, you add the employee’s email addresses and set their various skills as keywords.

Now that these details are included, Core Spotlight will index each and pull the results during a search. This means that your users can now search for coworkers by name, department, title, phone number email and even skills!

Still in EmployeeSearch.swift, add the following line above the return in userActivity:

activity.contentAttributeSet = attributeSet

Here you tell the contentAttributeSet from NSUserActivity to use this information.

Build and run. Open Brent Reid’s record so the index can do its thing. Now go to the home screen pull up Spotlight and search for “brent reid”. If your previous search is still there, you’ll need to clear it and search again.

intro-app-search-app-screen-5

Voila! Aren’t you amazed with how little code it took to pull this off?

Great work! Now Spotlight can search for colleagues the user previously viewed. Unfortunately, there is one glaring omission…try opening the app from the search result. Nothing.

Opening search results

The ideal user experience is to launch the app directly to the relevant content without any fanfare. In fact — it’s a requirement — Apple uses the speed at which your app launches and displays useful information as one of the metrics to rank search results.

In the previous section, you laid the groundwork for this by providing both an activityType and a userInfo object for your NSUserActivity instances.

Open AppDelegate.swift and add this empty implementation of application(_:continueUserActivity:restorationHandler:) below application(_:didFinishLaunchingWithOptions:):

func application(application: UIApplication,
  continueUserActivity userActivity: NSUserActivity,
  restorationHandler: ([AnyObject]?) -> Void) -> Bool {
 
  return true
}

When a user selects a search result, this method is called — it’s the same method that’s called by Handoff to continue an activity from another device.

Add the following logic above return true in application(_:continueUserActivity:restorationHandler:):

guard userActivity.activityType == Employee.domainIdentifier,
  let objectId = userActivity.userInfo?["id"] as? String else {
    return false
}

This guard statement verifies the activityType is what you defined as an activity for Employees, and then it attempts to extract the id from userInfo. If either of these fail, then the method returns false, letting the system know that the activity was not handled.

Next, below the guard statement, replace return true with the following:

if let nav = window?.rootViewController as? UINavigationController,
  listVC = nav.viewControllers.first as? EmployeeListViewController,
  employee = EmployeeService().employeeWithObjectId(objectId) {
    nav.popToRootViewControllerAnimated(false)
 
    let employeeViewController = listVC
      .storyboard?
      .instantiateViewControllerWithIdentifier("EmployeeView") as!
        EmployeeViewController
 
    employeeViewController.employee = employee
    nav.pushViewController(employeeViewController, animated: false)
    return true
}
 
return false

If the id is obtained, your objective is to display the EmployeeViewController for the matching Employee.

The code above may appear a bit confusing, but think about the app’s design. There are two view controllers, one is the list of employees and the other shows employee details. The above code pops the application’s navigation stack back to the list and then pushes to the specific employee’s details view.

If for some reason the view cannot be presented, the method returns false.

Okay, time to build and run! Select Cary Iowa from the employees list, and then go to the home screen. Activate Spotlight and search for Brent Reid. When the search result appears, tap it. The app will open and you’ll see it fade delightfully from Cary to Brent. Excellent work!

Deleting items from the search index

Back to the premise of your app. Imagine that an employee was fired for duct taping the boss to the wall after a particularly rough day. Obviously, you won’t contact that person anymore, so you need to remove him and anybody else that leaves the company from the Colleagues search index.

For this sample app, you’ll simply delete the entire index when the app’s indexing setting is disabled.

Open EmployeeService.swift and add the following import statement at the top of the file.

import CoreSpotlight

Next, find destroyEmployeeIndexing(). Replace the TODO with the following logic:

CSSearchableIndex
  .defaultSearchableIndex()
  .deleteAllSearchableItemsWithCompletionHandler { error in
  if let error = error {
    print("Error deleting searching employee items: \(error)")
  } else {
    print("Employees indexing deleted.")
  }
}

This single parameterless call destroys the entire indexed database for your app. Well played!

Now to test out the logic; perform the following test to see if index deletion works as intended:

  1. Build and run to install the app.
  2. Stop the process in Xcode.
  3. In the simulator or on your device, go to Settings \ Colleagues and set Indexing to Viewed Records.
  4. Open the app again and select a few employees so that they are indexed.
  5. Go back to the home screen and activate Spotlight.
  6. Search for one of the employees you viewed and verify the entry appears.
  7. Go to Settings \ Colleagues and set Indexing to Disabled.
  8. Quit the app.
  9. Reopen the app. This will purge the search index.
  10. Go to the home screen and activate Spotlight.
  11. Search for one of the employees you viewed and verify that no results appear for the Colleagues app.

So deleting the entire search index was pretty easy, huh? But what if you want to single out a specific item? Good news — these two APIs give you more fine-tuned control over what is deleted:

  • deleteSearchableItemsWithDomainIdentifiers(_:completionHandler:) lets you delete entire “groups” of indexes based on their domain identifiers.
  • deleteSearchableItemsWithIdentifiers(_:completionHandler:) allows you work with individual records using their unique identifiers.

This means that globally unique identifiers (within an app group) are required if you’re indexing multiple types of records.

Note: If you can’t guarantee uniqueness across types, like when replicating a database that uses auto-incrementing IDs, then a simple solution could be to prefix the record’s ID with its type. For example, if you had a contact object with an ID of 123 and an order object with an ID of 123, you could set their unique identifiers to contact.123 and order.123 respectively.

If you’ve run into any issues you can download the completed project here.

Where to go from here?

This iOS 9 app search tutorial has covered iOS 9’s simple yet powerful approach to indexing the content inside your app using user activities. Searching isn’t limited to content though — you can also use it to index navigation points within an app.

Consider a CRM app that has multiple sections such as Contacts, Orders and Tasks. By creating user activities whenever a user lands on one of these screens, you’d make it possible for them to search for Orders and be directly to that section of your app. How powerful would this be if your app has many levels of navigation?

There are many unique ways to bubble up content to your users. Think outside the box and remember to educate your users about this powerful function.

This tutorial was an abbreviated version of Chapter 2, “Introducing App Search” from iOS 9 by Tutorials. If you’d like to learn about indexing large data sets with Core Spotlight or web content with iOS 9 please check out the book!

The post iOS 9 App Search Tutorial: Introduction to App Search appeared first on Ray Wenderlich.

Viewing all 4369 articles
Browse latest View live


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