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

Video Tutorial: iOS Animations Part 4: Advanced View Animations


iOS 8 by Tutorials Now Available!

$
0
0

iOS 8 by Tutorials

Note from Ray: Happy Wednesday – it’s book release day during the iOS 8 Feast!

This year at WWDC, Tim Cook declared iOS 8 the most significant change for developers since the introduction of the original iPhone OS SDK.

Not only does iOS 8 introduce an entirely new programming language—Swift—but it also introduces a wide range of new technologies and APIs, that allow entirely new classes of apps to be developed.

From Adaptive UI to App Extensions to Cloud Kit and Scene Kit, there’s a ton to learn to keep your iOS skills up-to-date. And as always, the Tutorial Team is here to help!

So today, we are happy to release perhaps the most anticipated of our new Swift books: iOS 8 by Tutorials!

About iOS 8 by Tutorials

iOS 8 by Tutorials is for intermediate to advanced developers, who already know the basics of iOS and Swift development but want to learn the new APIs introduced in iOS 8.

12 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 8 the easy way via tutorials, in the style you know and love from this site!

iOS 8 by Tutorials is a whopping 30 chapters and 775 pages. Let’s take a quick look at what’s inside.

Section I: Adaptive Layout

In this section, you’ll learn a completely new approach to building user interfaces for iOS 8; ones that are completely independent of device, screen size, resolution or device orientation.

Section1

You’ll be introduced to trait collections, size classes and universal storyboards, each of which will not only help you build the best interfaces for the current crop of iOS devices, but for future ones as well.

Here’s a quick overview of the chapters in this section:

  1. Chapter 1, Introducing Adaptive Layout: Before you dive into any code, you’ll be given an overview of what Adaptive Layout is and the reasons why you should give serious consideration to this new design paradigm.
  2. Chapter 2, Beginning Adaptive Layout: You’ll learn how to use Interface Builder to create user interfaces that seamlessly adapt to different devices, resolutions and orientations.
  3. Chapter 3, Intermediate Adaptive Layout: You’ll take Adaptive Layout to the next level and learn about self-sizing table view cells, installable views, trait collections, and size-class dependent assets.
  4. Chapter 4, Adaptive View Controller Hierarchies: Adaptive Layout affects not only your views, but view controllers as well. In this chapter you’ll learn about UISplitViewController, which is now available on iPhone, and how to upgrade a legacy application to take advantage of it.
  5. Chapter 5, Transition Coordinators: You’ll learn about the significant upgrades to transition coordinators, which were first introduced in iOS 7, and how to implement the new layout transitions as well.
  6. Chapter 6, Introducing Presentation Controllers: You’ll dig into iOS 8 approach of presenting view controllers and how the new APIs natively support adaptive layouts.
  7. Chapter 7, Custom Presentations: Building on Chapter 6, this chapter shows you how to add polish to your apps with custom presentations.

Section II: App Extensions

App Extensions are one of the most anticipated features of iOS 8; they let you extend your app’s content and functionality throughout the rest of the OS. Such extensions allow you to present custom functionality in other apps and gives users the power of your applications when and where they need it most.

Section2

Once you’ve completed the following seven chapters you’ll no doubt wonder how you ever survived without App Extensions – both as a developer and as a user.

Here’s a quick overview of the chapters in this section:

  1. Chapter 8, Introducing Extensions: Get a quick overview of what App Extensions are, their API, some of the limitations imposed by Apple, and an overview of how to add one to an existing app.
  2. Chapter 9, Today Extensions: You’ll learn how to create Today Extensions, the iOS equivalent of desktop widgets. They’re a great way to provide immediate and up-to-date information to your users.
  3. Chapter 10, Share Extensions: You’ll see how to craft extensions that let users share content directly to your web service from within whichever app they’re using at the time.
  4. Chapter 11, Action Extensions: Build exciting extensions that let users view, transform, or manipulate content within the context of the host app. Think Google Translate, but in real-time, in-place, without ever leaving the web page.
  5. Chapter 12, Photo Editing Extensions: Does your app offer photo filters, or some other kind of photo manipulation functionality? Ever wish you could provide that same functionality to other third-party apps, or even Apple’s stock apps? Well now you can! You’ll learn how to create extensions to do exactly this.
  6. Chapter 13, Document Provider Extensions: Learn how to build extensions that allow users to send and receive files between apps. You’ll also learn how to present cloud storage services as if they were the local file system. Document Providers allow iOS apps to share, edit, and update each other’s files.
  7. Chapter 14, Keyboard Extensions: You’ll replace the system keyboard with one of your own; iOS is now open to the world of intelligent swipe, Emoji, and animated GIF keyboards.

Section III: Major New Features

In this section, you’ll learn about the biggest and most important new features and frameworks in iOS 8 not already covered in this book.

Section3

You’ll cover Apple’s entry into the BaaS (Backend as a Service) sector, a new and highly performant way to render 3D content in your app, a much improved way to access and manipulate users’ photos, how to render your custom views and controls in Interface Builder and much, much more.

Here’s a quick overview of the chapters in this section:

  1. Chapter 15, Beginning CloudKit: You’ll learn what CloudKit is, why you should use it and gain a solid overview of the CloudKit convenience APIs.
  2. Chapter 16, Intermediate CloudKit: You’ll take a deeper dive into CloudKit, learn about the lower-level APIs that allow fine-grained control of your data as well as some of the limitations of the API and how best to work around them.
  3. Chapter 17, Beginning Scene Kit: Scene Kit has been available on OS X since 2012, but it’s now debuted in iOS 8 as well. Take advantage of this battle- tested framework to add 3D visualizations to your UIKit based apps.
  4. Chapter 18, Intermediate Scene Kit: You’ll learn how to introduce physics to your 3D visualizations, how to import 3D models and take advantage of the amazing Scene Kit support in Xcode 6.
  5. Chapter 19, Beginning Photos: The Assets framework has often been the Achilles’ heel of developers when working with photos on iOS. Apple has changed all of this by introducing a brand spanking new and modern framework, which aims to remove all the pain points of working with Assets. You’ll learn the basics of it in this chapter.
  6. Chapter 20, Intermediate Photos: Here you’ll take a closer look the new Photos framework, explore all four corners of the API, and learn about such things as asset caching and change requests.
  7. Chapter 21, Beginning Live Rendering: Interface Builder now renders custom views and controls just as if they were Apple’s own – finally! You’ll learn how to set up Live Rendering, how to expose your custom controls’ properties and then delve into creating custom controls using CALayer.
  8. Chapter 22, Intermediate Live Rendering: You’ll learn how to mix your custom views and controls with Apple’s stock controls for the purposes of Live Rendering, and you’ll see how Live Rendering can seriously speed up the development process.
  9. Chapter 23, Handoff: You’ll discover what Handoff is, see how it works, gain a solid overview of the API, and learn how to add this near-magical functionality to your iOS apps.
  10. Chapter 24, Beginning WebKit: Apple has finally replaced UIWebView! You’ll get to experience the long-awaited and modern WebKit as you create a bespoke web browser.
  11. Chapter 25, Intermediate WebKit: The new WebKit framework is useful beyond simply loading up web pages. You’ll learn how to hook into the page loading process to execute custom JavaScript, which allows you to bend any web page to your will.

Section IV: Bonus Chapters

In this section, you’ll learn about further additions to your iOS developer tool belt.

Section4

You’ll implement some real-time blurring, take in-depth look at all things Xcode 6, before rounding out the section with an overview of the new and shiny bits Apple added to the testing framework introduced in iOS 7.

  1. Chapter 26, iOS 8 Visual Effects: You’ll learn how to efficiently add static blurs to your app, then take advantage of the brand new UIVisualEffectView to add real-time blurring and vibrancy to your app as well.
  2. Chapter 27, An In-depth Look at Xcode 6: You’ll get up-to-speed on the most important updates in the latest revision of Apple’s development suite. There’s an absolute ton of changes, including a whole new programming language!
  3. Chapter 28, Xcode 6 & Swift: You’ll discover how Swift integrates with Xcode, what Playgrounds are and how they should be used, and you’ll be introduced to the Swift REPL, which among other things, lets you run Swift from the command line.
  4. Chapter 29, What’s New with Testing: You’ll learn all about the updates to XCTest, Apple’s testing framework, including asynchronous testing and performance testing.
  5. Chapter 30, What’s New with iTunes Connect: Learn about some exciting new features in iTunes Connect, such as adding videos to your App Store pages, TestFlight beta testing, and more.

Where To Go From Here?

If you have preordered iOS 8 by Tutorials, you can download it now on your My Loot page.

Otherwise, be sure to grab your copy now!

The Tutorial Team and I hope you enjoy iOS 8 by Tutorials, and we hope to see you use some of these amazing new APIs in your own apps!

iOS 8 by Tutorials Now Available! is a post from: Ray Wenderlich

The post iOS 8 by Tutorials Now Available! appeared first on Ray Wenderlich.

Beginning Adaptive Layout Tutorial

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

Learn how to make your apps use Adaptive Layout in iOS 8!

Note from Ray: This is an abbreviated version of a chapter from iOS 8 by Tutorials released as part of the iOS 8 Feast to give you a sneak peek of what’s inside the book. We hope you enjoy!

The introduction of Adaptive Layout in iOS 8 is 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 8 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 running iOS 8. 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:

image1

Select iOS\Application\Single View Application, then click Next:

image2

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

image3

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

image4

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:

image5

That’s right – it’s now a rough 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:

image6

This option is enabled by default for all new iOS 8 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.

image7

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 Red: 74, Green: 171, Blue: 247.

Next, select the TextContainer view and set its background color to Red: 55, Green: 128, Blue: 186.

Your view controller should now look like the screenshot below:

image8

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.

image9

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

image10

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:

image11

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

image12

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

The Size Inspector looks a bit different in Xcode 6. Select your image view and open the Size Inspector to see how it looks now:

image13

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:

image14

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 now look like the following:

image15

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:

image16

You’ll notice a few orange 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, then click on the + sign at the bottom of the left panel and select New Image Set from the popup menu:

image17

Double-click the title Image in the top-left corner of the detail view and rename Image to cloud, as shown below:

image18

Your new image set is simply a collection of image files, where each file has a purpose for a particular scenario. For example, an image set may have non-Retina, Retina and Retina HD versions of the same image. Since asset libraries are well integrated with UIKit, you refer to the entire image set by name and let iOS choose the correct file at runtime.

Note: If you’ve used an asset library in the past then you’ll notice something strange here – what’s this 3x all about?

This new display scale is known as Retina HD and is found on the iPhone 6 Plus. This means that in each direction, there are actually 3 pixels making up one point – i.e. there are 9 times more pixels in a 3x image than a 1x image!

Your cloud image set is currently empty. Download cloud_images.zip and unzip it. Inside you’ll find three files. Drag cloud_small.png from the finder to the 1x box in the cloud image set like so:

image19

Since the cloud image is white with a transparent background, you won’t be able to see it easily in the asset library. However, you can preview the image in QuickLook. Select the 1x box and press space:

image20

Now drag cloud_small@2x.png to the 2x box, and cloud_small@3x.png to the 3x box. Again, you won’t be able to clearly see the images, but you can check using QuickLook.

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 View\Mode drop down list, like so:

image21

Your storyboard now should look like the following:

image22

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:

image23

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

image24

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

image25

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 View\Assistant Editor\Show 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:

image27

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

image28

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:

image29

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:

image30

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

image31

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:

image32

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.Height then select Reverse First and Second Item in the First Item dropdown as shown below:

image33

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

image34

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:

image35

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

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:

image36

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:

image37

image38

Next, select the Attributes Inspector and set Text to Cupertino, Color to White, and the font to Helvetica Neue, 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:

image39

Use the Attributes Inspector to set the Text to 28C, the color to White, and the font to Helvetica Neue, 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 Views\Update Frames. You’ll see the storyboard update:

image40

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:

image41

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

image42

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 6 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:

image43

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 at this point. 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:

image44

You’ll immediately notice two changes in the editor:

image45

  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:

image46

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:

image47

image48

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:

image49

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:

image50

image51

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:

image52

image53

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.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, either from the document outline or on the storyboard:

image54

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:

image55

The context menu shows bullets next to existing constraints. Shift-click on Top Space to Top Layout Guide and Equal Widths:

image56

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 Views\Update frames. The storyboard updates and displays the new layout as shown below:

image57

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:

image58

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:

image59

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:

image60

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:

image61

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:

image62

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:

image63

Repeat exactly the same procedure for the temperature label.

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

image64

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:

image65

image66

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 book iOS 8 by Tutorials. In the book, we take this example a little bit further, and we have several more chapters on Adaptive Layout: Intermediate Adaptive Layout, Adaptive View Controller hierarchies, Transition Coordinators, and more.

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

Beginning Adaptive Layout Tutorial is a post from: Ray Wenderlich

The post Beginning Adaptive Layout Tutorial appeared first on Ray Wenderlich.

iOS 8 Today Extension Tutorial

$
0
0
Learn how to create a today extension in iOS 8!

Learn how to create a today extension in iOS 8!

Note from Ray: This is an abbreviated version of a chapter from iOS 8 by Tutorials released as part of the iOS 8 Feast to give you a sneak peek of what’s inside the book. We hope you enjoy!

iOS 8 introduces a new concept called App Extensions: a way for you to share your app’s functionality with other apps, with to the OS itself.

One of these types of extensions are Today Extensions, also known as Widgets. These allow you to present information in the Notification Center, which can be a great way to provide immediate and up-to-date information that the user is interested in.

In this tutorial, you’ll write a Today Extension that renders the current market price of a Bitcoin based on the United States Dollar.

Never has it been so easy to deliver valuable information so quickly to your users. Let’s get started!

Introducing Bitcoin

If you’re not familiar with Bitcoin, the short explanation is that it’s a digital cryptocurrency that’s still in its infancy. Aside from using it for peer-to-peer exchanges and purchases, Bitcoin trading allows the user to exchange it for a number of other cryptocurrencies like Dogecoin and Litecoin, and flat currency such as the US Dollar and the Euro.

As a relatively new currency, its market value fluctuates by the minute; there have been huge peaks and troughs in its short lifetime. Thus, it’s a perfect candidate for a Today Extension since investors will want up-to-the-second price quotes!

Introducing Crypticker

Since you’re writing an extension, you’ll first need a host app to extend; meet Crypticker.

Crypticker is a simple app that displays the current Bitcoin price, the difference between yesterdays price and the current price, as well as a price history chart. The chart includes 30 days of history; sliding your finger on the chart reveals the exact price for a specific day in the past.

The extension will contain all of these features, except for touching the chart to see the price for a specific day. There are some limitations within Today Extensions, especially when it comes to swiping. The swipe gesture often triggers swiping between the Today and Notifications sections within Notification Center, so it doesn’t really provide the best or most reliable user experience.

Getting Started

Download the Crypticker starter project to get started. The project contains the entire Crypticker app as described above. This tutorial will not focus on the development of the container app, so you may be pleasantly surprised to find this tutorial to be rather succinct. After all, you’re writing an extension, not an entire app.

Build and run. Please note that you’ll need a working Internet connection to pull the real-time prices from a web service.

CryptickerApp-1

The app looks very similar to the screenshot above; the data displayed will, of course, depend on how the Bitcoin market looks right now. Touching the chart near the bottom of the view will draw a line and display the price for the relevant day.

BTC widget

For the unfamiliar, BTC is shorthand for Bitcoin; much like USD stands for United States Dollar. The Today Extension will render a scaled down version of Crypticker’s primary view.

Theoretically, the Crypticker app has the ability to show pricing for multiple Cryptocurrencies, but your extension is specific to BTC. Therefore, its name shall be BTC Widget.

Note: Extensions, by nature, have just one simple purpose. If you wanted to provide information for another cryptocurrency, like Dogecoin, it would be best to package a second widget with the app or design your UI appropriately, perhaps like the Stocks widget.

By the end of the tutorial, your Today Extension will look something like this:

BTC-Widget-2

Add a Today Extension target

Extensions are packaged as a separate binary from their host app. So you’ll need to add a Today Extension target to the Crypticker project.

In Xcode’s Project Navigator, select the Crypticker project and add a new target by selecting Editor\Add Target… When the template picker appears, choose iOS\ Application Extension, and then Today Extension. Click Next.

AddTarget-3

Set the Product Name to BTC Widget, and verify that the language is Swift, the project is Crypticker and Embed in Application is also Crypticker. Click Finish.

AddTarget-4

When prompted activate the BTC Widget scheme. As the text indicates, another Xcode scheme will be created for you.

Congratulations! BTC Widget will now appear in your list of targets.

BTC-AddTarget-5

Make sure you select BTC Widget, then the General tab, and then press the + button under Linked Frameworks and Libraries.

BTC-LinkFramework-6

Select CryptoCurrencyKit.framework and click Add.

CryptoCurrencyKit is a custom framework used by the Crypticker app to retrieve currency prices from the web. Luckily for you, the incredibly kind and thoughtful developer behind Crypticker modularized the code into a framework, so that it can be shared between multiple targets :]

In order to share code between a host app and its extensions you must use a custom framework. If you don’t, you’ll find yourself duplicating a lot of code and violating an important rule of software engineering: DRY – or, Don’t Repeat Yourself. I’ll say it again: “Don’t repeat yourself”.

At this point, you’re ready to begin implementing the extension.

Notice there’s now a group in the Project navigator named after your new target, BTC Widget. This is where the Today Extension’s code is grouped, by default.

Expand the group and you’ll see there is a view controller, a storyboard file and an Info.plist file. Its target configuration also tells it to load its interface from MainInterface.storyboard, which contains a single view controller with the class set to TodayViewController.swift.

BTC-ListofFiles-7

You’ll notice some files you might expect to see are missing from the Today Extension template; like an app delegate for instance. Remember that extensions run inside another host app, so they don’t go through the traditional app lifecycle.

In essence, the lifecycle of the extension is mapped to the lifecycle of the TodayViewController.

Open MainInterface.storyboard. You’ll see a dark colored view with a light Hello World label. Today Extensions are most legible when they have a clear background and light or vibrantly colored text in order to harmonize with the dark, blurred background of Notification Center.

Make sure the BTC Widget scheme is selected in Xcode’s toolbar and build and run. This will cause a window to appear asking which app to run. Xcode is asking you which host app to run. Choose Today. This tells iOS to open Notification Center in the Today view, which in turn launches your widget. Notification Center is effectively the host app for Today Extensions.

BTC-PickAppToRun-8

This also causes Xcode to attach its debugger to the widget’s process.

BTC-WidgetWithNothing-9

Behold your widget. Cool, right? Whilst this is super-exciting stuff, the widget clearly needs a little work. It’s time to make it do some interesting things!

Note: You might notice a lot of Auto Layout errors printed to the console when launching the widget. This is an issue with the Xcode template, and will hopefully be resolved by Apple in the future. Don’t worry though, since you’ll be adding your own interface, including Auto Layout constraints.

Build the Interface

Open MainInterface.storyboard and delete the label. Set the view to 150pts tall and 320pts wide in the Size Inspector. Drag a Button, two Labels and a View from the Object Library onto the view controllers view.

  • Position one of the labels in the top left corner, and in the Attributes Inspector set its Text to $592.12 and its Color to Red: 66, Green: 145 and Blue: 211. This will display the current market price.
  • Position the other label to the right of the one you’ve just set up, but leave a margin to the right for the button. In the Attributes Inspector set its Text to +1.23 and its Color to Red: 133, Green: 191 and Blue: 37. This will display the difference between yesterdays price and the current price.
  • Move the button to the upper right of the view, and in the Attributes Inspector set its Image to caret-notification-center and delete its Title.
  • Finally, position the empty view below the two labels and the button, stretch it so it’s bottom and side edges are touching the containing view and set its Height to 98. In the Attributes Inspector set its Background to Clear Color, and in the Identity Inspector set its Class to JBLineChartView.

Note: There is a class named JBLineChartDotView that Xcode may suggest when typing, verify that you chose JBLineChartView.

The view and Document Outline should now look something like this:

BTC-DocumentOutline-10

Note: The view shown here has a white background for the purpose of visibility in this book. Your view will actually have a dark grey background that simulates how your view appears within Notification Center.

Don’t worry about laying things out exactly as shown, as you’ll soon be adding Auto Layout constraints to properly define the layout.

Now, expand the Crypticker group in the Project Navigator and select Images.xcassets. In the File Inspector, add the asset catalog to the extension’s target by checking the box to the left of BTC Widget.

This causes Xcode to include the image asset catalog from the Crypticker target in your BTC Widget target; that is where the caret-notification-center image that you used for your button resides. If you have an overlap of image assets between your container app and widget it is a good idea to use a dedicated catalog that only contains the assets that will actually be shared. This will reduce bloat in your finalized bundles by not including images that are unused.

BTC-AssetCatalog-11

Switch back to MainInterface.storyboard and open the Assistant Editor. Make sure TodayViewController.swift is the active file. Add this at the top of the file:

import CryptoCurrencyKit

This imports the CryptoCurrencyKit framework.

Next, you need to update the class declaration, like this:

class TodayViewController: CurrencyDataViewController, NCWidgetProviding {

This makes the TodayViewController a subclass of CurrencyDataViewController, and ensures it conforms to the NCWidgetProviding protocol.

CurrencyDataViewController is included in CryptoCurrencyKit and is also used by the primary view within Crypticker. Since the widget and app will be displaying similar information through a UIViewController, it makes sense to put reusable components in a superclass and then sub-class that as requirements vary.

NCWidgetProviding is a protocol specific to widgets; there are two methods from the protocol that you’ll be implementing.

Ctrl+drag from the button to the class, just below the class declaration. In the popup dialog make sure the Connection is set to Outlet, the Type is set to UIButton, and enter toggleLineChartButton for the Name. Click Connect.

BTC-WireToggleButton

Then ctrl+drag from the button to the bottom of the class this time. In the popup dialog change the Connection to Action, set the Type to UIButton, and enter toggleLineChart for the Name. Click Connect.

BTC-WireUpAction

TodayViewController subclasses CurrencyDataViewController, which has outlets for the price label, price change label and line chart view. You now need to wire these up. In the Document Outline, ctrl+drag from Today View Controller to the price label (the one with its text set to $592.12). Select priceLabel from the popup to create the connection. Repeat for the other label, selecting priceChangeLabel from the popup. Finally, do the same for the Line Chart View, selecting lineChartView from the popup.

BTC-WiringMiscThingsUp

Auto Layout

For your widget to be adaptive, you’ll need to set up Auto Layout constraints. New with iOS 8 is the concept of Adaptive Layout. The general idea is that views are designed with a single layout that can work on a variety of screen sizes. The view is considered adaptive when it can adapt to unknown future device metrics.

One of the constraints that you will add is to show and hide the chart, and help define the overall height of the widget. Notification Center will rely on you to display your widget with the appropriate height.

Select the $592.12 label and then select Editor\Size to Fit Content. If the Size to Fit Content option is disabled in the menu, deselect the label, and then reselect it and try again; sometimes Xcode can be a little temperamental. Next, using the Pin button at the bottom of the storyboard canvas, pin the Top and Leading space to 8 and 16 respectively. Make sure that Constrain to margins is turned off.

BTC-AutoLayout-1

Select the +1.23 label and again select Editor\Size to Fit Content. Then, using the Pin button, pin the Top and Trailing space both to 8.

BTC-AutoLayout-2

Select the Button, and using the Pin button, pin its Top and Trailing space to 0, and its Bottom space to 8. Pin both its Width and Height to 44. Make sure that Constrain to margins is turned off.

BTC-AutoLayout-3

You need to reduce the priority for the bottom spacing constraint of the button. Select the button, and then open the Size Inspector. Locate the Bottom Space to: constraint in the list of constraints, click Edit and change it’s Priority to 250.

By lowering the priority you are allowing the Auto Layout system to break this constraint, as it deems necessary. 250 is an arbitrary value that happens to be less than 1,000, which is what the priority is set to for all constraints by default and means required. This constraint needs to be broken when the widget is in its collapsed state. By having varying levels of priorities per constraint you are hinting to the system which constraints to break first or last when a conflict arises.

BTC-AutoLayout-4

Finally, select the Line Chart View. Using the Pin button, pin its Leading, Trailing and Bottom space to 0 and its Height to 98.

BTC-AutoLayout-5

From the Document Outline select the view controllers View, the choose Editor\Resolve Auto Layout Issues\All Views\Update Frames. This will fix any Auto Layout warnings in the canvas by updating the frames of the views to match their constraints. If Update Frames is not enabled then you laid everything out perfect and it is unnecessary to run.

With all of your constraints in place, the final step is to create an outlet for the line chart view’s height constraint. Find the Line Chart View in the Document Outline and click the disclosure triangle.

Then click the disclosure triangle for the Constraints to find the necessary height constraint. Select it, and then ctrl+drag into the Assistant Editor, releasing just below the other outlet. In the popup, make sure the Connection is set to Outlet and enter lineChartHeightConstraint for the Name. Click Connect.

BTC-AutoLayout-6

Implementing TodayViewController.swift

Now the interface is in place and everything is wired up, open up TodayViewController.swift in the Standard Editor.

You’ll notice you’re working with bog-standard UIViewController subclass. Comforting, right? Although later you’ll encounter a new method called widgetPerformUpdateWithCompletionHandler from the NCWidgetProviding protocol. You’ll learn more about it towards the end of the tutorial.

This view controller is responsible for displaying the current price, price difference, responding to a button press and showing the price history in a line chart.

Define a property at the top of TodayViewController that you’ll use to track if the line chart is visible or not:

var lineChartIsVisible = false

Now replace the boilerplate viewDidLoad() method with the following implementation:

override func viewDidLoad() {
  super.viewDidLoad()
  lineChartHeightConstraint.constant = 0
 
  lineChartView.delegate = self;
  lineChartView.dataSource = self;
 
  priceLabel.text = "--"
  priceChangeLabel.text = "--"
}

This method does the following:

  1. Sets the line view charts’ height constraints’ constant to 0, so that it’s hidden by default.
  2. Sets self as the data source and delegate for the line chart view.
  3. Sets some placeholder text on the two labels.

Still in TodayViewController, add the following method:

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)
 
  fetchPrices { error in
    if error == nil {
      self.updatePriceLabel()
      self.updatePriceChangeLabel()
      self.updatePriceHistoryLineChart()
    }
  }
}

The fetchPrices method is defined in CurrencyDataViewController, and is an asynchronous call that takes a completion block. The method makes a request to the web-service mentioned at the beginning of the tutorial to obtain Bitcoin price information.

In the method’s completion block update both labels and the line chart. The update methods are also defined for you in the super-class. They simply take the values retrieved by the fetchPrices method and format them appropriately for display.

Due to the design of the widget, you’ll also need to implement widgetMarginInsetsForProposedMarginInsets to provide custom margin insets. Add the following code to TodayViewController:

func widgetMarginInsetsForProposedMarginInsets
  (defaultMarginInsets: UIEdgeInsets) -> (UIEdgeInsets) {
    return UIEdgeInsetsZero
}

By default, widgets have a large left margin, which is evident in many of Apple’s default widgets. If you want to fill the entire width of Notification Center, you must implement this method and return UIEdgeInsetsZero, which translates to 0 on all sides.

Now it’s time to see what you have so far. Select the BTC Widget scheme. Build and run. Choose Today as the app to run when prompted.

  • If Notification Center doesn’t appear, swipe down from the top of the screen to activate it.
  • If the widget doesn’t appear in Notification Center, you’ll need to add it via the Edit menu. Towards the bottom of the Today’s view content you will see an Edit button. Tap the button to reveal a menu of all Today Extensions that are installed on the system. Here you can enable, disable and re-order them as desired. Enable BTC Widget if not already.

BTC-Widget-Almost1

Cool! Your widget now displays real-time Bitcoin pricing right in Notification Center. But you may have noticed a problem; the button doesn’t work and you can’t see the line chart.

BTC-YUNO

Next, you’ll implement toggleLineChart for the button that you added so that it expands the widget’s view and exposes the line chart. As the method name implies, this button will behave as a toggle; it will also collapse the view to hide the chart.

Replace the empty toggleLineChart method with the following code:

@IBAction func toggleLineChart(sender: UIButton) {
  if lineChartIsVisible {
    lineChartHeightConstraint.constant = 0
    let transform = CGAffineTransformMakeRotation(0)
    toggleLineChartButton.transform = transform
    lineChartIsVisible = false
  } else {
    lineChartHeightConstraint.constant = 98
    let transform = CGAffineTransformMakeRotation(CGFloat(180.0 * M_PI/180.0))
    toggleLineChartButton.transform = transform
    lineChartIsVisible = true
  }
}

This method manipulates the constant of the Line Chart Views’ height constraint to toggle its display. It also applies a rotation transform to the button so it accurately reflects the visibility of the chart.

After updating the constraint, you must reload the chart’s data so that it redraws based on the new layout.

You’ll do this in viewDidLayoutSubviews. Add the following to TodayViewController:

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  updatePriceHistoryLineChart()
}

Make sure the BTC Widget scheme is selected. Build and run. Choose Today as the app to run when prompted.

On the left, you’ll see how the widget appears when the chart is hidden. On the right, you’ll see how it appears when opened. Not too shabby!

BTC-Widet-Almost2

A quick update of the line’s color and you’ll have one sharp looking widget. Add the following method to TodayViewController:

override func lineChartView(lineChartView: JBLineChartView!,
  colorForLineAtLineIndex lineIndex: UInt) -> UIColor! {
    return UIColor(red: 0.17, green: 0.49,
      blue: 0.82, alpha: 1.0)
}

Make sure the correct scheme is still selected. Build and run. Choose Today as the app to run when prompted.

BTC-Widet-Done

Your last order of business is to add support to your widget to update its view when it’s off-screen, by allowing the system to create a snapshot. The system does this periodically to help your widget stay up to date.

Replace the existing implementation of widgetPerformUpdateWithCompletionHandler with the following code:

func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
  fetchPrices { error in
    if error == nil {
      self.updatePriceLabel()
      self.updatePriceChangeLabel()
      self.updatePriceHistoryLineChart()
      completionHandler(.NewData)
    } else {
      completionHandler(.NoData)
    }
  }
}

This method does the following:

  • It fetches the current price data from the web service by calling fetchPrices.
  • If there’s no error the interface is updated.
  • Finally – and as required by the NCWidgetProviding protocol – the function calls the system-provided completion block with the .NewData enumeration.
  • In the event of an error, the completion block is called with the .Failed enumeration. This informs the system that no new data is available and the existing snapshot should be used.

And that wraps up your Today Extension! You can download the final project here.

Where To Go From Here?

The iOS 8 Notification Center is your own personal playground! Widgets have been available on some other mobile operating systems for years, and Apple has finally provided you the ability to create them.

As an enterprising developer, you might want to take another look at your existing apps and think about how you can update them with widgets. Take it a step further and dream up new app ideas that exploit the possibilities of widgets.

If you’d like to learn more about creating other types of iOS 8 App Extensions, check out our book iOS 8 by Tutorials where you can learn about Photo Extensions, Share Extensions, Action Extensions, and more!

We can’t wait to see what you come up with, and hope to have your Today Extensions at the top of our Notification Centers soon!

iOS 8 Today Extension Tutorial is a post from: Ray Wenderlich

The post iOS 8 Today Extension Tutorial appeared first on Ray Wenderlich.

Video Tutorial: Introduction to Unity Part 10: Unity API

Beginning CloudKit Tutorial

$
0
0
Learn how to store your app's data in the cloud with CloudKit!

Learn how to store your app’s data in the cloud with CloudKit!

Note from Ray: This is an abbreviated version of a chapter from iOS 8 by Tutorials released as part of the iOS 8 Feast to give you a sneak peek of what’s inside the book. We hope you enjoy!

CloudKit, Apple’s new remote data storage service for apps based on iCloud, provides a low-cost option to store and share app data using users’ iCloud accounts as a back-end storage service.

There are two parts to CloudKit:

  1. A web dashboard to manage the record types along with any public data.
  2. A set of APIs to transfer data between iCloud and the device.

Cloud Kit is secure as well; users’ private data is completely protected, as developers can only access their own private database and aren’t able to look at any users’ private data.

CloudKit is a good option for iOS-only applications that use a lot of data but don’t require a great deal of server-side logic.

In this CloudKit tutorial, you’ll get hands-on experience using CloudKit, by creating a restaurant rating app with a twist called BabiFüd!

Note: To work with the sample app in this CloudKit tutorial you’ll need an active iOS developer account. Without one you won’t be able to enable the iCloud entitlements or access the CloudKit dashboard.

Why CloudKit?

First things first, you might wonder why you should choose CloudKit over Core Data, other commercial BaaS (Back end as a Service) offerings, or even rolling your own server.

The answers are three: simplicity, trust, and cost.

Simplicity

Unlike other backend solutions, CloudKit requires little setup. You don’t have to choose, configure, or install servers and worry about scaling and security.

Simply registering for the iOS Developer Program makes you eligible to use CloudKit – you don’t have to register for additional services or create new accounts. As part of turning on the CloudKit capabilities, all the necessary setup magic on the servers happens automatically.

There’s no need to download additional libraries and configure them – CloudKit is imported like any other iOS framework. The CloudKit framework itself also provides a level of simplicity by offering convenience APIs for common operations.

There’s also simplicity for users. Since CloudKit uses the iCloud credentials already entered when the device is set up (or through the Settings app), there’s no need for building complicated login screens. As long as they are logged in, users can seamlessly start using your app!

Trust

Another benefit to CloudKit is that users can trust the privacy and security of their data by trusting Apple, rather than app developers, since CloudKit insulates the users’ data from you.

While this lack of access can be frustrating during development (for debugging) it is a net plus since you don’t have to worry about security or convince users their data is secure. Even if an app user isn’t aware of CloudKit’s nuances, if they trust iCloud they can trust you.

Cost

Finally, for any developer the cost of running a service is a huge deal. Even the cheapest server hosts do not care if your app is free or cheap, and so it will cost even a little bit to run an app.

With CloudKit, you get a reasonable amount of storage and data transfer for public data for free. See https://developer.apple.com/icloud/documentation/cloudkit-storage/ for details.

These strengths make the CloudKit service a low-hassle no-brainer for Mac and iOS apps.

Introducing BabiFüd

The sample app for this CloudKit tutorial, BabiFüd, is the freshest take on the standard “rate a location” style of app. However, instead of reviewing locations based upon food quality, speed of service, or price, users rate locations in terms of child-friendliness. This includes availability of changing facilities, booster seats, and healthy food options.

The app contains four tabs: a list of nearby locations, a map showing nearby locations, user-generated notes, and settings. You can get a glimpse of the app in action from the two screenshots below:

NearbyMap

A model class backs these views and wraps the calls to CloudKit. CloudKit objects are termed records; the main record type in your model is an Establishment, which represents the various locations in your app. Along the way you’ll add Rating and Note records to your database as well.

Getting Started

Start by downloading the starter project for this CloudKit tutorial.

You’ll have to change the Bundle Identifier and Team of your app before you can start coding. You need a team set in order to get the necessary entitlements from Apple, and having a unique bundle identifier makes the process a whole lot easier.

To do this, open BabiFud.xcodeproj in Xcode. Select the BabiFud project in the Project Navigator, and then select the BabiFud target. With the General tab selected, replace the Bundle Identifier with something unique; I recommend using reverse domain name notation, and include the project name. Next, select the appropriate Team:

Change team name

That takes care of the Bundle Identifier and Team. Now you’ll need to get your app set up for CloudKit, and create some containers to hold your data.

Entitlements and containers

You’ll need a container to hold the app’s records before you can add any via your app. A container is the term for the conceptual location of all the app’s data on the server—it is the grouping of public and private databases. To create a container you first need to enable the iCloud entitlements for your app.

To do so, select the Capabilities tab in the target editor, and then flip the switch in the iCloud section to ON, as shown in the screenshot below:

Capabilities

At this point, Xcode might prompt you to enter the Apple ID associated with your iOS developer account; if so, type it in as requested. Finally, enable CloudKit by checking the CloudKit checkbox in the Services group.

This creates a default container named iCloud.<your app’s bundle id>, as illustrated in the screenshot below:

Cloudkit container

If you see any warnings or errors when creating entitlements, when building the project, or when running the app, and it’s complaining about the container ID, here are some troubleshooting tips:

  • If there are any warnings or errors shown in Steps group in the iCloud section, try pressing the Fix Issue button. This might need to be done a few times.

    Fix Issues

  • It’s important that the app’s bundle id and iCloud containers match, and exist in the developer account. For example, if the bundle identifier is “com.<your domain>.BabiFud”, then the iCloud container name should be “iCloud.” plus the bundle bundle id: “iCloud.com.<your domain>.BabiFud”.
  • The iCloud container name must be unique because this is the global identifier used by CloudKit to access the data. Since the iCloud container name contains the bundle id, this means that the bundle id must also be unique (which is why it has to be changed from com.raywendrelich.BabiFud).
  • In order for the entitlements piece to work, the app/bundle id has to be listed in the App IDs portion of Certificates, Identifiers, and Profiles portal. This means that that the certificate used to sign the app has to be from the set team id, and has to list the app id, which also implies the iCloud container id.


    Normally Xcode does all of this automatically for you, if you are signed in to a valid developer account. Unfortunately sometimes this gets out of sync. It can help to start with a fresh ID, and then change the CloudKit container ID to match, using the iCloud capabilities pane. Otherwise, to fix it you may have to edit the info.plist or in BabiFud.entitlements files to make sure the id values there reflect what you set for the bundle id.

Introducing the CloudKit Dashboard

After setting up the necessary entitlements, the next step when implementing CloudKit is to create some record types that define the data used by your app, and you can do this using the CloudKit dashboard. Click CloudKit Dashboard, found in the target’s Capabilities pane, under iCloud, as shown below:

Cloudkit dashboard button

Note: You can also launch the CloudKit dashboard by opening the URL https://icloud.developer.apple.com/dashboard/ in your browser.

You’ll see the dashboard appear, like so:

Dashboard Start

Here’s a basic overview of what you’ll see in the CloudKit dashboard, for the purposes of this tutorial:

The SCHEMA section of the left-hand pane represents the high level objects of a CloudKit container: Record Types, Security Roles, and Subscription Types. You’ll only be concerned with Record Types in this tutorial.

A Record Type is a set of attributes that define individual records. In terms of object orientated programming, a Record Type is like a class template for individual objects. A record can be considered an instance of a particular Record Type. It represents structured data in the container, much like a typical row in a database, and encapsulates a series of key/value pairs.

The PUBLIC DATA and PRIVATE DATA sections let you add data to, or search for data, in the databases you have access to; remember, as a developer you access all public data, but only your own private data. The User Records store data about the current iCloud user such as name and email. A Record Zone (here noted as the Default Zone) is used to provide a logical organization to a private database, by grouping records together. Custom zones support atomic transactions by allowing multiple records to be saved at the same time before processing other operations. Custom zones are outside the scope of this book.

The ADMIN section provides a means to configure the dashboard permissions available to your team members. If you have multiple people on your development team, you can restrict their ability to edit data here. This too is out-of-scope for this book.

Adding the Establishment Record Type

With Record Types selected, click the + icon in the top left of the detail pane to add a new record type, as shown below:

Add New Record Type

Name your new record type Establishment.

Think about the design of your app for a moment. Each establishment you’ll want to track has lots of data: a name, a location, and whether or not various child-friendly options are available. Record types use attributes to define the various pieces of data contained in each record.

You’ll see a row of fields where you can define the Name, Attribute Type, and Index, as shown below. A template StringAttribute has been automatically created for you.

New Record Type template

Replace StringAttribute and then add the following attributes one at a time. Click Add Attribute… to add new attributes as required:

table_ck_attributes

When you’re done, your list of attributes should look like the following:

Establishment all filled out

Click Save at the bottom of the page to save your new record type.

You’re now ready to add some sample establishment records to your database.

Select Default Zone under the PUBLIC DATA section in the navigation pane on the left; this zone will contain the public records for your app. Select the Establishment record type from the dropdown list in the center pane if it’s not already selected, then click the + icon in the right detail pane, as shown in the screenshot below:

Add a new record

This will create a new, empty Establishment record as shown below:

New Establishment Template

At this point you’re ready to enter some test data for your app.

The following sample establishment data is fictional; the data has them located near Apple’s headquarters so they’re easy to find in the simulator.

Enter each record as described below:

Note: The image for each CoverPhoto element is included in the Supporting Files\Sample Images folder in the Xcode project. To add the image to the establishment record, simply drag it to the CoverPhoto field. The image uploads automatically when the record is saved.

table_ck_records

Once all three records have been saved, the dashboard should look like:

Completed record

For each record, the values entered are the database representation of the data. On the app side, the data types are different. For example SeatingType and ChangingTable are an enum, so the specified Int value corresponds to the of the enumeration values. For HealthyOption and KidsMenu, the Int values represent Boolean types: a 0 means that establishment doesn’t have that option and a 1 means that it does.

Return to Xcode. It’s time to start integrating this data into your app.

Querying Establishment Records

CKQuery objects are used to select records from a database. A CKQuery describes how to find all records of a specified record type that match certain criteria. These criteria can be something like “all records with a Name attribute that starts with ‘M’”, or “all records that have booster seats”, or “all records within 3km.” These types of expressions are coded in Cocoa with NSPredicate objects. A NSPredicate evaluates objects to see if they match the specified criteria. Predicates are also used a lot in Core Data and are a natural fit for CloudKit, because predicates commonly are defined as a comparison on an attribute.

CloudKit supports only a subset of available NSPredicate functions. These include mathematical comparisons, some string and set operations (such as “attribute matches one of the items in a list”), and new special distance function. The function distanceToLocation:FromLocation: was added to NSPredicate for CloudKit to match records with a location field within a specified radius for a known location. This type of predicate is covered in detail below. For other types of queries, the CKQuery class reference has a detailed list of the supported functions and how to use them.

Note: CloudKit includes support for CLLocation objects; these are Core Location Framework objects that contain geospatial coordinates. This makes it quite easy to run a query for finding establishments inside of a geographic region – without doing all of the messy coordinate math yourself.

Open Model\Model.swift; this contains stubs for all server calls.

Replace fetchEstablishments(location:, radiusInMeters:) with the following:

func fetchEstablishments(location:CLLocation,
                           radiusInMeters:CLLocationDistance) {
// 1  
let radiusInKilometers = radiusInMeters / 1000.0 
  // 2
  let locationPredicate = NSPredicate(format: "distanceToLocation:fromLocation:(%K,%@) < %f",
    "Location",
    location,
    radiusInKilometers) 
  // 3
  let query = CKQuery(recordType: EstablishmentType,
                       predicate:  locationPredicate) 
  // 4
  publicDB.performQuery(query, inZoneWithID: nil) { 
    results, error in
    if error != nil {
      dispatch_async(dispatch_get_main_queue()) {
        self.delegate?.errorUpdating(error)
        return
      }
    } else {
      self.items.removeAll(keepCapacity: true)
      for record in results{
        let establishment = Establishment(record: record as CKRecord, database: self.publicDB)
        self.items.append(establishment)
      }
      dispatch_async(dispatch_get_main_queue()) {
        self.delegate?.modelUpdated()
        return
      }
    }
  }
}

Taking each numbered comment in turn:

  1. CloudKit uses kilometers in its distance predicates; this line simply converts radiusInMeters to kilometers.
  2. The predicate filters establishments based on their distance in kilometers from the current location. This statement finds all establishments with a location value that puts them within the specified distance from the user’s current location.
  3. CKQuery objects are created using a predicate and a record type. Both will be used when performing the query.
  4. Finally, performQuery(_:, inZoneWithID:, completionHandler:) sends your query up to iCloud, returning any matching results. In this case, you’re running the query against your default zone; that is, your public database. This is because you passed nil as the inZoneWithID parameter. If you want to retrieve records from both public and private databases, you have to query each database using a separate call.

This is all fine and good, but you’re probably wondering where the CKDatabase instance publicDB comes from. Take a look the top of Model:

let container : CKContainer
let publicDB : CKDatabase
let privateDB : CKDatabase
 
init() {
  // 1
  container = CKContainer.defaultContainer() 
  // 2
  publicDB = container.publicCloudDatabase 
  // 3
  privateDB = container.privateCloudDatabase 
}

Here you define your databases:

  1. The default container represents the one you specified in the iCloud capabilities pane.
  2. The public database is the one shared among all users of your app.
  3. The private database contains only the data belonging to the currently logged-in user; in this case, you.

This code will retrieve some local establishments from the public database, but it has to be wired up to a view controller in order to see anything in the app.

Setting up the Requisite Callbacks

You can take care of notifications with the familiar delegate pattern. Here’s the protocol from the top of Model.swift that you’ll implement in your view controller:

protocol ModelDelegate {
  func errorUpdating(error: NSError)
  func modelUpdated()
}

Open MasterViewController.swift and replace modelUpdated() with the following:

func modelUpdated() {
  refreshControl?.endRefreshing() 
  tableView.reloadData() 
}

This executes when new data is available. All the wiring up of the table view cells to CloudKit objects has already been taken care of in tableView(_: cellForRowAtIndexPath:). Feel free to take a look.

Now replace errorUpdating(error:) with the following:

func errorUpdating(error: NSError) {
  let message = error.localizedDescription 
  let alert = UIAlertView(title: "Error Loading Establishments",
   message: message, delegate: nil, cancelButtonTitle: "OK")
  alert.show()
}

Any errors produced by query result in this method being called. Errors can occur from poor networking conditions, or CloudKit specific issues such as missing or incorrect user credentials, or when the record you requested wasn’t found.

Note: Good error handling is essential when dealing with any kind of remote server. For now, you’ll just display a popup informing the user that there has been an issue.

Build and run using the simulator. You should see list of nearby establishments, as shown below:

Places with no assets

You can see the establishment name, and the services the establishments offer, but none of the images are being displayed. What gives?

When you retrieved the establishment records, you automatically retrieved the images as well. However, you still need to perform the necessary steps to load the images into your app.

Troubleshooting Steps

If the list doesn’t populate, make sure you have the correct location set by selecting Debug\Location\Apple. If you needed to change the location, pull the table down to force a refresh, instead of waiting for a location trigger.

If you’re using an iPhone or iPad, location services are enabled, and the list still isn’t populating, it’ll be because the establishments aren’t close enough in to your current location. You have two options here: either change the coordinates of the sample data to be closer to your current location, or use the simulator to run the app. There is a third option, but it’s not terribly practical as you’d have to travel to Cupertino and hang around the Apple campus :]

If the data isn’t appearing properly – or isn’t appearing at all – inspect the sample data using the CloudKit dashboard. Make sure all of the records are present, and that you’ve added them to the default zone and they have the correct values. If you need to re-enter the data, you can delete records by clicking the trash icon as shown below:

CloudKit trash

Debugging CloudKit errors can be tricky at times. As of writing, CloudKit error messages don’t contain a tremendous amount of information, so to determine the cause of the error you’ll need to look at the error code and what particular database operation you’re attempting. Using the numerical error code, look up the matching CKErrorCode enum. The name and description in the documentation will help narrow down the cause of the issue. See below for some examples.

Note: For a good primer on CloudKit errors, read the official Apple documentation on the CKErrorCode enum under CloudKit constants.

Here are some common error enums, and suggestions on how to address them:

  • .BadContainer and .MissingEntitlement
    • Check that you have specified a container in the iCloud entitlements section, that it matches the CKContainer object, and that the container exists in the CloudKit dashboard.
  • .NotAuthenticated and .PermissionFailure
    • Check that you’ve correctly entered your iCloud credentials in Settings.app and that iCloud is enabled.
  • .UnknownItem
    • Check that your RecordType string matches the record type name in the CloudKit dashboard.

Working with Binary Assets

An asset is binary data, such as an image, that you associate with a record. In your case, your app’s assets are the establishment photos shown in the Nearby table view.

In this section you’ll add the logic to load the assets that were downloaded when you retrieved the establishment records.

Open Model\Establishment.swift and replace loadCoverPhoto(completion:) with the following code:

func loadCoverPhoto(completion:(photo: UIImage!) -> ()) { 
  // 1
  dispatch_async(
   dispatch_get_global_queue(
    DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)){
    var image: UIImage!
    // 2
    let coverPhoto = self.record.objectForKey("CoverPhoto") as CKAsset! 
    if let asset = coverPhoto {
      // 3
      if let url = asset.fileURL { 
        let imageData = NSData(contentsOfFile: url.path!)
        // 4
        image = UIImage(data: imageData) 
      }
    }
    // 5
    completion(photo: image) 
  }
}

This method loads the image from the asset attribute as follows:

  1. Although you download the asset at the same time that you retrieve the rest of the record, you want to load the image asynchronously, so wrap everything in a dispatch_async block.
  2. Assets are stored in CKRecord as instances of CKAsset, so cast appropriately.
  3. Load the image data from the local file URL provided by the asset.
  4. Use the image data to create an instance of UIImage.
  5. Execute the completion callback with the retrieved image.

Build and run. The establishment images should now appear:

With assets

There are two gotchas with CloudKit assets:

  1. Assets can only exist in CloudKit as attributes on records; you can’t store them on their own. Deleting a record will also delete any associated assets.
  2. Retrieving assets can negatively impact performance because the assets are downloaded at the same time the rest of the record data is. If your app makes heavy use of assets, you should store references to a different type of record that holds just the asset.

Where To Go From Here?

The app is now able to download Establishment records and load their details and photos into the table view. You can take what’s been built so far (download here) and enhance it in several ways:

  • Allow the user to add their own pictures, notes, reviews, and complaints. This will help them from re-visiting a bad experience.
  • Let the user create new Establishment records using the map. The functions added to your Model class can serve as an example for saving the record to either the public or private databases.
  • Add filtering and searching. The Model class constructs a CKQuery with a distance predicate, but this can be modified to be a more complex predicate. CloudKit supports text searching of string attributes as well.
  • Improve the performance of the app and the data loading experience. This tutorial used the available convenience methods to invoke the necessary completion handlers when everything is ready. Instances of CKDatabase also have NSOperation-based methods that provide more control over how the API is executed, such the ability to receive data during the operation instead of all at the same time, once the operation is complete.
  • Provide caching and synchronization so the app remains responsive offline and keeps the content up to date when it reconnects to a network.

Chapter 15 – Beginning CloudKit of iOS 8 By Tutorials covers the this tutorial and through the first bullet. The chapter shows how to create new records and add references between records by allowing users to rate the restaurants from 1 to 5 stars, and to upload their own photos. Also covered is how to create records that are only visible to their creating user, by adding personal notes about each establishment.

Chapter 16 – Intermediate CloudKit of iOS 8 by Tutorials takes BabiFüd even further. It covers the last two suggestions. Importantly, it covers topics like performance, error handling, and offline usage.

With CloudKit you can really take your apps up to the next level and beyond with this great Apple-provided backend API. We can’t wait to see what you make, and feel free to join us and our kids for a slice at Caesar’s Pizza!

Beginning CloudKit Tutorial is a post from: Ray Wenderlich

The post Beginning CloudKit Tutorial appeared first on Ray Wenderlich.

iOS 8 Visual Effects Tutorial

$
0
0
Learn how to blur views and add vibrant text in iOS 8!

Learn how to blur views and add vibrant text in iOS 8!

Note from Ray: This is an abbreviated version of a chapter from iOS 8 by Tutorials released as part of the iOS 8 Feast to give you a sneak peek of what’s inside the book. We hope you enjoy!

iOS 7 raised the bar for iOS app design, especially when it came to on-screen effects.

One of the most significant changes was the use of blur throughout iOS 7, most notably in Notification Center and Control Center, as illustrated below:

blurs

However, when developers were handed the task of adding similar blur effects to their own apps, they hit a serious roadblock. Apple had decreed that the crop of devices available at the time simply weren’t powerful enough to support real-time blurs in third-party apps, claiming developers would over-use blurs resulting in slower apps with much degraded user experiences.

Developers, being the crafty individuals they are, soon created their own blurring algorithms from static blurring of images all the way to live-blur hacks.

A lot of these solutions worked pretty well. However, iOS 8 adds official, high-performance blur effects to the developer’s toolbox – and you’ll be surprised how easy it is to use the new blur effects.

Note: For one approach to using static images to mimic live blur effects, check out this blog post.

Clear Facts on Blurs

Executing blurs in a tasteful – and efficient – manner takes a bit of finesse. In this section you’ll learn about a common blurring algorithm and how you can use blurs to improve the user experience of your apps.

How Blurs Work

All blurs start with an image. To achieve a blur, you apply a blurring algorithm to each pixel in the image; the resulting image then contains an evenly blurred version of the original. Blurring algorithms vary in style and complexity, but you’ll use a common algorithm known as Gaussian blur in this tutorial’s project.

Blurring algorithms generally examine each pixel of an image and use the surrounding pixels to calculate new color values for that pixel. For example, consider the following theoretical image grid below:

blur-how1

Each cell in the above grid represents an individual pixel, and each pixel has a value between 1 and 10. Consider the case where the algorithm is evaluating the center pixel. The algorithm averages the values of the surrounding pixels and inserts this averaged value into the center pixel, resulting in the following new pixel grid:

blur-how2

You then repeat this process for every pixel in the original image.

The sample algorithm above only examined one pixel in each direction to create the new averaged value. You can expand this blur radius even further to increase the amount of blurring in your image, as demonstrated in the image below:

3px and 16px Gaussian Blur applied to an image

3px and 16px Gaussian Blur applied to an image

Note: Generally, the greater your blur radius is, the more processing power you’ll require to process the image. iOS offloads most image processing activities to the GPU to keep the main thread free.

Blur Design Strategies

Humans have a tendency to pay attention to elements that are in-focus and ignore things that aren’t. Believe it or not, this is a natural consequence of how our eyes work. Focusing on an object as it moves closer or further away from the eye is known as accommodation; it’s what helps you perceive depth and the distance of objects around you.

App designers exploit this fact and blur unimportant items on the screen to direct the user’s attention to the remaining non-blurred elements, as demonstrated in this screenshot of the popular Twitter client Tweetbot:

tweetbot

The user interface in the background is barely recognizable in the image above. This provides contextual awareness to the user about where they are in the navigational hierarchy. For instance, you’d expect to return to the blurred-out view in the background once you select one of the accounts displayed.

Note: Be careful about the overuse of blurs in your mobile apps. Although blurs can provide great-looking effects, they can be distracting and annoying if used inappropriately or too often.

Follow the standard design approach to use blurs to direct a user’s attention to things that matter and you’ll seldom go wrong. See the Designing for iOS section of the iOS Human Interface Guidelines document found on Apple’s iOS developer center for more.

Getting Started

To learn how to implement blurring, you’ll add some blur effects to a brand new Brothers Grimm fairytale app – aptly named Grimm.

The app displays a library of fairytales to the user; when the user taps on a fairytale, the app presents the full story on screen. The user can customize the display font, text alignment, or even the color theme for daytime or nighttime reading.

Start by downloading the starter project, then open Grimm.xcodeproj in Xcode. Open Grimm.storyboard and take a look at the view controllers contained in the app as illustrated below:

storyboard

You can ignore the very first view controller in the image above as it’s simply the root navigation controller of the app. Taking each numbered view controller in turn, you’ll see the following:

  1. The first controller is a StoryListController, which displays a list of all of the fairy tales in the database.
  2. When you tap a story in the list you segue to this detail StoryViewController, which displays the title and text of the selected fairy tale.
  3. This is a OptionsController which is contained inside the StoryViewController and displays the available font, text alignment, and color options. To display it simply tap the settings icon in the detail controller.

Build and run. You’ll see the initial screen as shown below:

firstrun

Have some fun exploring the app; select different stories, tap the ellipsis icon and swipe to different fonts and reading modes to understand how the user interface functions.

Note: You can use any simulator or iOS 8 device to run this app except for the iPad 2. Apple disabled much of the iPad 2’s ability to display blurs for performance reasons. The app will still work on the iPad 2; however, you won’t be able to see any of the neat blur effects.

Once you have a handle on how the app behaves, head straight to the next section to learn how to apply blur effects to the app.

Manual Blur Techniques

Eagle-eyed readers might have noticed that there is some legacy Objective-C code in this project.

objcnswift

Don’t fret – the category in this project has been used by hundreds of apps and is incredibly solid. It’s been bridged into all of your Swift files using Grimm-Bridging-Header.h since there’s no need to rewrite it for Swift.

Note: Swift was architected to be interoperable with Objective-C so that developers, including Apple’s own developers, could start adding Swift code to their projects without refactoring. Bridging Headers allow you to include Objective-C with your Swift files.

To have a look at the category in question, open Grimm\Categories\UIImage+ImageEffects.m from the Project Explorer and skim past all of the legalese comments until you find applyBlurWithRadius:tintColor:saturationDeltaFactor:maskImage:. This tutorial won’t cover all of the ins and outs of this code, but it will help you to understand some of the basic functionality contained within.

Apple provided this UIImage category with the release of iOS 7 to demonstrate how to apply static blurs to images. It takes advantage of the Accelerate.framework to simplify vector and matrix math and is optimized for image processing.

applyBlurWithRadius:tintColor:saturationDeltaFactor:maskImage: takes a blur radius, saturation delta and an optional masking image and uses a lot of math to produce a new, manipulated image.

Obtaining a Snapshot Image

You’ll need a snapshot image before you can apply your blur effect. The focus of your efforts today (pardon the pun) will be the options drawer at the bottom of the StoryViewController view.

Open StoryViewController.swift and look for setOptionsHidden:. You’ll capture a screenshot of the entire StoryViewController view, blur it, and then set it as a subview of the options view.

Add the following function above setOptionsHidden::

func updateBlur() {
  // 1
  optionsContainerView.hidden = true  
  // 2
  UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, true, 1)
  // 3
  self.view.drawViewHierarchyInRect(self.view.bounds, afterScreenUpdates: true)
  // 4
  let screenshot = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()
}

Taking each numbered comment in turn:

  1. You don’t want a screenshot of the options if they are on the screen so make sure that they are hidden before you take the screenshot.
  2. In order to draw a screenshot you need to create a new image context. Since you’re going to blur the image anyway, you don’t need the full-resolution Retina image. This saves a ton of processing power and resources.
  3. Draw the story view into the image context. Since you have hidden the optionsContainerView, you will need to wait for screen updates before capturing a screenshot.
  4. Pull a UIImage from the image context and then clean it up.

You will need to actually call updateBlur when you tap on the ellipsis icon. Find the method setOptionsHidden: and add the following code to the top of the function:

if !hidden {
  updateBlur()
}

Before you go any further, you should check that you’re capturing the screenshot you think you’re capturing.

Add a breakpoint to the UIGraphicsEndImageContext() line you added to updateBlur in the previous step. Then build and run your project and tap on any fairy tale to select it.

Once that fairy tale is displayed, tap the ellipsis icon to trigger the breakpoint. In the variables section of the Debugging Editor, expand the screenshot variable and select the nested Some variable, as shown below:

debug1

Tap the Space key to open Quick Look; you should see a non-Retina screenshot of your story view, as you can see here:

debug2

Notice there aren’t any navigation elements from your UINavigationController; that’s because the story view is a subview of UINavigationController, therefore the navigation controls are outside of the screenshot area.

Now that you’re sure you are grabbing the correct screenshot, you can continue on and apply the blur using the UIImage category described earlier.

Blurring Your Snapshot

Still working in StoryViewController.swift, find the updateBlur function you added and add the following line directly below UIGraphicsEndImageContext():

let blur = screenshot.applyLightEffect()

Next, move your breakpoint to the line you just added, like so:

debug3

Note: You can move a breakpoint by dragging it up and down in the gutter.

Build and run. Tap any fairy tale, then tap the ellipsis icon in the navigation bar. Over in the Debugging Editor, find the blur variable and hit the space key to open Quick Look again.

Wait a second… there’s nothing there! What gives?

You don’t see anything because your breakpoint is right on the line that sets the variable and Xcode stopped executing just before it executed that line.

To execute the highlighted line of code, press F6 or click the Step over button as shown below:

debug4

Now expand the blur variable, select the nested Some variable, then press the Space key to open up Quick Look and see your new blurred image:

debug5

Note: LLDB (the debugging tool that Xcode uses) sometimes doesn’t play nicely with Swift. You may have to click the Step over button twice to get the Some variable to appear.

You’ve managed to take a screenshot and blur it – now it’s time to add the blurred image to the app.

Displaying the Blur in Your View

Open StoryViewController.swift and add the following property to the top of the file along with the other the property declarations:

var blurView = UIImageView()

This initializes a new UIImageView with every StoryViewController instance.

Find viewDidLoad and add the following code to the very end of the method:

optionsContainerView.subviews[0].insertSubview(blurView, 
	              atIndex:0)

Grimm.storyboard puts OptionsController inside a container view displayed each time the user taps the ellipsis icon. Since you don’t have direct access to the OptionsController view, you need to get the first subview of the container. In this case, that happens to be the view owned by OptionsController. Finally, you insert blurView as a subview to the very bottom of the view stack, beneath all other subviews.

Still inside StoryViewController.swift, go back to updateBlur and add the following code to the end of the function:

blurView.frame = optionsContainerView.bounds
blurView.image = blur
 
optionsContainerView.hidden = false

Since blurView isn’t setup in the Storyboard, it will have a frame of CGRectZero unless you set the frame manually. As well, you set the image property to the blurred image you’ve already created.
Also remember that you hid optionsContainerView before taking the screenshot. You need to make sure that you unhide the view before the method ends.

Disable the breakpoint you set earlier, then build and run. After selecting a fairy tale and displaying the options, behold the blur in all its glory, as shown below:

manualblur1

Hmm. That blur looks kind of… funky, doesn’t it? Why doesn’t it match up with the text behind it?

By default, UIImageView resizes the image to match the frame of the view. That means the huge blurred image is being squished into much smaller options view. That just won’t do!

To fix this, you need to set the UIImageView’s contentMode property to something other than the default UIViewContentMode.ScaleToFill.

Add the following line to updateBlur, just below where you set the blurred image on blurView:

blurView.contentMode = .Bottom

The UIViewContentMode.Bottom content mode forces the image to retain its size and instead fixes it to the bottom-center of the UIImageView.

Build and run. How does the blur-positioning look now?

manualblur2

There’s just one more thing you need to do before your static blur is ready for use. Try changing the orientation of your device or simulator (⌘-Left/Right Arrow). The view doesn’t resize!

Since you are using auto-layout with all of your text, the screenshot wont be valid anymore. You’ll need to take a new screenshot after the device rotates and update the blurView.

To do this, add the following function override inside StoryViewController.swift:

override func viewWillTransitionToSize(size: CGSize,
  withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
  // 1
  coordinator.animateAlongsideTransition(nil, completion: { context in
    // 2
    self.updateBlur()
  })
}
  1. The animateAlongsideTransition method gives you the ability to animate changes alongside the orientation change as well as do some cleanup after the rotation completes. You’re only using the completion block since you need the frame of the optionsViewController after the rotation change.
  2. Setup the blur again after the orientation animation completes. This will use the new text layout.

Build and run and try changing the orientation to see everything be resized with a new blur.

manualblur3

The blur is sized correctly, but it’s not terribly exciting. Scroll the text behind the blur and you’ll notice the blur doesn’t update.

You can definitely do better than that. iOS 8 gives you the tools to create dynamic, real-time blur effects in your apps – it’s a long way from the developer-led solutions of iOS 7!

Blur Effects in iOS 8

iOS 8 gives you an entire suite of visual effects goodies; UIBlurEffect, a subclass of UIVisualEffect, is particularly relevant to your interests. UIBlurEffect provides the nice blurs you see in navigation bars, Notification Center, and Control Center – and you can use it in your apps as well.

Adding a UIBlurEffect

Open StoryViewController.swift and find setOptionsHidden:. Comment out the call to updateBlur inside the if-statement you built up in the previous sections; afterwards it should look like the following screenshot:

newblur1

While you’re at it, you should ensure blurView isn’t added to the scene at all. In viewDidLoad, comment out the following line:

optionsContainerView.subviews[0].insertSubview(blurView, 
	              atIndex:0)
Note: Instead of deleting all the code you added previously, you’re simply commenting it out so you can go back and see the differences. If you’re not interested in preserving your manual blur code, you can delete the code instead of commenting it out.

Build and run. Check that everything compiles and that your static blur is gone.

Open Grimm.storyboard and find the Options Controller Scene. Select the view, open the Attributes Inspector and change the view’s Background to Clear Color, as shown below:

newblur2

Open OptionsController.swift and add the following code to viewDidLoad, just after the line where you add optionsView to the view:

// 1
let blurEffect = UIBlurEffect(style: .Light)
// 2
let blurView = UIVisualEffectView(effect: blurEffect)
// 3
blurView.setTranslatesAutoresizingMaskIntoConstraints(false)
view.insertSubview(blurView, atIndex: 0)

Taking each numbered comment in turn:

  1. Create a UIBlurEffect with a UIBlurEffectStyle.Light style. This defines which effect to use. The other available styles are UIBlurEffectStyle.ExtraLight and UIBlurEffectStyle.Dark.
  2. Create a UIVisualEffectView and tell it which effect to use. This class is a subclass of UIView; its sole purpose is to define and display complex visual effects.
  3. Disable translating the auto-resizing masks into constraints on the blurView, as you’ll manually add constraints in just a moment, and add it at the bottom of view stack. If you just added blurView on top of the view, it would end up blurring all of the controls underneath it instead!

Now you need to ensure your blurView lays out properly with the rest of the view.

Still working in viewDidLoad, add the following code just before the line where you call addConstraints::

constraints.append(NSLayoutConstraint(item: blurView,
  attribute: .Height, relatedBy: .Equal, toItem: view,
  attribute: .Height, multiplier: 1, constant: 0))
constraints.append(NSLayoutConstraint(item: blurView,
  attribute: .Width, relatedBy: .Equal, toItem: view,
  attribute: .Width, multiplier: 1, constant: 0))

These constraints keep the frame of the blurView consistent with that of the OptionsController view.

Build and run. Select a fairy tale, and then scroll the text. Behold as the blur updates in real-time.

newblur3

You now have a dynamic blur effect in your app that not only looks great – you’re also using core iOS functionality to make it happen.

Adding Vibrancy to your Blur

Blur effects are great – but as usual, Apple has taken it to the next level with the UIVibrancyEffect, which when used in combination with UIVisualEffectView adjusts the colors of the content to make it feel more vivid.

The following image demonstrates how vibrancy makes your labels and icons pop off the screen, while at the same time blending with the background itself:

vibrancy

The left side of the image shows a normal label and button, while the right side shows a label and button with vibrancy applied.

Note: UIVibrancyEffect must be added to a UIVisualEffectView that has been setup and configured with a UIBlurEffect object; otherwise, there won’t be any blurs to apply a vibrancy effect!

Open OptionsController.swift and add the following code to viewDidLoad, just before where the Auto Layout constraints are added:

// 1
let vibrancyEffect = UIVibrancyEffect(forBlurEffect: blurEffect)
// 2
let vibrancyView = UIVisualEffectView(effect: vibrancyEffect)
vibrancyView.setTranslatesAutoresizingMaskIntoConstraints(false)
// 3
vibrancyView.contentView.addSubview(optionsView)
// 4
blurView.contentView.addSubview(vibrancyView)

Taking each numbered comment in turn:

  1. Create a UIVibrancyEffect that uses the blurEffect you set up earlier. UIVibrancyEffect is another subclass of UIVisualEffect.
  2. Create a UIVisualEffectView to contain the vibrancy effect. This process is exactly the same as creating a blur. Since you’re using Auto Layout, you make sure to disable auto-resizing translations here.
  3. Add the optionsView to your vibrancy view’s contentView property; this ensures the vibrancy effect will be applied to the view that contains all of the controls.
  4. Finally you add the vibrancy view to the blur view’s contentView to complete the effect.

The final thing to do is set up the Auto Layout constraints for the vibrancy view so that it uses the same height and width of your controller’s view.

Add the following constraints below the others, at the end of viewDidLoad:

constraints.append(NSLayoutConstraint(item: vibrancyView,
  attribute: .Height, relatedBy: .Equal,
  toItem: view, attribute: .Height,
  multiplier: 1, constant: 0))
constraints.append(NSLayoutConstraint(item: vibrancyView,
  attribute: .Width, relatedBy: .Equal,
  toItem: view, attribute: .Width,
  multiplier: 1, constant: 0))

Build and run. Bring up the options view to see your new vibrancy effect in action:

effect1

Unless you have high-contrast vision, the vibrancy effect makes it really difficult to read the labels and controls. What’s going on?

Ah – the content behind the blur view is white and you’re applying a UIBlurEffectStyle.Light effect. That’s counterproductive, to be sure.

Modify the line near the top of viewDidLoad that initializes the blurEffect:

let blurEffect = UIBlurEffect(style: .Dark)

This changes the blur effect to add more contrast between the background and text.

Build and run. You’re now experiencing some true vibrancy:

effect2

Where To Go From Here?

You can download the finished project here.

You’ve seen how to blur images manually, as well as how to create real-time blur effects. You can just as easily add UIVisualEffectViews to your own apps.

Your manual blur technique relied on static images, so you couldn’t animate the image or efficiently update the blur in real-time. UIBlurEffect, however, does update in real-time, so you can achieve all sorts of weird and wonderful things with these effects, such as animations etc.

Whilst you might be tempted to go ahead and blur all the things, keep in mind what was talked about earlier in the tutorial regarding using these effects sparingly and only where appropriate. As is often the case, with blur and vibrancy, less is definitely more.

If you want to learn more about new iOS 8 APIs like this, check out our book iOS 8 by Tutorials, where we have 30 chapters and over 750 pages of tutorials on new iOS APIs ready for you!

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

iOS 8 Visual Effects Tutorial is a post from: Ray Wenderlich

The post iOS 8 Visual Effects Tutorial appeared first on Ray Wenderlich.

3 New Sponsors for RWDevCon!

$
0
0
Check out the first round of sponsors for RWDevCon!

Check out the first round of sponsors for RWDevCon!

In case you haven’t heard already, we are teaming up with the folks from 360iDev to host an iOS developer conference next February: RWDevCon!

RWDevCon is the first ever tutorial conference. That means instead of just passively watching talks, you get to participate – via hands-on tutorials!

You’ll play around with iOS 8, Swift, and app development in general, along with the authors of the iOS Apprentice, iOS 8 by Tutorials, iOS Games by Tutorials, and more.

And best of all – you won’t just have a hodge-podge of random talks. Instead, RWDevCon will be coordinated as a team so you have an amazing experience.

I’ll be giving you periodic updates of how things are proceeding with the conference. This week, I’m pleased to announce three new sponsors for the conference!

3 New Sponsors!

Today, we are happy to announce 3 new sponsors for the conference:

rwdevcon_sponsors1

  • Facebook (gold sponsor): Facebook is doing some amazing things lately in the mobile world. In addition to creating some well-designed apps of their own, Facebook has been providing some powerful libraries and frameworks to the community like Facebook Pop, Tweaks, Shimmer, Origami, Parse, and more.
  • ShinobiControls (silver sponsor): ShinobiControls is a suite of high performance (and polished) data components and UI controls for iOS and Android. If you need a sweet chart or table for your app, look no further!
  • Count.ly (bronze sponsor): Count.ly makes it easy to integrate analytics into your apps, with real-time reporting and a sweet web-based dashboard. This is a great way to see what your users are doing and improve your app accordingly.

Huge thanks to Facebook, ShinobiControls, and Count.ly for being a part of RWDevCon!

Get Your Ticket!

Save yourself $100 by signing up now!

This early-bird discount will only last for a short time, so be sure to grab it while you still can.

The team and I look forward to meeting you at RWDevCon for some tutorials, inspiration, and fun!

3 New Sponsors for RWDevCon! is a post from: Ray Wenderlich

The post 3 New Sponsors for RWDevCon! appeared first on Ray Wenderlich.


Video Tutorial: iOS Animation Part 5: Keyframe Animations

Text Kit Tutorial in Swift

$
0
0
Achieve next-level typography with Text Kit!

Achieve next-level typography with Text Kit!

Note from Ray: This is a Swift update to a popular Objective-C tutorial on our site, released as part of the iOS 8 Feast. Update by Gabriel Hauber, Original post by Tutorial Team member Colin Eberhardt. Enjoy!

The way that iOS renders text continues to grow more powerful over the years as Apple adds more features and capabilities. The release of iOS 7 brought with it some of the most significant text rendering changes yet. Now iOS 8 builds on that power, and makes it easier to use. A brief overview of iOS text editing might help you keep things in perspective.

In the old days before iOS 6, web views were usually the easiest way to render text with mixed styling, such as bold, italics, or even colors.

In 2012, iOS 6 added attributed string support to a number of UIKit controls. This made it much easier to achieve this type of layout without resorting to rendered HTML.

In iOS 6, UIKit controls based their text capabilities on both WebKit and Core Graphics’ string drawing functions, as illustrated in the hierarchical diagram below:

TextRenderingArchitecture-iOS6

Note: Does anything strike you as odd in this diagram? That’s right — UITextView uses WebKit under the hood. iOS 6 renders attributed strings on a text views as HTML, a fact that’s not readily apparent to developers who haven’t dug deeply into the framework.

Attributed strings in iOS 6 were indeed helpful for many use cases. However, for advanced layouts and multi-line rendered text, Core Text remained the only real option — a relatively low-level and cumbersome framework.

However, since iOS 7 there’s an easier way. With the current minimalistic design focus that eschews ornamentation and focuses more on typography — such as the UIButton that strips away all borders and shadows, leaving only text — it’s no surprise that iOS 7 added a whole new framework for working with text and text attributes: Text Kit.

The architecture is now much tidier; all of the text-based UIKit controls (apart from UIWebView) now use Text Kit, as shown in the following diagram:

TextRenderingArchitecture-iOS7

Text Kit is built on top of Core Text, inherits the full power of the Core Text framework, and to the delight of developers everywhere, wraps it in an improved object-oriented API.

In this Text Kit tutorial you’ll explore the various features of Text Kit as you create a simple yet feature-rich note-taking app for the iPhone that features reflowing text, dynamic text resizing, and on-the-fly text styling.

Ready to create something of note? :] Then read on to get started with Text Kit!

Note: At the time of writing this tutorial, our understanding is we cannot post screenshots of iOS 8 since it is still in beta. All the screenshots here are from iOS 7, which should look very close to what things will look like in iOS 8.

Getting Started

This tutorial includes a starter project with the user interface for the app pre-created so you can stay focused on Text Kit. Download the starter project archive to get started. Unzip the file, open the project in Xcode and build and run the app. It will look like the following:

Starter Image

The app creates an initial array of Note instances and renders them in a table view controller. Storyboards and segues detect cell selection in the table view and handle the transition to the view controller where users can edit the selected note.

Note: If you are new to Storyboards, check out Storyboards Tutorial in iOS 7. And if you’re subscribing to the video tutorials check out Video Tutorial Storyboards and Segues

Browse through the source code and play with the app a little to get a feel for the apps’ structure and how it functions. When you’re done with that, move on to the next section, which discusses the use of dynamic type in your app.

Dynamic Type

Dynamic type is one of the most game-changing features of iOS 7; it places the onus on your app to conform to user-selected font sizes and weights.

In iOS 7, open the Settings app and navigate to General/Accessibility and General/Text Size to view the settings that affect how the app displays text:

UserTextPreferences

In iOS 8, open the Settings app and navigate to General/Accessibility/Larger Text to access Dynamic Type text sizes.

iOS 7 offered the ability to enhance the legibility of text by increasing font weight, as well as an option to set the preferred font size for apps that support dynamic text.

Note: When Apple released Text Kit at WWDC 2013, they strongly encouraged developers to adopt Dynamic Type. At WWDC 2014 Apple went a bit further. They stressed that all built-in apps support Dynamic Type. Moreover, they made Dynamic Type much easier to use in iOS 8. While they didn’t threaten to break anyone’s legs if they didn’t get with the program, Apple did make their point forcefully. The WWDC 2014 session 226 What’s New in Tables and Collection Views, covers iOS 8 Dynamic Type support for table views and collections. You’d be well-advised to watch it! Users will expect apps written for iOS 7 and later to honor these settings, so ignore them at your own risk!

In order to make use of dynamic type you need to specify fonts using styles rather than explicitly stating the font name and size. iOS 7 added a new method to UIFont, preferredFontForTextStyle, that creates a font for the given style using the user’s font preferences.

The diagram below gives an example of each of the six different font styles:

TextStyles

The text on the left uses the smallest user selectable text size, the text in the center uses the largest, and the text on the right shows the effect of enabling the accessibility bold text feature.

Basic Support

Implementing basic support for dynamic text is relatively straightforward. Rather than using explicit fonts within your application, you instead request a font for a specific style. At runtime the app selects a suitable font based on the given style and the user’s text preferences.

With iOS 8, Apple made it far easier to implement Dynamic Type than was the case in iOS 7. In particular, default labels in table views support Dynamic Type automatically! Nonetheless, you may well want to support iOS 7, and/or you might want to use custom labels in your table views. So first you’ll learn how to handle Dynamic Type for iOS 7. Then you’ll discover how Apple makes your life even easier in iOS 8.

Why iOS 7 is Great, but iOS 8 is Even Greater

The starter project comes set with its deployment set to iOS 8. Before proceeding, build and run the app and try changing the default text size to various values. You will discover that both the text size and cell height in the table view list of notes changes accordingly. And you didn’t have to do a thing! But do observe also that the notes themselves do not reflect changes to the text size settings.

While not quite as wonderful, things are still pretty great in iOS 7. For most of this tutorial, it won’t matter if you’re using iOS 7 or iOS 8 (just be sure you’re using Xcode 6!), but for now, set the deployment level for the app to iOS 7 and follow along in the iOS simulator. And most of what follows is of use in iOS 8 too, so it’s worthwhile even if you do not plan to support iOS versions earlier than iOS 8.

Note: To set the deployment level to iOS 7 in Xcode 6, choose View/Navigators/Show Project Navigator. In the right hand panel, choose the project, click on info and select iOS 7 in the iOS Deployment Target popup menu. Also, select the Target in the right-hand panel and the set the Deployment Target to iOS 7.

It’s also important to make sure the simulator is acting as an iOS 7 device. So in the iOS Simulator choose Hardware/Device/iOS 7/iPhone 5s.

Now that you’re all set to run as an iOS 7 app, go ahead and build and run. Play with the text size settings as before and you’ll see that sadly enough, the app is ignoring your settings. Now, you will do something to make it work in iOS 7.

Open NoteEditorViewController.swift and add the following to the end of viewDidLoad:

textView.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)

Notice you’re not specifying an exact font such as Helvetica Neue. Instead, you’re asking for an appropriate font for body text with the UIFontTextStyleBody text style constant.

Next, open NotesListViewController.swift and add the following to the tableView(_:cellForRowAtIndexPath:) method, just before the return call:

cell.textLabel?.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)

Again, you’re specifying a text style and iOS will return an appropriate font.

Using a semantic approach to font names, such as UIFontTextStyleSubHeadline, helps avoid hard-coded font names and styles throughout your code — and ensures that your app will respond properly to user-defined typography settings as expected.

Build and run the app again, and you’ll notice that the table view and the note screen now honor the current text size; the difference between the two is shown in the screenshots below:

That looks pretty good — but sharp readers will note that this is only half the solution. Head back to the Settings app under General/Text Size and modify the text size again. This time, switch back to SwiftTextKitNotepad — without re-launching the app — and you’ll notice that your app didn’t respond to the new text size.

Your users won’t take too kindly to that! Looks like that’s the first thing you need to correct in this app.

Responding to Updates

Open NoteEditorViewController.swift and add the following code to the end of viewDidLoad

NSNotificationCenter.defaultCenter().addObserver(self, 
    selector: "preferredContentSizeChanged:", 
    name: UIContentSizeCategoryDidChangeNotification,
    object: nil)

The above code registers the class to receive notifications when the preferred content size changes and passes in the method to be called (preferredContentSizeChanged) when this event occurs.

Next, add the following method to the class:

func preferredContentSizeChanged(notification: NSNotification) {
  textView.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
}

This simply sets the text view font to one based on the new preferred size.

Note: You might be wondering why it seems you’re setting the font to the same value it had before. When the user changes their preferred font size, you must request the preferred font again; it won’t update automatically. The font returned via preferredFontForTextStyle will be different when the font preferences are changed.

Open up NotesListViewController.swift and override the viewDidLoad function by adding the following code to the class:

override func viewDidLoad() {
  super.viewDidLoad()
  NSNotificationCenter.defaultCenter().addObserver(self,
      selector: "preferredContentSizeChanged:", 
      name: UIContentSizeCategoryDidChangeNotification, 
      object: nil)
}

Hey, isn’t that the same code you just added to NoteEditorViewController.swift? Yes, it is — but you’ll handle the preferred font change in a slightly different manner.

Add the following method to the class:

func preferredContentSizeChanged(notification: NSNotification) {
  tableView.reloadData()
}

The above code simply instructs the table view to reload its visible cells, which updates the appearance of each cell. This will trigger the calls to preferredFontForTextStyle() and refresh the font choice.

Build and run your app; change the text size setting, and verify that your app responds correctly to the new user preferences.

Changing Layout

That part seems to work well, but when you select a really small font size, your table view ends up looking a little sparse, as shown in the right-hand screenshot below:

ChangingLayout

This is one of the trickier aspects of dynamic type (in iOS 7). To ensure your application looks good across the range of font sizes, your layout needs to be responsive to the user’s text settings. Auto Layout solves a lot of problems for you, but this is one problem you’ll have to solve yourself.

Your table row height needs to change as the font size changes. Implementing the tableView(_:heightForRowAtIndexPath:) delegate method solves this quite nicely.

Add the following code to NotesListViewController.swift, in the section labelled Table view data source:

let label: UILabel = {
  let temporaryLabel = UILabel(frame: CGRect(x: 0, y: 0, width: Int.max, height: Int.max))
  temporaryLabel.text = "test"
  return temporaryLabel
}()
 
override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
  label.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
  label.sizeToFit()
  return label.frame.height * 1.7
}

The above code creates a single shared instance of UILabel which the table view uses to calculate the height of the cell. Then, in tableView(_:heightForRowAtIndexPath:) you set the label’s font to be the same font used by the table view cell. It then invokes sizeToFit on the label, which forces the label’s frame to fit tightly around the text, and results in a frame height proportional to the table row height.

Build and run your app; modify the text size setting once more and the table rows now size dynamically to fit the text size, as shown in the screenshot below:

If you like, you may now reset the deployment to iOS 8 for the rest of the tutorial.

Letterpress Effect

The letterpress effect adds subtle shading and highlights to text that give it a sense of depth — much like the text has been slightly pressed into the screen.

Note: The term “letterpress” is a nod to early printing presses, which inked a set of letters carved on blocks and pressed them into the page. The letters often left a small indentation on the page — an unintended but visually pleasing effect, which is frequently replicated in digital typography today.

Open NotesListViewController.swift and replace tableView(_:cellForRowAtIndexPath:) with the following implementation:

override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? {
  let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
 
  let note = notes[indexPath.row]
  let font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
  let textColor = UIColor(red: 0.175, green: 0.458, blue: 0.831, alpha: 1)
  let attributes = [
    NSForegroundColorAttributeName : textColor,
    NSFontAttributeName : font,
    NSTextEffectAttributeName : NSTextEffectLetterpressStyle
  ]
  let attributedString = NSAttributedString(string: note.title, attributes: attributes)
 
  cell.textLabel?.attributedText = attributedString
 
  return cell
}

The above code creates an attributed string for the title of a table cell using the letterpress style.

Build and run your app; your table view will now display the text with a nice letterpress effect, as shown below:

Letterpress is a subtle effect — but that doesn’t mean you should overuse it! Visual effects may make your text more interesting, but they don’t necessarily make your text more legible.

Exclusion Paths

Flowing text around images or other objects is a standard feature of most word processors. Text Kit allows you to render text around complex paths and shapes with exclusion paths.

It would be handy to tell the user the note’s creation date; you’re going to add a small curved view to the top right-hand corner of the note that shows this information.

You’ll start by adding the view itself – then you’ll create an exclusion path to make the text wrap around it.

Adding the View

Open up NoteEditorViewController.swift and add the following property declaration to the class:

var timeView: TimeIndicatorView!

As the name suggests, this houses the time indicator subview.

Next, add this code to the very end of viewDidLoad:

timeView = TimeIndicatorView(date: note.timestamp)
textView.addSubview(timeView)

This simply creates an instance of the new view and adds it as a subview.

TimeIndicatorView calculates its own size, but it won’t do this automatically. You need a mechanism to call updateSize when the view controller lays out the subviews.

Finally, add the following two methods to the class:

override func viewDidLayoutSubviews() {
  updateTimeIndicatorFrame()
}
 
func updateTimeIndicatorFrame() {
  timeView.updateSize()
  timeView.frame = CGRectOffset(timeView.frame, textView.frame.width - timeView.frame.width, 0)
}

viewDidLayoutSubviews calls updateTimeIndicatorFrame, which does two things: it calls updateSize to set the size of the subview, and positions the subview in the top right corner of the text view.

All that’s left is to call updateTimeIndicatorFrame when your view controller receives a notification that the size of the content has changed. Replace the implementation of preferredContentSizeChanged to the following:

func preferredContentSizeChanged(notification: NSNotification) {
  textView.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
  updateTimeIndicatorFrame()
}

Build and run your project; tap on a list item and the time indicator view will display in the top right hand corner of the item view, as shown below:

Modify the device Text Size preferences, and the view will automatically adjust to fit.

However, something doesn’t look quite right. The text of the note renders behind the time indicator view instead of flowing neatly around it. Fortunately, this is the exact problem that exclusion paths are designed to solve.

Exclusion Paths

Open TimeIndicatorView.swift and take a look at curvePathWithOrigin(). The time indicator view uses this code when filling its background, but you can also use it to determine the path around which you’ll flow your text. Aha — that’s why the calculation of the Bezier curve is broken out into its own method!

All that’s left is to define the exclusion path itself. Open up NoteEditorViewController.swift and add the following code block to the very end of updateTimeIndicatorFrame:

let exclusionPath = timeView.curvePathWithOrigin(timeView.center)
textView.textContainer.exclusionPaths = [exclusionPath]

The above code creates an exclusion path based on the Bezier path created in your time indicator view, but with an origin and coordinates that are relative to the text view.

Build and run your project and select an item from the list; the text now flows nicely around the time indicator view, as shown in the following screenshot:

ExclusionPath

This simple example only scratches the surface of the abilities of exclusion paths. You might notice that the exclusionPaths property expects an array of paths, meaning each container can support more than one exclusion path.

Furthermore, exclusion paths can be as simple or as complicated as you want. Need to render text in the shape of a star or a butterfly? As long as you can define the path, exclusionPaths will handle it without problem!

As the text container notifies the layout manager when an exclusion path changes, you can implement dynamic or even animated exclusions paths — just don’t expect your user to appreciate the text moving around on the screen as they’re trying to read!

Dynamic Text Formatting and Storage

You’ve seen that Text Kit can dynamically adjust fonts based on the user’s text size preferences. But wouldn’t it be cool if fonts could update dynamically based on the actual text itself?

For example, what if you want to make this app automatically:

  • Make any text surrounded by the tilde character (~) a fancy font
  • Make any text surrounded by the underscore character (_) italic
  • Make any text surrounded by the dash character (-) crossed out
  • Make any text in all caps colored red

That’s exactly what you’ll do in this section by leveraging the power of the Text Kit framework!

To do this, you’ll need to understand how the text storage system in Text Kit works. Here’s a diagram that shows the “Text Kit stack” used to store, render and display text:

TextKitStack

Behind the scenes, Apple creates these classes for you automatically when you create a UITextView, UILabel or UITextField. In your apps, you can either use these default implementations or customize any part to get your own behavior. Let’s go over each class:

  • NSTextStorage stores the text it is to render as an attributed string, and informs the layout manager of any changes to the text’s contents. You might want to subclass NSTextStorage in order to dynamically change the text attributes as the text updates (as you will see later in this tutorial).
  • NSLayoutManager takes the stored text and renders it on the screen; it serves as the layout ‘engine’ in your app.
  • NSTextContainer describes the geometry of an area of the screen where the app renders text. Each text container is typically associated with a UITextView. You might want to subclass NSTextContainer to define a complex shape that you would like to render text within.

To implement the dynamic text formatting feature in this app, you’ll need to subclass NSTextStorage in order to dynamically add text attributes as the user types in their text.

Once you’ve created your custom NSTextStorage, you’ll replace UITextView’s default text storage instance with your own implementation. Let’s give this a shot!

Subclassing NSTextStorage

Right-click on the SwiftTextKitNotepad group in the project navigator, select New File…, and choose iOS/Source/Cocoa Touch Class and click Next.

Name the class SyntaxHighlightTextStorage, make it a subclass of NSTextStorage, and confirm that the Language is set to Swift. Click Next, then Create.

Open SyntaxHighlightTextStorage.swift and add a new property inside the class declaration:

let backingStore = NSMutableAttributedString()

A text storage subclass must provide its own persistence hence the use of a NSMutableAttributedString backing store – more on this later.

Next add the following to the class:

override var string: String {
  return backingStore.string
}
 
override func attributesAtIndex(index: Int, effectiveRange range: NSRangePointer) -> [NSObject : AnyObject] {
  return backingStore.attributesAtIndex(index, effectiveRange: range)
}

The first of these two declarations overrides the string computed property, deferring to the backing store. Likewise the attributesAtIndex method also delegates to the backing store.

Finally add the remaining mandatory overrides to the same file:

override func replaceCharactersInRange(range: NSRange, withString str: String) {
  println("replaceCharactersInRange:\(range) withString:\(str)")
 
  beginEditing()
  backingStore.replaceCharactersInRange(range, withString:str)
  edited(.EditedCharacters | .EditedAttributes, range: range, changeInLength: (str as NSString).length - range.length)
  endEditing()
}
 
override func setAttributes(attrs: [NSObject : AnyObject]!, range: NSRange) {
  println("setAttributes:\(attrs) range:\(range)")
 
  beginEditing()
  backingStore.setAttributes(attrs, range: range)
  edited(.EditedAttributes, range: range, changeInLength: 0)
  endEditing()
}

Again, these methods delegate to the backing store. However, they also surround the edits with calls to beginEditing, edited and endEditing. The text storage class requires these three methods in order to notify its associated layout manager when making edits.

You’ve probably noticed that you need to write quite a bit of code in order to subclass text storage. Since NSTextStorage is a public interface of a class cluster, you can’t just subclass it and override a few methods to extend its functionality. Instead, there are certain requirements that you must implement yourself, such as the backing store for the attributed string data.

Note: Class clusters are a commonly used design pattern throughout Apple’s frameworks.

A class cluster is simply the Objective-C implementation of the Abstract Factory pattern, which provides a common interface for creating families of related or dependent objects without specifying the concrete classes. Familiar classes such as NSArray and NSNumber are in fact the public interface to a cluster of classes.

Apple uses class clusters to encapsulate private concrete subclasses under a public abstract superclass, and it’s this abstract superclass that declares the methods a client must use in order to create instances of its private subclasses. Clients are also completely unaware of which private class is being dispensed by the factory, since it only ever interacts with the public interface.

Using a class cluster certainly simplifies the interface, making it much easier to learn and use the class, but it’s important to note there’s been a trade-off between extensibility and simplicity. It’s often far more difficult to create a custom subclass of the abstract superclass of a cluster.

Now that you have a custom NSTextStorage, you need to make a UITextView that uses it.

A UITextView with a Custom Text Kit Stack

Instantiating UITextView from the storyboard editor automatically creates an instance of NSTextStorage, NSLayoutManager and NSTextContainer (i.e. the Text Kit stack) and exposes all three instances as read-only properties.

There is no way to change these from the storyboard editor, but luckily you can if you create the UITextView and Text Kit stack programatically.

Let’s give this a shot. Open up Main.storyboard, and locate the NoteEditorViewController view by expanding Detail Scene/Detail/View and select Text View. Delete this UITextView instance.

Next, open NoteEditorViewController.swift and remove the UITextView outlet from the class and replace it with the following property declarations:

var textView: UITextView!
var textStorage: SyntaxHighlightTextStorage!

These two properties are for your text view and the custom storage subclass.

Next remove the following lines from viewDidLoad:

textView.text = note.contents
textView.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)

Since you are no longer using the outlet for the text view and will be creating one manually instead, you no longer need these lines.

Still working in NoteEditorViewController.swift, add the following method to the class:

func createTextView() {
  // 1. Create the text storage that backs the editor
  let attrs = [NSFontAttributeName : UIFont.preferredFontForTextStyle(UIFontTextStyleBody)]
  let attrString = NSAttributedString(string: note.contents, attributes: attrs)
  textStorage = SyntaxHighlightTextStorage()
  textStorage.appendAttributedString(attrString)
 
  let newTextViewRect = view.bounds
 
  // 2. Create the layout manager
  let layoutManager = NSLayoutManager()
 
  // 3. Create a text container
  let containerSize = CGSize(width: newTextViewRect.width, height: CGFloat.max)
  let container = NSTextContainer(size: containerSize)
  container.widthTracksTextView = true
  layoutManager.addTextContainer(container)
  textStorage.addLayoutManager(layoutManager)
 
  // 4. Create a UITextView
  textView = UITextView(frame: newTextViewRect, textContainer: container)
  textView.delegate = self
  view.addSubview(textView)
}

This is quite a lot of code. Let’s consider each step in turn:

  1. Instantiate an instance of your custom text storage, and initialize it with an attributed string holding the content of the note.
  2. Create a layout manager.
  3. Create a text container and associate it with the layout manager. Then, associate the layout manager with the text storage.
  4. Create the actual text view with your custom text container, set the delegate, and add the text view as a subview.

At this point the earlier diagram, and the relationship it shows between the four key classes (storage, layout manager, container and text view) should make more sense:

TextKitStack

Note that the text container has a width matching the view width, but has infinite height — or as close as CGFLOAT_MAX can come to infinity. In any case, this is more than enough to allow the UITextView to scroll and accommodate long passages of text.

Now still working in NoteEditorViewController.swift add the line below directly after the super.viewDidLoad() line in viewDidLoad:

createTextView()

One last thing: a custom view created in code doesn’t automatically inherit the layout constraints set in the storyboard; therefore, the frame of your new view won’t resize when the device orientation changes. You’ll need to explicitly set the frame yourself.

To do this, add the following line to the end of viewDidLayoutSubviews:

textView.frame = view.bounds

Build and run your app; open a note and edit the text while keeping an eye on the Xcode console. You should see a flurry of log messages created as you type, as below:

LogMessages

This is simply the logging code from within SyntaxHighlightTextStorage to give you an indication that your custom text handling code is actually being called.

The basic foundation of your text parser seems fairly solid — now to add the dynamic formatting!

Dynamic Formatting

In this next step you are going to modify your custom text storage to embolden text *surrounded by asterisks*.

Open SyntaxHighlightTextStorage.swift and add the following method:

func applyStylesToRange(searchRange: NSRange) {
  // 1. create some fonts
  let fontDescriptor = UIFontDescriptor.preferredFontDescriptorWithTextStyle(UIFontTextStyleBody)
  let boldFontDescriptor = fontDescriptor.fontDescriptorWithSymbolicTraits(.TraitBold)
  let boldFont = UIFont(descriptor: boldFontDescriptor, size: 0)
  let normalFont = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
 
  // 2. match items surrounded by asterisks
  let regexStr = "(\\*\\w+(\\s\\w+)*\\*)"
  let regex = NSRegularExpression(pattern: regexStr, options: nil, error: nil)
  let boldAttributes = [NSFontAttributeName : boldFont]
  let normalAttributes = [NSFontAttributeName : normalFont]
 
  // 3. iterate over each match, making the text bold
  regex.enumerateMatchesInString(backingStore.string, options: nil, range: searchRange) {
    match, flags, stop in
    let matchRange = match.rangeAtIndex(1)
    self.addAttributes(boldAttributes, range: matchRange)
 
    // 4. reset the style to the original
    let maxRange = matchRange.location + matchRange.length
    if maxRange + 1 < self.length {
      self.addAttributes(normalAttributes, range: NSMakeRange(maxRange, 1))
    }
  }
}

The above code performs the following actions:

  1. Create a bold and a normal font for formatting the text using font descriptors. Font descriptors help you avoid the use of hardcoded font strings to set font types and styles.
  2. Create a regular expression (or regex) that locates any text surrounded by asterisks; for example, in the string “iOS 8 is *awesome* isn’t it?”, the regular expression stored in regexStr above will match and return the text “*awesome*”. Don’t worry if you’re not totally familiar with regular expressions; they’re covered in a bit more detail later.
  3. Enumerate the matches returned by the regular expression and apply the bold attribute to each one.
  4. Reset the text style of the character that follows the final asterisk in the matched string to “normal”. This ensures that any text added after the closing asterisk is not rendered in bold type.
Note: Font descriptors are a type of descriptor language that allows you to modify fonts by applying specific attributes, or to obtain details of font metrics, without the need to instantiate an instance of UIFont.

Add the following method right after the code above:

func performReplacementsForRange(changedRange: NSRange) {
  var extendedRange = NSUnionRange(changedRange, NSString(string: backingStore.string).lineRangeForRange(NSMakeRange(changedRange.location, 0)))
  extendedRange = NSUnionRange(changedRange, NSString(string: backingStore.string).lineRangeForRange(NSMakeRange(NSMaxRange(changedRange), 0)))
  applyStylesToRange(extendedRange)
}

The code above expands the range that your code inspects when attempting to match your bold formatting pattern. This is required because changedRange typically indicates a single character; lineRangeForRange extends that range to the entire line of text.

Finally, add the following method right after the code above:

override func processEditing() {
  performReplacementsForRange(self.editedRange)
  super.processEditing()
}

processEditing sends notifications for when the text changes to the layout manager. It also serves as a convenient home for any post-editing logic.

Build and run your app; type some text into a note and surround some of the text with asterisks. The text will be automagically bolded, as shown in the screenshot below:

That’s pretty handy — you’re likely thinking of all the other styles that you might add to your text.

You’re in luck; the next section shows you how to do just that!

Adding Further Styles

The basic principle of applying styles to delimited text is rather straightforward: use a regex to find and replace the delimited string using applyStylesToRange to set the desired style of the text.

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

func createAttributesForFontStyle(style: String, withTrait trait: UIFontDescriptorSymbolicTraits) -> [NSObject : AnyObject] {
  let fontDescriptor = UIFontDescriptor.preferredFontDescriptorWithTextStyle(UIFontTextStyleBody)
  let descriptorWithTrait = fontDescriptor.fontDescriptorWithSymbolicTraits(trait)
  let font = UIFont(descriptor: descriptorWithTrait, size: 0)
  return [NSFontAttributeName : font]
}

This method applies the supplied font style to the body font. It provides a zero size to the UIFont(descriptor:size:) constructor which forces UIFont to return a size that matches the user’s current font size preferences.

Next, add the following property and function to the class:

var replacements: [String : [NSObject : AnyObject]]!
 
func createHighlightPatterns() {
  let scriptFontDescriptor = UIFontDescriptor(fontAttributes: [UIFontDescriptorFamilyAttribute : "Zapfino"])
 
  // 1. base our script font on the preferred body font size
  let bodyFontDescriptor = UIFontDescriptor.preferredFontDescriptorWithTextStyle(UIFontTextStyleBody)
  let bodyFontSize = bodyFontDescriptor.fontAttributes()[UIFontDescriptorSizeAttribute] as NSNumber
  let scriptFont = UIFont(descriptor: scriptFontDescriptor, size: CGFloat(bodyFontSize.floatValue))
 
  // 2. create the attributes
  let boldAttributes = createAttributesForFontStyle(UIFontTextStyleBody, withTrait:.TraitBold)
  let italicAttributes = createAttributesForFontStyle(UIFontTextStyleBody, withTrait:.TraitItalic)
  let strikeThroughAttributes = [NSStrikethroughStyleAttributeName : 1]
  let scriptAttributes = [NSFontAttributeName : scriptFont]
  let redTextAttributes = [NSForegroundColorAttributeName : UIColor.redColor()]
 
  // construct a dictionary of replacements based on regexes
  replacements = [
    "(\\*\\w+(\\s\\w+)*\\*)" : boldAttributes,
    "(_\\w+(\\s\\w+)*_)" : italicAttributes,
    "([0-9]+\\.)\\s" : boldAttributes,
    "(-\\w+(\\s\\w+)*-)" : strikeThroughAttributes,
    "(~\\w+(\\s\\w+)*~)" : scriptAttributes,
    "\\s([A-Z]{2,})\\s" : redTextAttributes
  ]
}

Here’s what’s going on in this method:

  • First, create a “script” style using Zapfino as the font. Font descriptors help determine the current preferred body font size, which ensures the script font also honors the users’ preferred text size setting.
  • Next, construct the attributes to apply to each matched style pattern. You’ll cover createAttributesForFontStyle(withTrait:) in a moment; just park it for now.
  • Finally, create a dictionary that maps regular expressions to the attributes declared above.

If you’re not terribly familiar with regular expressions, the dictionary above might look a bit strange. But if you deconstruct the regular expressions that it contains, piece by piece, you can decode them without much effort.

Take the first regular expression you implemented above that matches words surrounded by asterisks:

(\\*\\w+(\\s\\w+)*\\*)

The double slashes are a result of having to escape special characters in literal strings with an extra backslash. If you cast out the escaping backslashes, and consider just the core regular expression, it looks like this:

(\*\w+(\s\w+)*\*)

Now, deconstruct the regular expression step by step:

  1. (\* – match an asterisk
  2. \w+ – followed by one or more “word” characters
  3. (\s\w+)* – followed by zero or more groups of spaces followed by “word” characters
  4. \*) – followed by an asterisk
Note: If you’d like to learn more about regular expressions above and beyond this tutorial, check out this NSRegularExpression tutorial and cheat sheet.

As an exercise, decode the other regular expressions yourself, using the explanation above and the cheat sheet as a guide. How many can you do on your own?

Here’s a question for you. Can you describe, in plain English, what the regular expression:

\s([A-Z]{2,})\s matches?

Solution Inside SelectShow>

You will also need to initialize the replacements dictionary. Add the following class initializer to the SyntaxHighlightTextStorage class:

override init() {
	super.init()
	createHighlightPatterns()
}
 
required init(coder aDecoder: NSCoder) {
	super.init(coder: aDecoder)
}

You’re calling the plain initializer with no arguments in the rest of your project. The init(coder:) initializer is required to keep the compiler happy.

Finally, replace the implementation of applyStylesToRange() with the following:

func applyStylesToRange(searchRange: NSRange) {
  let normalAttrs = [NSFontAttributeName : UIFont.preferredFontForTextStyle(UIFontTextStyleBody)]
 
  // iterate over each replacement
  for (pattern, attributes) in replacements {
    let regex = NSRegularExpression(pattern: pattern, options: nil, error: nil)
    regex.enumerateMatchesInString(backingStore.string, options: nil, range: searchRange) {
      match, flags, stop in
      // apply the style
      let matchRange = match.rangeAtIndex(1)
      self.addAttributes(attributes, range: matchRange)
 
      // reset the style to the original
      let maxRange = matchRange.location + matchRange.length
      if maxRange + 1 < self.length {
        self.addAttributes(normalAttrs, range: NSMakeRange(maxRange, 1))
      }
    }
  }
}

Previously, this method performed just one regex search for bold text. Now it does the same thing, but it iterates over the dictionary of regex matches and attributes since there are many text styles to look for. For each regex, it runs the search and applies the specified style to the matched pattern.

Build and run your app, and exercise all of the new styles available to you, as illustrated below:

ExclusionPath

And here is a slightly more challenging exercise. If you enter the text: “*This is   not   bold*” (without the quotes) into a note, you’ll discover that it does not turn bold. In other words, if the selected text has multiple spaces between the words, there is no match.
Can you create a regular expression that will embolden that text? It’s just a simple modification of the one already in the code.

Solution Inside SelectShow>

Your app is nearly complete; there are just a few loose ends to clean up.

If you’ve changed the orientation of your screen while working on your app, you’ve already noticed that the app no longer responds to content size changed notifications since your custom implementation doesn’t yet support this action.

As for the second issue, if you add a lot of text to a note you’ll notice that the bottom of the text view is partially obscured by the keyboard; it’s a little hard to type things when you can’t see what you’re typing!

Time to fix up those two issues.

Reviving Dynamic Type

To correct the issue with dynamic type, your code should update the fonts used by the attributed string containing the text of the note when the content size change notification occurs.

Add the following function to the SyntaxHighlightTextStorage class:

func update() {
  // update the highlight patterns
  createHighlightPatterns()
 
  // change the 'global' font
  let bodyFont = [NSFontAttributeName : UIFont.preferredFontForTextStyle(UIFontTextStyleBody)]
  addAttributes(bodyFont, range: NSMakeRange(0, length))
 
  // re-apply the regex matches
  applyStylesToRange(NSMakeRange(0, length))
}

The method above updates all the fonts associated with the various regular expressions, applies the body text style to the entire string, and then re-applies the highlighting styles.

Finally, open NoteEditorViewController.swift and modify preferredContentSizeChanged() to perform the update:

func preferredContentSizeChanged(notification: NSNotification) {
  textStorage.update()
  updateTimeIndicatorFrame()
}

Build and run; change your text size preferences, and the text should adjust accordingly as in the example below:

ExclusionPath

Resizing Text Views

All that’s left to do is solve the problem of the keyboard obscuring the bottom half of the text view when editing long notes. This is one issue that iOS 8 hasn’t solved for us yet!

To fix this, you’ll reduce the size of the text view frame when the keyboard is visible.
Open NoteEditorViewController.swift and add the following line to viewDidLoad(), right after the call to createTextView():

textView.scrollEnabled = true

This enables text view scrolling in your note editor view.

Now add the following code to the bottom of viewDidLoad():

NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardDidShow:", name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardDidHide:", name: UIKeyboardDidHideNotification, object: nil)

This adds notifications for when the keyboard is shown or hidden; this is your signal to resize your text view frame accordingly.

Next, add the following method to the class:

func updateTextViewSizeForKeyboardHeight(keyboardHeight: CGFloat) {
  textView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height - keyboardHeight)
}

This method reduces the height of the text view to accommodate the keyboard.

Finally, you need to implement the two methods to respond to the notifications. Add the following methods to the class:

func keyboardDidShow(notification: NSNotification) {
  if let rectValue = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue {
    let keyboardSize = rectValue.CGRectValue().size
    updateTextViewSizeForKeyboardHeight(keyboardSize.height)
  }
}
 
func keyboardDidHide(notification: NSNotification) {
  updateTextViewSizeForKeyboardHeight(0)
}

When the keyboard is shown, you need to pull out the keyboard size from the notification, and from there it is simple to obtain the keyboard height. When the keyboard is hidden you just need to reset the height adjustment back to zero.

Note: Although on earlier versions of iOS you needed to account for the current screen orientation when calculating the new text view size (because the width and height properties of UIView instances are swapped when the screen orientation changes, but the keyboard’s width and height properties are not!), this is no longer required on iOS 8.

Build and run your app, edit a note and check that displaying the keyboard no longer obscures the text, as shown below:

Keyboard Shown

Note: At the time of writing there is a subtle bug with iOS 8 – when the text view resizes, the cursor position may still be off-screen. The cursor moves to its correct location if the user taps the ‘return’ key. We’ll keep an eye on this, and if the bug persists we’ll try to find an alternative solution.

Where To Go From Here?

Hopefully, this Text Kit tutorial has helped you understand the various new features such as dynamic type, font descriptors and letterpress, that you will no doubt find use for in practically ever app you write. You can download the final project and review the code and the app.

If you’d like to learn more about Text Kit, check out our book iOS 7 By Tutorials. The book has two chapters on Text Kit that provide an in-depth look at the Text Kit architecture and will show you how to create high performance multi-column text layouts.

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

Text Kit Tutorial in Swift is a post from: Ray Wenderlich

The post Text Kit Tutorial in Swift appeared first on Ray Wenderlich.

Video Tutorial: iOS Animation Part 6: Basic Layer Animations

Readers’ App Reviews – September 2014

$
0
0
COVERIMAGE

Downright Funky Apps!

September is almost over and the world is home to not one, but two new iPhones!

The new screen sizes of the iPhone 6 and iPhone 6 Plus bring new challenges and opportunities to developers.

With more screen than ever before, our apps have a chance to offer even more unique interfaces taking advantage of the multitouch we all take for granted.

With our latest OS and devices, our favorite indies have been busy. ;] I’ve got an inbox full of apps and I’m excited to share a few with you for September.

This month we’ve got:

  • A game the teaches you about famous art
  • An app that makes markdown a breeze
  • A 3D editor so easy to use its for kids
  • And of course, much more!

Keep reading to learn more about your fellow readers’ latest creations.

Blox 3d Junior!

Blox3DJunior
Blox 3D Junior is a very cool 3D shape editor so easy its designed for kids.

Blox 3D focuses on voxels. You can create them in any direction in 3D space using a variety of colors. The creations are incredible. You can zoom and spin your creation to see it from every angle. This is an app that really showcases how awesome the iPad is.

You can save up to 38 models to show off later. The app also comes preloaded with some fun models and a mode than builds them automatically for very young kids to watch and learn from.

GREG

Greg
GREG is a puzzle game that uses your love for matching to build your speed for addition.

GREG is a simple game, a grid of numbers called tokens sits below a target number and timer. Select tokens that add up to the target number before time runs out, simple.

With some special tricks you can gain extra points, like selecting tokens in a row or column. And some tokens will be highlighted with powerups that only last for a small time.

A full bank of available achievements will keep you fighting for that final match.

Huerons

Huerons
There can only be one! Hueron that is.

Huerons is an addictive puzzle game focused on combining Huerons down to just one. A tap on an empty spot between two Huerons will trigger a merge. Huerons come in different colors and sometimes must be converted before the puzzle is complete.

Order is everything. Combine too soon and you could be left with Huerons unable to be combined.

With over 100 puzzles, Huerons will keep you busy for awhile. And you can always go back and take a shot at solving a puzzle quicker than before.

Spend Stack

SpendStack
Budgeting can be a hard thing for some. Spend Stack is here to help.

Spend Stack lets you organize stacks of items be that groceries, furniture, or in my case electronics I’ve got my eye on. ;]

Each stack lets you create items with prices, prefilled tax rates based on location, and a category. You can drag and drop to order them based on your priority. And each stack will keep track of a total for all items.

I normally do this in a sticky on my Dashboard, but having a dedicated app for it is already proving nicer. The app also has themes per stack, so your new toys Stack can have a nice dark theme while gifts for your family can have a nice colorful theme.

Mixpix

MixPix
Pictures are boring. MixPix are fun!

MixPix offers hundreds of add ons for your photos to spice them up. Its got speech bubbles, beards, glasses, frames, borders, hair, hats, and more. And before you get to work on your fun photo, make sure its a good one. MixPix is also packing image filters for exposure and color correction along with blurs and overlays.

When you’re finished perfecting your master piece, Twitter and Facebook export is built right in. And of course its got export to PhotoStream, iMessage, Instagram, etc.

Summbot

Summbot
Summbot is a unique app that aims to help us consume our favorite articles quickly and efficiently.

Summbot takes both free text and a direct URL. Summbot analyzes your content for the most important parts. Entire articles are converted to a simple bullet list of the key take aways.

Summbot’s UI strips away the chrome and leaves just your content for quick efficient reading. My testing with Summbot showed it really works. I was able to condense 1000 word articles into just a handful of bullet points. Reading the bullet points first and the article second, I was able to see that I had gotten the gist of the article already. The algorithms are really quite amazing.

Art Scrambles

ArtScramble
Puzzles are almost universally fun. I love a good puzzle large and small.

Art Scrambles brings this fun to your pocket. Art Scrambles has over 6 different types of puzzles generated from over 600 different works of classic art. It includes art from artists such as Leonardo da Vinci, Vincent Van Gogh, and Hans Holbein the Younger.

Art Scrambles saves your progress per painting so you can start and continue anytime with multiple puzzles in progress.

Tap’s Up

TapUp
Tap’s up is a simple arcade game thats simply addicting.

Tap’s up has just 4 things to learn. Blue faces need a tap to cheer up. Gray faces need a double tap to wake up. And Red faces should be left alone because they’ve just shattered their iPhone. Or something like that. ;]

Basically each level has a grid of faces, and you’ve got less and less time to cheer up the Blue and wake up the Gray.

Its simple never ending gameplay challenges and relaxes you. GameCenter integration lets you brag to friends as well as Challenge them to beat your latest score.

ClarityHD

Clarity
ClarityHD is a yet another content creation tool for your iPad. You know, that device supposedly only capable of consumption.

ClarityHD lets you write without distractions while taking full advantage of Markdown to dress up and organize your writing.

ClarityHD’s editor automatically detects links, phone numbers, and dates. With a full web browser built in, you can view links right inside ClarityHD. When you’re finished you can preview it in the app to see how your Markdown renders.

ClarityHD also packs Dropbox syncing for when you’re back at your desk. Or if you prefer, it can export to HTML with a beautiful theme. It also offers sharing over Facebook, Twitter, iMessage, and Email as you’d expect.

Dev Cards

DevCards
Dev Cards is an app for us. Dev Cards offers a way to study concepts from iOS, Objective-C, Xcode, and more. Dev Cards even has support for our latest tool, Swift.

Dev Cards has flash cards for learning, True False quizzes to test along the way, and Multiple Choice tests to verify what you’ve learned. Dev Cards supports spaced recognition as well. So as you test yourself, questions you get right several times in a row will be hidden for awhile then brought back to test your longterm knowledge.

Dev Cards is a must have for any beginning developer. A great tool for those times you’re away from your computer. Learning is often accelerated by immersion, so having this tool available everywhere is a great opportunity for all of us.

Peeps

Peeps
Peeps is a new social app focused around video. Peeps is built around sending short videos to your friends instead of boring messages or still images.

Peeps works off your phone number. This means sending video messages to your Peeps doesn’t require your friends download the app. Peeps will forward your message on to your friends using normal MMS video messages.

Your messages are organized into long running conversations. Peeps also supports group conversations for all your friends sending videos back and forth.



Honorable Mentions

Every month I get more submissions than I can handle. I give every app a shot, but I can’t write about them all. These are still great apps made by readers like you. Its no a popularity contest or even a favorite picking contest. I just try to get a glimpse of what the community is working on through your submissions. Take a moment and checkout these other great apps I didn’t have time to showcase properly.

Run, Run Away!
Egg Hunt Mania
RoboBird Space
Firing at ICE
Calls Game
Math Bomb
Balloon Saw Massacre
Greenchick
Push the cap!
Truth or Dare
Fun with Flags
Retro Formula
Retro Tunnel
Cave Dash
Tap on Shape
Zig Zag Bird
Month Calendar 2
AR Tap Shooter
A Bird Called Mike
Monster Attack
Exert It
BMI Explorer Pro
4 Pics 1 Word Puzzle: Guess It!



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’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!

Readers’ App Reviews – September 2014 is a post from: Ray Wenderlich

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

iOS Games by Tutorials Second Edition Now Available!

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

iOS Games by Tutorials Second Edition Now Available!

Note from Ray: Happy Wednesday – it’s book release day during the iOS 8 Feast!

The team and I are happy to announce that we have released a brand new second edition of our popular book, iOS Games by Tutorials!

In iOS Games by Tutorials, you learn how to make 2D games for iOS using Apple’s game framework, Sprite Kit.

This new second edition is fully updated for Swift and iOS 8. In addition, there are a few extra cool goodies inside.

In addition, Matthijs Hollemans has updated part 2 of the iOS Apprentice to iOS 8 and Swift. Keep reading to find out more about each update!

iOS Games by Tutorials Second Edition

In iOS Games by Tutorials Second Edition, we went through each and every chapter of this book and updated all of the games to use Swift (previously Objective-C), to make sure everything works on iOS 8 and the latest version of Xcode, and to use the latest and greatest technologies and API calls where necessary.

This update was a huge amount of work, since we had to update everything to an entirely new language. But even though it took such a large effort on our part, we are releasing this as a free update to our PDF customers!

This is our way of saying thanks for purchasing the PDF. You are what makes all of the tutorials, books, and starter kits we make on raywenderlich.com possible.

Want more details on what’s changed in the second edition? Here’s the full list:

  • Updated all chapters and sample projects to Swift.
  • Wrote a new version of our Sprite Kit utility library (SKTUtils) for Swift. Takes advantage of new Swift capabilities such as operator overloading, for even more readable games.
  • Updated all games to work on both iPhone and iPad (previously only iPhone), including the new iPhone 6 and iPhone 6 Plus.
  • Updated all chapters to be compatible with iOS 8, including using new APIs where possible and adding notes about relevant updates for iOS 8.
  • Updated all chapters to be compatible with the latest version of Xcode (6.0.1 at the time of this update).
  • Added additional clarifications and useful notes to many chapters.
  • Removed the “AirPlay” chapter. Instead, we added two new chapters: “OS X” and “More OS X”, to cover how to port your Sprite Kit games to OS X. Note that these chapters are not yet released since Yosemite is still in beta; we will release a free update at some point after Yosemite is out of beta.

The iOS Apprentice Third Edition Part 2: Checklists

iOS Apprentice Third Edition Part 2 Now Available!

iOS Apprentice Third Edition Part 2 Now Available!

As you may know, Matthijs Hollemans has been updating his popular iOS Apprentice series to iOS 8 and Swift as well.

Today, we are happy to announce that the update to Part 2 is now available!

Again, this is a free update to existing PDF customers. If you bought the iOS Apprentice when it first came out, you have seen it update from iOS 5 -> 6 -> 7 -> 8, and even an entirely new language!

Pretty good value for the money, eh? :]

How To Get Your Copy

If you are an existing PDF customer for iOS Games by Tutorials or the iOS Apprentices, you can download it now on your My Loot page.

Otherwise, you can pick up a copy here:

We hope you enjoy these massive free updates; again it’s our way of saying thank you for supporting everything we do on this site.

And by the way, don’t forget to sign up for RWDevCon, and grab your early bird discount while you still can. See you there!

iOS Games by Tutorials Second Edition Now Available! is a post from: Ray Wenderlich

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

Sprite Kit Swift Tutorial for Beginners

$
0
0
Learn how to create a simple 2D iOS game in this Sprite Kit Swift Tutorial!

Learn how to create a simple 2D iOS game in this Sprite Kit Swift Tutorial!

Note from Ray: This is a Swift update to a popular tutorial on our site, released as part of the iOS 8 Feast.

Like Batman and Robin or Superman and Lois Lane, Sprite Kit and Swift are an amazing combination:

  • Sprite Kit is one of the best ways to make games on iOS. It’s easy to learn, powerful, and is fully-supported by Apple.
  • Swift is an easy language to get started with, especially if you are a beginner to the iOS platform.

In this tutorial, you will learn how to create a simple 2D game using Apple’s 2D game framework, Sprite Kit – using Swift!

You can either follow along with this tutorial, or just jump straight to the sample project at the end. And yes. There will be ninjas.

Note: This tutorial has a special place in my heart, as the original version was one of the first tutorials ever released on our site. It was written in a completely different language (Objective-C) and a completely different game framework (Cocos2D). My, how times have changed! :]

Sprite Kit vs. Unity

The most popular alternative to Sprite Kit at the moment is a game framework called Unity. Unity was originally developed as a 3D engine, but it recently got full built-in 2D support too.

So before you get started, I recommend you put some thought into whether Sprite Kit or Unity is the best choice for your game.

Advantages of Sprite Kit

  • It’s built right into iOS. There is no need to download extra libraries or have external dependencies. You can also seamlessly use other iOS APIs like iAd, In-App Purchases, etc. without having to rely on extra plugins.
  • It leverages your existing skills. If you already know Swift and iOS development, you can pick up Sprite Kit extremely quickly.
  • It’s written by Apple. This gives you some confidence that it will be well supported moving forward on all of Apple’s new products.
  • It’s free. Maybe one of the best reasons for small indies! You get all of Sprite Kit’s functionality at no cost. Unity does have a free version but does not have all of the features of the Pro version (you’ll need to upgrade if you want to avoid the Unity splash screen, for example).

Advantages of Unity

  • Cross-platform. This is one of the big ones. If you use Sprite Kit, you’re locked into the iOS ecosystem. With Unity, you can easily port your games to Android, Windows, and more.
  • Visual scene designer. Unity makes it extremely easy to lay out your levels and test your game in realtime with the click of a button. Sprite Kit does have a very basic scene editor in iOS 8, but it is very basic compared to what Unity offers.
  • Asset store. Unity comes with a built-in asset store where you can buy various components for your game. Some of these components can save you a good bit of development time!
  • More powerful. In general, Unity just has more features and functionality than the Sprite Kit / Scene Kit combination.

Which Should I Choose?

After this a lot of you may be thinking, “Well, which 2D framework should I choose?”

The answer that depends on what your goals are. Here’s my 2c:

  • If you are a complete beginner, or solely focused on iOS: Use Sprite Kit – it’s built in, easy to learn, and will get the job done.
  • If you’re want to be cross-platform, or have a more complicated game: Use Unity – it’s more powerful and flexible.

If you think Unity is for you, check out some of our Unity written tutorials or our new Unity video tutorial series.

Otherwise, keep reading on to get started with Sprite Kit!

Hello, Sprite Kit!

Let’s start by getting a simple Hello World project up and running by using the Sprite Kit Game template that comes built in to Xcode 6.

Start up Xcode, select File\New\Project, choose the iOS\Application\Game template, and click Next:

001_New_Game

Enter SpriteKitSimpleGame for the Product Name, Swift for Language< SpriteKit for Game Technology, iPhone for Devices, and click Next:

002_Options

Choose somewhere on your drive to save the project, and click Create. Select your iPhone 6 simulator, then click the play button to run the project as-is. After a brief splash screen, you should see the following:

003_Hello_World

Sprite Kit is organized into the concept of scenes, which are kind of like “levels’ or “screens” for a game. For example, you might have a scene for the main gameplay area, and another scene for the world map between levels.

If you take a look at your project, you’ll see the template has already created a scene for you by default – GameScene. Open GameScene.swift and you’ll see that it contains some code to put a label on the screen, and add a rotating spaceship when you tap somewhere.

In this tutorial, you’ll mainly be working within GameScene. But before you begin, you have to make a few tweaks because this game needs to run in landscape instead of portrait.

Initial Setup

The template provided to you has two issues. First, it’s set up the game to be Portrait, but you want landscape. Second, it is currently using Sprite Kit’s scene editor, which you don’t need for this tutorial. Let’s fix these issues.

First, open your target setting by clicking your SpriteKitSimpleGame project in the Project Navigator, selecting the SpriteKitSimpleGame target. Then, in the Deployment Info section, uncheck Portrait so only Landscape Left and Landscape Right are checked, as shown below:

004_Landscape

Second, delete GameScene.sks and choose Move to Trash when prompted. This file allows you to lay out sprites and other components of a scene visually, however for this game it’s just easier to create things programmatically, so you don’t need it.

Next, open GameViewController.swift and replace the contents with the following:

import UIKit
import SpriteKit
 
class GameViewController: UIViewController {
 
  override func viewDidLoad() {
    super.viewDidLoad()
    let scene = GameScene(size: view.bounds.size)
    let skView = view as SKView
    skView.showsFPS = true
    skView.showsNodeCount = true
    skView.ignoresSiblingOrder = true
    scene.scaleMode = .ResizeFill
    skView.presentScene(scene)
  }
 
  override func prefersStatusBarHidden() -> Bool {
    return true
  }
}

GameViewController is a normal UIViewController, except that its root view is a SKView, which is a view that contains a Sprite Kit scene.

Here, you’ve implemented viewDidLoad() to create a new instance of the GameScene on startup, with the same size of the view itself.

That’s it for the initial setup – now let’s get something on the screen!

Adding a Sprite

First, download the resources for this project and drag them into your Xcode project. Make sure that “Copy items into destination group’s folder (if needed)” is checked, and that your SpriteKitSimpleGame target is selected.

Next, open GameScene.swift and replace the contents with the following:

import SpriteKit
 
class GameScene: SKScene {
 
  // 1
  let player = SKSpriteNode(imageNamed: "player")
 
  override func didMoveToView(view: SKView) {
    // 2
    backgroundColor = SKColor.whiteColor()
    // 3
    player.position = CGPoint(x: size.width * 0.1, y: size.height * 0.5)
    // 4
    addChild(player)
  }
}

Let’s go over this step-by-step.

  1. Here you declare a private constant for the player (i.e. the ninja), which is an example of a sprite. As you can see, creating a sprite is easy – simply pass in the name of the image to use.
  2. Setting the background color of a scene in Sprite Kit is as simple as setting the backgroundColor property. Here you set it to white.
  3. You position the sprite to be 10% across vertically, and centered horizontally.
  4. To make the sprite appear on the scene, you must add it as a child of the scene. This is similar to how you make views children of other views.

Build and run, and voila – ladies and gentlemen, the ninja has entered the building!

005_Ninja

Moving Monsters

Next you want to add some monsters into your scene for your ninja to combat. To make things more interesting, you want the monsters to be moving – otherwise there wouldn’t be much of a challenge! So let’s create the monsters slightly off screen to the right, and set up an action for them telling them to move to the left.

Add the following methods to GameScene.swift:

func random() -> CGFloat {
  return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
 
func random(#min: CGFloat, max: CGFloat) -> CGFloat {
  return random() * (max - min) + min
}
 
func addMonster() {
 
  // Create sprite
  let monster = SKSpriteNode(imageNamed: "monster")
 
  // Determine where to spawn the monster along the Y axis
  let actualY = random(min: monster.size.height/2, max: size.height - monster.size.height/2)
 
  // Position the monster slightly off-screen along the right edge,
  // and along a random position along the Y axis as calculated above
  monster.position = CGPoint(x: size.width + monster.size.width/2, y: actualY)
 
  // Add the monster to the scene
  addChild(monster)
 
  // Determine speed of the monster
  let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
 
  // Create the actions
  let actionMove = SKAction.moveTo(CGPoint(x: -monster.size.width/2, y: actualY), duration: NSTimeInterval(actualDuration))
  let actionMoveDone = SKAction.removeFromParent()
  monster.runAction(SKAction.sequence([actionMove, actionMoveDone]))
 
}

I’ve spelled out things in a verbose manner here to make things as easy to understand as possible. The first part should make sense based on what we’ve discussed so far: you do some simple calculations to determine where you want to create the object, set the position of the object, and add it to the scene the same way you did for the player sprite.

The new element here is adding actions. Sprite Kit provides a lot of extremely handy built-in actions that help you easily change the state of sprites over time, such as move actions, rotate actions, fade actions, animation actions, and more. Here you use three actions on the monster:

  • SKAction.moveTo(_:duration:): You use this action to direct the object to move off-screen to the left. Note that you can specify the duration for how long the movement should take, and here you vary the speed randomly from 2-4 seconds.
  • SKAction.removeFromParent(): Sprite Kit comes with a handy action that removes a node from its parent, effectively “deleting it” from the scene. Here you use this action to remove the monster from the scene when it is no longer visible. This is important because otherwise you’d have an endless supply of monsters and would eventually consume all device resources.
  • SKAction.sequence(_:): The sequence action allows you to chain together a sequence of actions that are performed in order, one at a time. This way, you can have the “move to” action perform first, and once it is complete perform the “remove from parent” action.

One last thing before you go. You need to actually call the method to create monsters! And to make things fun, let’s have monsters continuously spawning over time.

Simply add the following code to the end of didMoveToView():

runAction(SKAction.repeatActionForever(
  SKAction.sequence([
    SKAction.runBlock(addMonster),
    SKAction.waitForDuration(1.0)
  ])
))

Here you run a sequence of actions to call a block of code (you can seamlessly pass in your addMonster() method here thanks to the power of Swift), and then wait for 1 second. You then repeat this sequence of actions endlessly.

That’s it! Build and run the project, now you should see monsters happily moving across the screen:

006_Monsters

Shooting Projectiles

At this point, the ninja is just begging for some action – so let’s add shooting! There are many ways you could implement shooting, but for this game you are going to make it so when the user taps the screen, it shoots a projectile from the player in the direction of the tap.

I want to use a “move to” action to implement this to keep things at a beginner level, but in order to use this you have to do a little math.

This is because the “move to” action requires you to give a destination for the projectile, but you can’t just use the touch point because the touch point represents just the direction to shoot relative to the player. You actually want to keep the bullet moving through the touch point until the bullet goes off-screen.

Here’s a picture that illustrates the matter:

Projectile Triangle

So as you can see, you have a small triangle created by the x and y offset from the origin point to the touch point. You just need to make a big triangle with the same ratio – and you know you want one of the endpoints to be off the screen.

To run these calculations, it really helps if you have some basic vector math routines you can call (like methods to add and subtract vectors). However, Sprite Kit doesn’t have any by default so you’ll have to write your own.

Luckily they are very easy to write thanks to the power of Swift operator overloading. Add these functions to the top of your file, right before the GameScene class:

func + (left: CGPoint, right: CGPoint) -> CGPoint {
  return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
 
func - (left: CGPoint, right: CGPoint) -> CGPoint {
  return CGPoint(x: left.x - right.x, y: left.y - right.y)
}
 
func * (point: CGPoint, scalar: CGFloat) -> CGPoint {
  return CGPoint(x: point.x * scalar, y: point.y * scalar)
}
 
func / (point: CGPoint, scalar: CGFloat) -> CGPoint {
  return CGPoint(x: point.x / scalar, y: point.y / scalar)
}
 
#if !(arch(x86_64) || arch(arm64))
func sqrt(a: CGFloat) -> CGFloat {
  return CGFloat(sqrtf(Float(a)))
}
#endif
 
extension CGPoint {
  func length() -> CGFloat {
    return sqrt(x*x + y*y)
  }
 
  func normalized() -> CGPoint {
    return self / length()
  }
}

These are standard implementations of some vector math functions. If you’re confused about what’s going on here or are new to vector math, check out this quick vector math explanation.

Next, add a new method to the file:

override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
 
  // 1 - Choose one of the touches to work with
  let touch = touches.anyObject() as UITouch
  let touchLocation = touch.locationInNode(self)
 
  // 2 - Set up initial location of projectile
  let projectile = SKSpriteNode(imageNamed: "projectile")
  projectile.position = player.position
 
  // 3 - Determine offset of location to projectile
  let offset = touchLocation - projectile.position
 
  // 4 - Bail out if you are shooting down or backwards
  if (offset.x < 0) { return }
 
  // 5 - OK to add now - you've double checked position
  addChild(projectile)
 
  // 6 - Get the direction of where to shoot
  let direction = offset.normalized()
 
  // 7 - Make it shoot far enough to be guaranteed off screen
  let shootAmount = direction * 1000
 
  // 8 - Add the shoot amount to the current position
  let realDest = shootAmount + projectile.position
 
  // 9 - Create the actions
  let actionMove = SKAction.moveTo(realDest, duration: 2.0)
  let actionMoveDone = SKAction.removeFromParent()
  projectile.runAction(SKAction.sequence([actionMove, actionMoveDone]))
 
}

There’s a lot going on here, so let’s review it step by step.

  1. One of the cool things about SpriteKit is that it includes a category on UITouch with locationInNode(_:) and previousLocationInNode(_:) methods. These let you find the coordinate of a touch within a SKNode’s coordinate system. In this case, you use it to find out where the touch is within the scene’s coordinate system.
  2. You then create a projectile and place it where the player is to start. Note you don’t add it to the scene yet, because you have to do some sanity checking first – this game does not allow the ninja to shoot backwards.
  3. You then subtract the projectile’s current position from the touch location to get a vector from the current position to the touch location.
  4. If the X value is less than 0, this means the player is trying to shoot backwards. This is is not allowed in this game (real ninjas don’t look back!), so just return.
  5. Otherwise, it’s OK to add the projectile to the scene.
  6. Convert the offset into a unit vector (of length 1) by calling normalized(). This will make it easy to make a vector with a fixed length in the same direction, because 1 * length = length.
  7. Multiply the unit vector in the direction you want to shoot in by 1000. Why 1000? It will definitely be long enough to go past the edge of the screen :]
  8. Add the shoot amount to the current position to get where it should end up on the screen.
  9. Finally, create moveTo(_:, duration:) and removeFromParent() actions like you did earlier for the monster.

Build and run, and now your ninja should be able to fire away at the oncoming hordes!

007_Shoot

Collision Detection and Physics: Overview

So now you have shurikens flying everywhere – but what your ninja really wants to do is to lay some smack down. So let’s add in some code to detect when your projectiles intersect your targets.

One of the nice things about Sprite Kit is it comes with a physics engine built right in! Not only are physics engines great for simulating realistic movement, but they are also great for collision detection purposes.

Let’s set up the game to use Sprite Kit’s physics engine to determine when monsters and projectiles collide. At a high level, here’s what you’re going to do:

  • Set up the physics world. A physics world is the simulation space for running physics calculations. One is set up on the scene by default, and you might want to configure a few properties on it, like gravity.
  • Create physics bodies for each sprite. In Sprite Kit, you can associate a shape to each sprite for collision detection purposes, and set certain properties on it. This is called a physics body. Note that the physics body does not have to be the exact same shape as the sprite. Usually it’s a simpler, approximate shape rather than pixel-perfect, since that is good enough for most games and performant.
  • Set a category for each type of sprite. One of the properties you can set on a physics body is a category, which is a bitmask indicating the group (or groups) it belongs to. In this game, you’re going to have two categories – one for projectiles, and one for monsters. Then later when two physics bodies collide, you can easily tell what kind of sprite you’re dealing with by looking at its category.
  • Set a contact delegate. Remember that physics world from earlier? Well, you can set a contact delegate on it to be notified when two physics bodies collide. There you’ll write some code to examine the categories of the objects, and if they’re the monster and projectile, you’ll make them go boom!

Now that you understand the battle plan, it’s time to put it into action!

Collision Detection and Physics: Implementation

Start by adding this struct to the top of GameScene.swift:

struct PhysicsCategory {
  static let None      : UInt32 = 0
  static let All       : UInt32 = UInt32.max
  static let Monster   : UInt32 = 0b1       // 1
  static let Projectile: UInt32 = 0b10      // 2
}

This is setting up the constants for the physics categories you’ll need in a bit – no pun intended! :]

Note: You may be wondering what the fancy syntax is here. Note that the category on Sprite Kit is just a single 32-bit integer, and acts as a bitmask. This is a fancy way of saying each of the 32-bits in the integer represents a single category (and hence you can have 32 categories max). Here you’re setting the first bit to indicate a monster, the next bit over to represent a projectile, and so on.

Next, mark GameScene as implementing the SKPhysicsContactDelegate protocol:

class GameScene: SKScene, SKPhysicsContactDelegate {

Then inside didMoveToView(_:) add these lines after adding the player to the scene:

physicsWorld.gravity = CGVectorMake(0, 0)
physicsWorld.contactDelegate = self

This sets up the physics world to have no gravity, and sets the scene as the delegate to be notified when two physics bodies collide.

Inside the addMonster() method, add these lines right after creating the monster sprite:

monster.physicsBody = SKPhysicsBody(rectangleOfSize: monster.size) // 1
monster.physicsBody?.dynamic = true // 2
monster.physicsBody?.categoryBitMask = PhysicsCategory.Monster // 3
monster.physicsBody?.contactTestBitMask = PhysicsCategory.Projectile // 4
monster.physicsBody?.collisionBitMask = PhysicsCategory.None // 5

Let’s go over what this does line by line.

  1. Creates a physics body for the sprite. In this case, the body is defined as a rectangle of the same size of the sprite, because that’s a decent approximation for the monster.
  2. Sets the sprite to be dynamic. This means that the physics engine will not control the movement of the monster – you will through the code you’ve already written (using move actions).
  3. Sets the category bit mask to be the monsterCategory you defined earlier.
  4. The contactTestBitMask indicates what categories of objects this object should notify the contact listener when they intersect. You choose projectiles here.
  5. The collisionBitMask indicates what categories of objects this object that the physics engine handle contact responses to (i.e. bounce off of). You don’t want the monster and projectile to bounce off each other – it’s OK for them to go right through each other in this game – so you set this to 0.

Next add some similar code to touchesEnded(_:withEvent:), right after the line setting the projectile’s position:

projectile.physicsBody = SKPhysicsBody(circleOfRadius: projectile.size.width/2)
projectile.physicsBody?.dynamic = true
projectile.physicsBody?.categoryBitMask = PhysicsCategory.Projectile
projectile.physicsBody?.contactTestBitMask = PhysicsCategory.Monster
projectile.physicsBody?.collisionBitMask = PhysicsCategory.None
projectile.physicsBody?.usesPreciseCollisionDetection = true

As a test, see if you can understand each line here and what it does. If not, just refer back to the points explained above!

As a second test, see if you can spot two differences. Answer below!

Solution Inside: What Are the Differences? SelectShow>

Next, add a method that will be called when the projectile collides with the monster. Note that nothing calls this automatically, you will be calling this later.

func projectileDidCollideWithMonster(projectile:SKSpriteNode, monster:SKSpriteNode) {
  println("Hit")
  projectile.removeFromParent()
  monster.removeFromParent()
}

All you do here is remove the projectile and monster from the scene when they collide. Pretty simple, eh?

Now it’s time to implement the contact delegate method. Add the following new method to the file:

func didBeginContact(contact: SKPhysicsContact) {
 
  // 1
  var firstBody: SKPhysicsBody
  var secondBody: SKPhysicsBody
  if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
    firstBody = contact.bodyA
    secondBody = contact.bodyB
  } else {
    firstBody = contact.bodyB
    secondBody = contact.bodyA
  }
 
  // 2
  if ((firstBody.categoryBitMask & PhysicsCategory.Monster != 0) &&
      (secondBody.categoryBitMask & PhysicsCategory.Projectile != 0)) {
    projectileDidCollideWithMonster(firstBody.node as SKSpriteNode, monster: secondBody.node as SKSpriteNode)
  }
 
}

Since you set the scene as the contactDelegate of the physics world earlier, this method will be called whenever two physics bodies collide (and their contactTestBitMasks are set appropriately).

There are two parts to this method:

  1. This method passes you the two bodies that collide, but does not guarantee that they are passed in any particular order. So this bit of code just arranges them so they are sorted by their category bit masks so you can make some assumptions later.
  2. Finally, it checks to see if the two bodies that collide are the projectile and monster, and if so calls the method you wrote earlier.

Give it a build and run, and now when your projectiles intersect targets they should disappear!

Finishing Touches

You’re pretty close to having a workable (but extremely simple) game now. You just need to add some sound effects and music (since what kind of game doesn’t have sound!) and some simple game logic.

Sprite Kit does not come with an audio engine like Cocos2D does, but the good news it does come with a simple way to play sound effects via actions, and that you can play background music pretty easily with AVFoundation.

You already have some cool background music I made and an awesome pew-pew sound effect in your project, from the resources for this tutorial you added to your project earlier. You just need to play them!

To do this, add the following code to the top of GameScene.swift:

import AVFoundation
 
var backgroundMusicPlayer: AVAudioPlayer!
 
func playBackgroundMusic(filename: String) {
  let url = NSBundle.mainBundle().URLForResource(
    filename, withExtension: nil)
  if (url == nil) {
    println("Could not find file: \(filename)")
    return
  }
 
  var error: NSError? = nil
  backgroundMusicPlayer = 
    AVAudioPlayer(contentsOfURL: url, error: &error)
  if backgroundMusicPlayer == nil {
    println("Could not create audio player: \(error!)")
    return
  }
 
  backgroundMusicPlayer.numberOfLoops = -1
  backgroundMusicPlayer.prepareToPlay()
  backgroundMusicPlayer.play()
}

This is some AVFoundation code to play some music endlessly.

To try this out, simply add this line to the beginning of didMoveToView(_:):

playBackgroundMusic("background-music-aac.caf")

As for the sound effect, add this line to the top of touchesEnded(_:withEvent:):

runAction(SKAction.playSoundFileNamed("pew-pew-lei.caf", waitForCompletion: false))

Pretty handy, eh? You can play a sound effect with one line!

Build and run, and enjoy your groovy tunes!

Game Over, Man!

Now, let’s create a new scene that will serve as your “You Win” or “You Lose” indicator. Create a new file with the iOS\Source\Swift File template, name the file GameOverScene and click Create.

Then replace GameOverScene.swift with the following:

import Foundation
import SpriteKit
 
class GameOverScene: SKScene {
 
  init(size: CGSize, won:Bool) {
 
    super.init(size: size)
 
    // 1
    backgroundColor = SKColor.whiteColor()
 
    // 2
    var message = won ? "You Won!" : "You Lose :["
 
    // 3
    let label = SKLabelNode(fontNamed: "Chalkduster")
    label.text = message
    label.fontSize = 40
    label.fontColor = SKColor.blackColor()
    label.position = CGPoint(x: size.width/2, y: size.height/2)
    addChild(label)
 
    // 4
    runAction(SKAction.sequence([
      SKAction.waitForDuration(3.0),
      SKAction.runBlock() {
        // 5
        let reveal = SKTransition.flipHorizontalWithDuration(0.5)
        let scene = GameScene(size: size)
        self.view?.presentScene(scene, transition:reveal)
      }
    ]))
 
  }
 
  // 6
  required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

There are five parts to point out here

  1. Sets the background color to white, same as you did for the main scene.
  2. Based on the won parameter, sets the message to either “You Won” or “You Lose”.
  3. This is how you display a label of text to the screen with Sprite Kit. As you can see, it’s pretty easy – you just choose your font and set a few parameters.
  4. Finally, this sets up and runs a sequence of two actions. I’ve included them all inline here to show you how handy that is (instead of having to make separate variables for each action). First it waits for 3 seconds, then it uses the runBlock action to run some arbitrary code.
  5. This is how you transition to a new scene in Sprite Kit. First you can pick from a variety of different animated transitions for how you want the scenes to display – you choose a flip transition here that takes 0.5 seconds. Then you create the scene you want to display, and use the presentScene(_:transition:) method on the self.view property.
  6. If you override an initializer on a scene, you must implement the required init(coder:) initializer as well. However this initializer will never be called, so you just add a dummy implementation with a fatalError(_:) for now.

So far so good, now you just need to set up your main scene to load the game over scene when appropriate.

Switch back to GameScene.swift, and inside addMonster(), replace the last line that runs the actions on the monster with the following:

let loseAction = SKAction.runBlock() {
  let reveal = SKTransition.flipHorizontalWithDuration(0.5)
  let gameOverScene = GameOverScene(size: self.size, won: false)
  self.view?.presentScene(gameOverScene)
}
monster.runAction(SKAction.sequence([actionMove, loseAction, actionMoveDone]))

This creates a new “lose action” that displays the game over scene when a monster goes off-screen. See if you understand each line here, if not refer to the explanation for the previous code block.

Also, another pop-quiz for you: why do you run the loseAction before actionMoveDone? Try reversing them to see what happens if you don’t know.

Solution Inside: Why is Lose Action First? SelectShow>

Now you should handle the win case too – don’t be cruel to your players! :] Add a new property to the top of GameScene, right after the declaration of player:

var monstersDestroyed = 0

And add this to the bottom of projectile(_:didCollideWithMonster:):

monstersDestroyed++
if (monstersDestroyed > 30) {
  let reveal = SKTransition.flipHorizontalWithDuration(0.5)
  let gameOverScene = GameOverScene(size: self.size, won: true)
  self.view?.presentScene(gameOverScene)
}

Go ahead and give it a build and run, and you should now have win and lose conditions and see a game over scene when appropriate!

008_You_Won

Where To Go From Here?

And that’s a wrap! Here is the full source code for this Sprite Kit Swift Tutorial for beginners.

I hope you enjoyed learning about Sprite Kit and are inspired to make your own game!

If you want to learn more about Sprite Kit, you should check out our book iOS Games by Tutorials. We’ll teach you everything you need to know – from physics, to tile maps, to particle systems, and even how to make your games “juicy” with polish and special effects.

If you have any questions or comments about this tutorial, please join the discussion below!

Sprite Kit Swift Tutorial for Beginners is a post from: Ray Wenderlich

The post Sprite Kit Swift Tutorial for Beginners appeared first on Ray Wenderlich.

How to Make a Waiting Game Like Farmville with Sprite Kit and Swift

$
0
0
Give your players the power to build their own Kookie Kiosk by implementing a waiting game.

Give your players the power to build their own Kookie Kiosk by implementing a waiting game.

Note from Ray: This is a brand new Swift tutorial released as part of the iOS 8 Feast. Enjoy!

Some gamers love fast-paced shoot-em-ups, while others love the anticipation of “waiting games” such as Farmville, Clash of Clans, or Tiny Tower.

The idea behind “waiting games” is that certain actions in your game – such as growing crops, building units, or constructing floors of a hotel, take time.

The user can only make progress if they return to the game frequently. This is good for two reasons:

  • Good for small time bursts. For example, a player can farm their crops if they have a spare 30 seconds while waiting in line. This is great for mobile games.
  • Good for the freemium monetization strategy. For example, since users are returning to your apps frequently, you have more opportunities for monetization – such as allowing users to skip waiting if they feel impatient!

In this tutorial you’ll implement a simple example of a waiting game — Kookie Kiosk — that sells shakes and cookies. Your players can get rich by buying and selling wares.

You’ll learn how to implement the basic mechanisms of any successful waiting game, including:

  • How to create a simple state machine.
  • How to handle timed events.
  • How to send local notifications.

These skills should prove useful to you, even as you build more complicated simulations.

Note: This Swift tutorial assumes you have working knowledge of Sprite Kit and Swift. If you’re new to Sprite Kit, check out our Sprite Kit Swift Tutorial for Beginners our our full book, iOS Games by Tutorials. For an introduction to Swift, check out this beginner Swift tutorial here.

Getting Started

Download the template project here. Extract the file to a convenient location on your computer. Start up XCode, select File\Open, then select the KookieKiosk-Swift.xcproject file and click Open.

Select the iPad 2 simulator in the upper left corner of XCode and click Play to test the app; you’ll see a very crowded screen as shown below:

This is what you should see when you run the template. What a mess!

This is what you should see when you run the template. What a mess!

Don’t worry — you’ll soon clean this up and make it playable.

Here are the main files in this project:

  • Constants.swift: This file contains all of the important constants for your project. The enum ZPosition ensures the layers of the game are drawn in the correct order. TimeScale gives you control over the speed of the game — you don’t want to wait around for what seems like forever to test your game, do you? :]
  • AppDelegate.swift: The only change to the default AppDelegate is a preload of two sound files to avoid any delay when the sound files are played the first time.
  • GameScene.swift: This contains the core logic of your game. It contains a list of all items in the kiosk and how much money you have in the game. At startup, all on-screen elements are added to GameScene. This class also loads and saves a .plist file containing important data about the items available in the kiosk. Finally, but most importantly, it responds to changes in the game state.
  • GameViewController.swift: This loads the initial scene. Nothing’s changed here; it’s still the basic Xcode template for a SpriteKit game.
  • StockItem.swift: This class represents a single item in the kiosk. It stores important properties of the item like type, flavor, and amount, as well as a few constants like the maximum amount the player can have of that item, the position on the screen, prices for restocking and selling this item, and of course the speed at which this item is stocked and sold. The class already contains two helper methods which draw the price label and the timer while the item is stocking.
  • Customer.swift: This class draws a customer on-screen and stores the item the customer wants to buy.
  • GameStateDelegate.swift: This protocol defines a method that responds to changes in the amount of money you have in the game. You’ll add more methods to this protocol later.
  • ProgressBar.swift: Kookie Kiosk makes use of several progress bars to show how much longer the player will have to wait for something. This protocol defines the structure every progress bar should have.
    • DiscreteProgressBar.swift: This is a progress bar with a fixed amount of steps. Each step corresponds to one image. All images for a single progress bar must share the same base name followed by a number, such as “customer_1″.
    • ContinuousProgressBar.swift: This progress bar has an almost infinite number of steps. In contrast to DiscreteProgressBar it only uses two images: one image where the progress bar is empty and one where it is full.

Have a little read through the code, and when you think you’ve absorbed enough of it move on to the next section where you’ll implement the state machine for your game.

Implementing the State Machine

In Kookie Kiosk you make a living by selling shakes and cookies. But you can’t sell something you don’t own (out-of-cookies exception), or restock a cookie platter that is already full (cookie overflow exception). The easiest way to manage the varying conditions of your game is with a state machine.

A state machine defines several states and the transitions between them. The state machine of Kookie Kiosk can be represented by the following state diagram:

States of an item in the Kiosk and transitions between states

States of an item in the Kiosk and transitions between states

Each stock item can be in one of four separate states:

  1. Empty: At the start of the game, a stock item is always empty. If you have sufficient funds, you can buy a new item by tapping the item.
  2. Stocking: Once you buy an item you’ll need to wait while the game replenishes your item stock.
  3. Stocked: When an item is fully stocked it switches to the stocked state.
  4. Selling: Once an item is stocked, the player can tap the stock item and start selling it to customers. When an item is sold out it returns to the empty state.

You’ll also need to provide some visual prompts to show the various states. Here are the images that correspond to each state:

Visualization of each state

Enough background — it’s time to get coding.

Add the following code to the bottom of Constants.swift:

enum State: Int {
    case empty, stocking, stocked, selling
}

This defines an enum with a value for each of the four states.

Next, add the following property at the end of the list of properties in StockItem.swift (after the declaration of priceTag):

var state : State

Note that you don’t specify an access modifier above such as public or private. This creates a property with internal visibility so that it can be accessed from anywhere within the target in which it’s defined — in this case the entire Kookie Kiosk project.

Note: At this point Xcode will display the error message “Property ‘self.state’ not initialized at super.init call”. Swift requires that you assign values to all properties of a class before you call the initializer of its superclass.

Add the following lines to init in StockItem.swift just before super.init():

var stateAsObject: AnyObject? = stockItemData["state"]
var stateAsInt = stateAsObject as Int
state = State.fromRaw(stateAsInt)!

In the code above you retrieve the state from the dictionary stockItemData, which contains all information about the stock item loaded from gamedata.plist shown below:

StockItemData Plist

In the first line above you retrieve the value from stockItemData stored under the key “state” and cast it to an Int. Then you map the value of the Int to the corresponding value of the enum State and assign the result to the state.

The exclamation mark here “unwraps” the optional value of fromRaw() and tells Swift you’re sure the result of this call will never be nil — if it ever was, your code would crash! When you give these guarantees to Swift you need to be completely certain that what you’re declaring is true. :]

Note: At the risk of being a bit obfuscated, you can reduce the need for temporary variables and write the above code in one line as shown below:
    state = State.fromRaw(stockItemData["state"] as AnyObject? as Int)!

Now that you’ve loaded the state of the stock item, you’ll need to make sure it’s been stored.

Add the following line to data() in StockItem.swift, right before the return statement:

data["state"] = state.toRaw()

This line sets the value for the key state to the raw Int value of the state for the stock item.

Note: If you see the error message unexpectedly found nil while unwrapping an Optional value just reset your simulator. You’ll see this error message when you try to load a variable from gamedata.plist that isn’t present in the file. You still have an old version of gamedata.plist loaded in the simulator — resetting the simulator removes this old file. If you’re working on a device then you’ll need to delete and reinstall the app entirely.

That takes care of loading and storing the states — but if you don’t actually display the states to your player they won’t have much fun in the game! Time to add some code for displaying the state changes.

Cleaning up the Interface

Add the following method to StockItem.swift:

func switchTo(#state : State) {
    self.state = state
    switch state {
    case .empty:
        stockingTimer.hidden = true
        sellButton.hidden = true
        priceTag.hidden = false
    case .stocking:
        stockingTimer.hidden = false
        sellButton.hidden = true
        priceTag.hidden = true
    case .stocked:
        stockingTimer.hidden = true
        sellButton.hidden = false
        priceTag.hidden = true
        progressBar.setProgress(percentage: 1)
    case .selling:
        NSLog("Try to do this last state on your own. You can do it!")
    }
}

This method cleans up the mess that greeted you at first run. It contains one switch statement that distinguishes between the four states of your stock item. For each state, it sets hidden appropriately for the stocking timer, sell button and price tag.

For example, only the price tag should be visible in the empty state; therefore you set the stocking timer and the sell button’s hidden property to true, while you set the price tag’s hidden property to false. The other states follow similar logic.

Eagle-eyed readers will have noticed that the final case of the switch hasn’t been implemented yet. Try to solve it on your own — you should have enough information to do so. Take a peek at the spoiler below to check your work:

Solution Inside SelectShow>

Add the following line to the end of init() in StockItem.swift:

switchTo(state: state)

This will to initialize the game to the default state.

Build and run your project; you see a much cleaner user interface with all stock items in the empty state, displaying their price tag, and waiting for the player to act as shown in the following screenshot:

Bringing order to the chaos is really satisfying!

Bringing order to the chaos is really satisfying!

The kiosk is all ready for some user interaction — fortunately you’re taking care of this next!

Switching States

Add the following method to StockItem.swift:

override func touchesBegan(touches: NSSet, withEvent event: UIEvent)  {
    switch state {
    case .empty:
        var enoughMoney = gameStateDelegate.gameStateDelegateChangeMoneyBy(delta: -stockingPrice * maxAmount)
        if enoughMoney {
            switchTo(state: State.stocking)
        } else {
            var playSound = SKAction.playSoundFileNamed("hit.wav", waitForCompletion: true)
            runAction(playSound)
 
            let rotateLeft = SKAction.rotateByAngle(0.2, duration: 0.1)
            let rotateRight = rotateLeft.reversedAction()
            let shakeAction = SKAction.sequence([rotateLeft, rotateRight])
            let repeatAction = SKAction.repeatAction(shakeAction, count: 3)
            priceTag.runAction(repeatAction)
        }
    case .stocked:
        switchTo(state: State.selling)
    default:
        break
    }
}

The above method operates on the two states that allow user interaction: empty state and the stocked state.

In the empty state, you first notify gameStateDelegate that the player’s money will possibly change. You then checks whether the player has enough money to make the purchase. If so, then call switchTo to change to the stocking state. If not, then let the player know they are short on funds by playing a small sound effect and shaking the price tag.

Handling the stocked state is even easier; you simply call switchTo with the selling state. There are no additional conditions that need to be met for this transition, and as you will see shortly, this puts the item in a state where it will update over time.

Updating States as Time Progresses

So the player pushes the button to buy the item, but…nothing happens!

This is how your player currently feels about your game!

This is how your player currently feels about your game!

Your game should update the item stock as time passes. To do this, you’ll first need to know the last time the state changed — you can use this to calculate how much time has passed, and consequently, how far along the stocking process should be.

Add the following property to StockItem.swift, right after the state property:

private var lastStateSwitchTime : CFAbsoluteTime

You use CFAbsoluteTime above to refer to a specific point in time. Even if the player restarts the game you’ll still need to know exactly when the event happened in order to update the stock properly.

Add the following line to init in StockItem.swift, just before super.init():

lastStateSwitchTime = stockItemData["lastStateSwitchTime"] as AnyObject? as CFAbsoluteTime

This line probably looks familiar — it’s almost the same code that loaded the state of the stock item, but instead loads the time of the last state change.

Add the following line to data() in StockItem.swift, right before the return statement:

data["lastStateSwitchTime"] = lastStateSwitchTime

This line adds an entry for the last state switch time to the data dictionary stored in gamedata.plist.

Now you need to make sure that lastStateSwitchTime is assigned the proper value while the game is running.

Add the following line of code to the beginning of switchTo() in StockItem.swift:

if self.state != state {
  lastStateSwitchTime = CFAbsoluteTimeGetCurrent()
}

This ensures that you’ve actually changed states. If so, then update lastStateSwitchTime to the current time. You can always get the current time using the ever-helpful CFAbsoluteTimeGetCurrent().

Stocking Your Items

Now that you know the absolute time of the last state switch, you can use it to show some progress indicators to your player. You’ll start by updating the countdown that shows the player how long they need to wait for a purchased stock item to restock.

Add the following method to StockItem.swift:

func updateStockingTimerText() {
    var stockingTimeTotal = CFTimeInterval(Float(maxAmount) * stockingSpeed)
    let currentTime = CFAbsoluteTimeGetCurrent()
    var timePassed = currentTime - lastStateSwitchTime
    var stockingTimeLeft = stockingTimeTotal - timePassed
    stockingTimer.text = String(format: "%.0f", stockingTimeLeft)
}

In this method you set the text of the stockingTimer to the time remaining until stocking is complete. First, you calculate the amount of time it takes to fully stock the item. You do so by multiplying stockingSpeed and the maximal amount of the stock item and then cast it to CFTimeInterval. Next, you store the current time in a temporary variable to calculate how much time has passed since the last state change.

The time to restock the item is now simply the total time minus the time that has passed to this point, as demonstrated in the following image:

This time bar shows you the different intervals and absolute times you are using.

This time bar shows you the different intervals and absolute times you are using.

Finally, you set set the text to the remaining time so the user can see when the item will be fully stocked. Since you only want to display whole seconds to your player you use the format specifier %.0f, which tells Swift to display a float variable with zero digits after the decimal.

Now you need a method to update the display of the stock item during stocking and selling.

Add the following method to StockItem.swift:

func update() {
    let currentTimeAbsolute = CFAbsoluteTimeGetCurrent()
    var timePassed = currentTimeAbsolute - lastStateSwitchTime
    switch (state) {
    case .stocking:
        updateStockingTimerText()
        amount = min(Int(Float(timePassed) / stockingSpeed), maxAmount)
        if amount == maxAmount {
            switchTo(state: .stocked)
        }
    default:
        break
    }
}

First, you calculate how much time has passed since the last state switch. Then you switch states depending on the current state of the stock item. If the state is stocking, you call the helper method updateStockingTimerText you created earlier.

Next, you update the item amount which is simply the time elapsed divided by the stocking speed. Of course, the player can never stock more items than maxAmount, so you use min to limit the amount to maxAmount. Finally, you check whether the new amount is equal to maxAmount. If so, then change the state to stocked.

Note: It’s much better to calculate the amount of items based on the amount of time elapsed like you’ve done here, rather than the naive approach periodically incrementing a count.

This is because it will make certain game behaviors (such as calculating the game’s state after the player returns to your game after a long time) much easier, as you’ll see later in this tutorial.

The only thing left to do is call update() for every stock item.

Override update() in GameScene.swift as follows:

override func update(currentTime: CFTimeInterval) {
    for stockItem in stockItems {
        stockItem.update()
    }
}

update() is a SpriteKit method defined on every SKScene and called for every frame. Here you iterate over all stock items and call update() on each.

Build and run your project; tap on a stock item and you’ll see the timer count down slowly to zero.

Your player suddenly got a whole lot more patient.

Your player suddenly got a whole lot more patient.

When the countdown reaches zero, the stock item switches to the state stocked where the item appears as full with a coin displayed in front.

Countdown

The coin indicates that the items are ready to sell – so let’s work on that next!

Selling Your Items

Stocking is, of course, only half of the timed interactions in your game. As soon as the item is fully stocked, you can start selling your wares.

Add the following code to update() in StockItem.swift, inside the case statement right before the default case:

case .selling:
    let previousAmount = amount
    amount = maxAmount - min(maxAmount, Int(timePassed / Double(sellingSpeed)))
    var amountSold = previousAmount - amount
    if amountSold >= 1 {
        gameStateDelegate.gameStateDelegateChangeMoneyBy(delta: sellingPrice * amountSold)
        progressBar.setProgress(percentage: Float(amount) / Float(maxAmount))
        if amount <= 0 {
            switchTo(state: .empty)
        }
    }

First, you store the current amount of the item in previousAmount. You then calculate the new amount by subtracting the quotient of timePassed and sellingSpeed from maxAmount. Again, you need to limit the number of items that can be sold to maxAmount. Now the number of items sold is simply is the difference between the previous amount and the new amount.

In order to limit the number of calls to progressBar and gameStateDelegate, you check whether at least one item has been sold since the last call to update. If so, notify gameStateDelegate about the change in the player’s funds, then set the progress bar to the value of the amount sold divided by the maximum amount available.

Finally, you check whether the stock item sold out by comparing the amount remaining to 0. If you’re sold out of the item, set the state back to empty. Your state machine is now complete!

Build and run, buy some cookies to sell, and then click the coin to start selling. You will see your cookies sell over time, increasing your money accordingly:

w00t I sold a cookie - I'm rich!

w00t I sold a cookie – I’m rich!

Note: Waiting several minutes until something happens might get a bit annoying. As a developer, you’re allowed to cheat a little. Simply change TimeScale in Constants.swift to something less than 1 and things will run a lot faster.

Introducing Your Customers

In order to spice things up, many waiting games have events that trigger at random points in time. For example, in Tiny Towers, specialists make an appearance from time to time to dramatically boost progress.

You’ll implement a similar concept for the Kooky Kiosk. Your player will have to serve demanding customers that appear randomly at the kiosk. Serving these customers will let the player earn money.

Add the following property to GameScene.swift, right below the moneyLabel property:

var customer : Customer?

This stores the current customer using the Customer class already implemented in the starter project. For the moment you’ll only serve one customer at a time.

Now you need to handle the timing of your customers’ arrivals.

Add the following properties to GameScene.swift, right below the customer property:

var timeOfLastCustomer : CFAbsoluteTime = CFAbsoluteTimeGetCurrent()
var timeTillNextCustomer : CFTimeInterval = CFTimeInterval(Float((arc4random() % 15 + 15)) * TimeScale)

The initial time the last customer appeared will be the startup time of the app since no customers have appeared yet. You also store a time interval that indicates how many seconds it will take for the next customer to appear. For this, you use a random value between 15 and 30 seconds. You then multiply this interval by TimeScale to control the rate at which customers appear.

Add the following code to the end of update() in GameScene.swift:

// 1 Check whether it is time for a customer to appear
let currentTimeAbsolute = CFAbsoluteTimeGetCurrent()
if customer == nil && currentTimeAbsolute - timeOfLastCustomer > timeTillNextCustomer {
    // 2 Make a list of potential wishes the customer could have
    var potentialWishes = [StockItem]()
    for stockItem : StockItem in stockItems {
        if stockItem.state == State.selling || stockItem.state == State.stocked {
            potentialWishes.append(stockItem)
        }
    }
    // 3 Select one of the potential wishes randomly and spawn the customer with it
    if potentialWishes.count > 0 {
        var random = arc4random() % UInt32(potentialWishes.count)
        var randomStockItem = potentialWishes[Int(random)]
        customer = Customer(type: randomStockItem.type, flavor: randomStockItem.flavor)
        customer!.position = CGPoint(x: frame.size.width + customer!.calculateAccumulatedFrame().size.width / 2, y: customer! .calculateAccumulatedFrame().size.height / 2)
        // 4 Animate the customer
        var moveLeft = SKAction.moveBy(CGVector(dx: -customer!.calculateAccumulatedFrame().size.width, dy: 0), duration: 1)
        customer?.runAction(moveLeft)
        addChild(customer!)
    }
}

This is a lot of code, but the logic is quite straightforward. Taking each numbered comment in turn:

  1. First you check how much time has passed since the last customer appeared. If it’s greater than the generated time interval, it’s time to spawn a new customer.
  2. Your customers have very specific wishes. To keep the game fun, these wishes are limited to the types and flavors of items that you currently have available — that is, items that are either fully stocked or are currently not sold out. Add all items that match this criteria to the list of potential wishes.
  3. Select a random index of the stock items from the list of potential wishes, then create a new customer wish from the type and flavor of the randomly selected item.
  4. Finally, make the customer appear from the right border of the screen. Using a simple SKAction you move it from the outside of the screen just until it’s entirely on screen.

Build and run your app; when you have available items, a customer will appear randomly and place an order at your kiosk.

Serve me, serve me NOOOOOOW!

Serve me, serve me NOOOOOOW!

You’ll have to disappoint this customer, unfortunately, since you have no way to fulfill their wish at this point. You’d better add this functionality and hope your customer’s bark is worse than their bite!

Declare the following method in GameStateDelegate.swift:

func gameStateServeCustomerWithItemOfType(#type: String, flavor: String)

At this point XCode will display the error message Type ‘GameScene’ does not conform to protocol ‘GameStateDelegate’.

To fix the error, implement the missing method in GameScene.swift as follows:

func gameStateServeCustomerWithItemOfType(#type: String, flavor: String) {
    // 1 Check if the player has served the correct item for the customer
    if customer?.type == type && customer?.flavor == flavor {
        gameStateDelegateChangeMoneyBy(delta: 50)
        var playSound = SKAction.playSoundFileNamed("coin.wav", waitForCompletion: true)
        runAction(playSound)
    } else {
        var playSound = SKAction.playSoundFileNamed("hit.wav", waitForCompletion: true)
        runAction(playSound)
    }
    if customer != nil {
        // 2 Clean up customer
        var moveRight = SKAction.moveBy(CGVector(dx: customer!.calculateAccumulatedFrame().size.width, dy: 0), duration: 1)
        customer!.runAction(moveRight, completion:{
            self.customer?.removeFromParent()
            self.customer = nil
            })
        // 3 Setup spawn of next customer
        timeOfLastCustomer = CFAbsoluteTimeGetCurrent()
        timeTillNextCustomer = CFTimeInterval(Float((arc4random() % 15 + 15)) * TimeScale)
    }
}

Again, this is a fair bit of code, but it’s relatively simple when explained out:

  1. You first check if the type and the flavor of the stock item correspond to the customer’s wish. If so, add $50 to the player’s funds and play a sound effect. Otherwise, play a sound effect indicating that you haven’t satisfied this customer’s wish. Play this sound as well if there’s no customer at the current time.
  2. Next, remove the customer sprite using an instance of SKAction that moves the customer off to the right and off the screen. As soon as the customer sprite is off the screen, remove the sprite from the scene and set it to nil.
  3. As soon as the customer leaves the scene you also need to schedule the time when the next customer will arrive. Set the time of the last customer to the current time. The interval until the next customer appears will again be a random value between 15 and 30 seconds.

All that’s left here is to call your new method from touchesBegan() in StockItem.swift like so (add this inside the case statement right before the default case):

        case .selling:
            gameStateDelegate.gameStateServeCustomerWithItemOfType(type: type, flavor: flavor)

To try this out, build and run and build some cookies. When a customer arrives, tap the cookies once to start selling, and then again to give a cookie to the customer.

Now you can serve your customers in record time — and rack up the profits while you’re at it!

Serving your customers quickly is the key to success!

Serving your customers quickly is the key to success!

Sending Notifications

Your game looks and plays great — but eventually the player will leave the game and you’ll want to draw them back to play some more. You can send updates about changes in the game state to your player using notifications.

Notifications can do the following:

  • Display a short text message
  • Play a brief sound
  • Set the badge on the app icon

Here’s what your notifications will look like at the moment they’re sent:

Receiving a notification

Receiving a notification

But what if the player isn’t looking at their the device when the notification is received? Don’t despair! Players can see a list of missed notifications by pulling down the context menu from the top like so:

Hey you missed something!

Hey you missed something!

And even if the player doesn’t open the context menu, you can still show them that there is something happening in the game by updating the badge on the icon, like so:

Pick me!

Pick me!

Time to draw your user back into the game — head into the next section to learn how to add these notifications to your game.

Comparing Local and Remote Notifications

There are two different ways to inform players about background changes in an app: local notifications and push notifications.

While both look and sound the same, there are major differences from the viewpoint of a developer:

  • Local Notifications are triggered directly on the device. They are very easy to implement, but the number of scheduled messages is limited to 64 at one time.
  • Push Notifications are triggered by a remote server and pushed to the device. Hence they are more complicated to implement. The number of scheduled messages is not limited.

For your Kookie Kiosk game local notifications will be more than sufficient. Even if you schedule a notification for each of the seven items in your kiosk, you’ll still have 57 notifications you could send. You’re barely making a dent at this point. :]

Note: If you’re interested in how to use push notifications have a look at the tutorial on this site covering Apple Push Notification Services.

Asking for User Permission

Before you can send any notifications to your players, you need to ask for their permission — otherwise the notifications won’t be displayed. You could do this anywhere in your application, but it’s most useful to do it at start up.

Open AppDelegate.swift and replace the application:didFinishLaunchingWithOptions method with the following code:

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
    // Override point for customization after application launch.
    let notificationSettings = UIUserNotificationSettings(forTypes: UIUserNotificationType.Alert | UIUserNotificationType.Sound | UIUserNotificationType.Badge, categories: nil)
    UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
    return true
}

You call this method on application launch. First, you define UIUserNotificationSettings to support alerts combined with sound and the privilege to set the app badge. Then you register those settings with your UIApplication using registerUserNotificationsSettings.

Build and run, and you’ll see the following dialog appear:

NotificationPermission

Tap OK to allow notifications. (If you tap Don’t Allow, notifications would not appear of course).

Note that after the first run, this dialog won’t show up again. Instead, the app will use the value from the Settings app, where the user can change the rights granted to your app at any time as shown below:

The player can change the rights granted to your application at any time.

The player can change the rights granted to your application at any time.

Scheduling Notifications

Equipped with the necessary rights, you are now free to schedule notifications. As most of your notifications are similar in structure, you’ll create a small helper method to schedule a notification.

Add the following method to GameScene.swift:

func scheduleNotificationWith(#message: String, intervalInSeconds: NSTimeInterval, badgeNumber: Int) {        
    // 1 Create empty notification
    var localNotification = UILocalNotification()
 
    // 2 Calculate notification time using NSDate
    var now = NSDate()
    var notificationTime = now.dateByAddingTimeInterval(intervalInSeconds)
 
    // 3 Set properties of your notification
    localNotification.alertBody = message
    localNotification.fireDate = notificationTime
    localNotification.timeZone = NSTimeZone.defaultTimeZone()
    localNotification.applicationIconBadgeNumber = badgeNumber
    localNotification.soundName = UILocalNotificationDefaultSoundName
 
    // 4 Schedule the notification
    UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}

The above method builds the notification from a message, a time interval, and the updated badge number as follows:

  1. First, you create an empty notification using UILocalNotification().
  2. Next, calculate the time of the notification. You need to store the time as an NSDate. You can easily get the current time stamp using the empty constructor of NSDate, but to get an NSDate object for a specific time in the future you can use dateByAddingTimeInterval which adds a time interval in seconds to another NSDate object.
  3. Now that you know the message, time, and badge number, simply set all those properties accordingly on your application. While you’re at it, set soundName to UILocalNotificationDefaultSoundName.
  4. Finally, you can schedule the notification! Simply call scheduleLocalNotification() on the singleton instance of UIApplication.

Calling your new scheduleNotificationWith(message:intervalInSeconds:badgeNumber:) method lets you easily schedule one local notification. However, you’ll want to schedule a notification for the state change of every stock item.

For this, you’ll need two things: a notification text and the time to show the notification.

Add the following method to StockItem.swift:

func notificationMessage() -> String? {
    switch state {
    case .selling:
        return NSString(format: "Your %@ %@ sold out! Remember to restock.", flavor, type)
    case .stocking:
        return NSString(format: "Your %@ %@ is now fully stocked and ready for sale.", flavor, type)
    default:
        return nil
    }
}

In the above method you implement a switch on the state of the stock item. Then for each state you formulate a message that gives details about the state switch and about the flavor and type of item, that has been affected.

There are four states in your app: empty, stocking, stocked, and selling, but only two of them — selling and stocking — are dependant on time. The other two states depend on user interaction, so they don’t need scheduled notifications.

Ready for a challenge? Implement the method func notificationTime() -> NSTimeInterval that calculates the time until the next state switch. You should be able to do this with your current knowledge on NSTimeInterval. If you are stuck, feel free to peek at the hints below before you look at the solution!

Solution Inside: Hints SelectShow>

Still stuck? Want to check your solution for correctness? Here you go:

Solution Inside: Solution SelectShow>

With all this groundwork done you can now schedule a notification for every stock item.

Add the following method to GameScene.swift:

func scheduleNotifications() {
    let itemsSortedByNotificationTime = stockItems.sorted({$0.notificationTime() < $1.notificationTime()})
    var count = 1
    for stockItem in itemsSortedByNotificationTime {
        let notificationMessage = stockItem.notificationMessage()
        if notificationMessage != nil {
            scheduleNotificationWith(message: notificationMessage!, intervalInSeconds: stockItem.notificationTime(), badgeNumber: count)
            count++
        }
    }
}

First, you sort the notifications by their notificationTime. Why is the order relevant? You can use it to manage the badge number, since this doesn’t happen automatically. Next you iterate over the list of stock items; for each item you retrieve the appropriate notification message. If the message is not nil, then schedule the notification. With every notification you send, increase the count of the badge number accordingly.

This finishes off the method that schedules a notification for every stock item. You still need a way to call it when the app enters the background. There is only one tiny problem here: only the AppDelegate knows that your app is entering the background, but it doesn’t know about your GameScene.

A great solution for this problem is to use NSNotificationCenter, which provides you with a mechanism to broadcast information within your app.

Open AppDelegate.swift add the following code to applicationDidEnterBackground():

NSNotificationCenter.defaultCenter().postNotificationName("scheduleNotifications", object: nil)

This will broadcast out a notification through the NSNotificationCenter when your app enters the background state. All you need to do now is listen for this notification.

Open GameScene.swift and add the following code to the end of didMoveToView():

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "scheduleNotifications", name: "scheduleNotifications", object: nil)

This registers the GameScene as an observer; you tell NSNotificationCenter to call scheduleNotifications when an event with the name “scheduleNotifications” triggers.

Build and run, start making some cookies, and hit Command-L a few times to go to the lock screen. When the cookies finish baking, you should see a notification appear on the screen:

NotificationOnLockScreen

You can swipe the notification to return to the app, and see your finished cookies!

AfterNotification

Resetting the App Badge

You’re nearly done — all that’s left to do is cancel all notifications and set the number of the badge to zero when the player resumes their game. You don’t want to pester the player with the same information twice.

Open AppDelegate.swift and add the following two lines of code to applicationDidBecomeActive():

UIApplication.sharedApplication().cancelAllLocalNotifications()
UIApplication.sharedApplication().applicationIconBadgeNumber = 0

The first line cancels all notifications that have not triggered since the last time you started the game. The second line sets applicationIconBadgeNumber to zero.

And that’s it — your kiosk game is complete!

Where to Go From Here?

You can download the completed project for this tutorial here.

This game is a great foundation on which to build future waiting-style games. Here are a few other fun features you could add to your app:

  • Charge the player pay rent for the kiosk.
  • Add more types and flavors of stock items.
  • Let items rot if the player doesn’t sell them in a timely fashion to his customer.
  • Add a patience level to your Customer object — if a customer isn’t served in time send them angrily away.

Have fun playing with the project! I am sure you will find fun ways to extend the game or to use the techniques learned here in your own games.

If you have any questions or comments about this tutorial, please come join the forum discussion below!

How to Make a Waiting Game Like Farmville with Sprite Kit and Swift is a post from: Ray Wenderlich

The post How to Make a Waiting Game Like Farmville with Sprite Kit and Swift appeared first on Ray Wenderlich.


Video Tutorial: Introduction to Unity Part 11: Animation

Sprite Kit and Inverse Kinematics with Swift

$
0
0
Learn how to implement Inverse Kinematics with Sprite Kit!

Learn how to implement inverse kinematics with Sprite Kit!

Note from Ray: This is a brand new Swift tutorial released as part of the iOS 8 Feast. Enjoy!

If you’ve ever added an animated character to a game, you probably pre-created animations for running, punching, jumping and so forth, and played them when the game executes the associated actions.

While this “canned” approach works for simple scenarios, there are often cases where you’d prefer your character to interact more realistically with other game objects.

For instance, if your character needs to reach out to pick up a nearby object at a variable height, you may not want to use the same animation for every picking action, or it could look really silly when the predefined hand motions don’t line up exactly with the object’s position!

In iOS 8, Sprite Kit comes with inverse kinematics, a technique that alleviates this issue by allowing you to procedurally generate joint motions such as reaching out to a point in space. This means you don’t have to resort to making a possibly infinite number of animations to reach every possible position.

In this tutorial, you’ll learn how to use Sprite Kit’s inverse kinematics feature to implement dynamic punches and kicks in a fun and simple ninja game. :]

In particular, you’ll learn how to:

  • Set up joint hierarchies in the new Sprite Kit Scene Editor.
  • Define joint constraints for more realistic joint behavior.
  • Dynamically animate selected joint hierarchies such that the terminal bones can reach out to various positions in 2D space.

Let’s get moving!

Note: This Swift tutorial assumes you have working knowledge of Sprite Kit and Swift. If you’re new to Sprite Kit, check out our Sprite Kit Swift Tutorial for Beginners or our full book, iOS Games by Tutorials. For an introduction to Swift, check out this beginner Swift tutorial.

Getting Started

Download the starter project, open it in Xcode and build and run it. You’ll see a static ninja already set up in the middle of the scene, begging to be released from his yoga stretch.

Introducing a new iOS Framework: YogaKit!

Introducing a new iOS Framework: YogaKit!

Take some time to familiarize yourself with the structure of the sample project. In particular, select GameScene.sks in your project navigator. The file should open up in the integrated Sprite Kit Scene Editor, a new feature in Xcode 6 and iOS 8.

editor

Note: Xcode’s Scene Editor provides a convenient user interface for initializing a scene by allowing you to visually manipulate your sprites and their properties such as position, scale and physics bodies. Later, you can create SKScene objects by unarchiving the .sks files you configured in the Scene Editor.

In the following steps, you’ll take advantage of the Scene Editor to set up and tweak the behavior of the joint hierarchy for your ninja, which would be difficult to visualize and set up programmatically.

Overview of Skeletal Hierarchy

In the scene, you can see that the ninja already has a basic skeleton comprised of a hierarchy of bones, each represented by a sprite node. The nodes are connected in a tree of parent-child relationships, as depicted below:

Skeletal Hierarchy of Ninja

The lower torso node, in red, is the root parent of the entire skeleton hierarchy, which in turn is a child of the scene itself.

The anchor point for each sprite node acts as a joint connecting the node with its parent. These have been adjusted with an offset so that one end of the node can rotate naturally about the joint.

Setting up a Rest Pose

While the ninja looks cool in that mid-air pose, you unfortunately aren’t building a game involving levitation, so you’ll have to bring him back to earth.

Let’s start by adjusting the legs so they touch the shadow on the ground. In the scene, select the front upper leg node, leg_upper_front, by clicking on it.

Selecting the upper front leg node

Set the rotation of leg_upper_front to -14. You can do this either by rotating the handle of the selected sprite or by setting the Rotation property manually in the editor, as shown below:

Angles

Note: Some of the other values (such as position) may be slightly different for you than this screenshot; just make sure the Rotation is -14 and you should be good!

Next, select leg_lower_front and set its rotation to -9 to keep it slightly bent backwards.

Moving on to the back leg nodes, set the rotations of leg_upper_back and leg_lower_back to 22 and -30, respectively. You should see the following:

Adjusting the legs

Now that your ninja is finally standing, let’s work on the arms. Set the rotation angles of both upper arm nodes, arm_upper_front and arm_upper_back, to -10. Also, set the rotation angles of the lower arm nodes, arm_lower_front and arm_lower_back, to 130.

Note: Selecting a particular node can be tricky, especially when it’s obscured by other nodes. One way around this is to right-click on the obscuring node(s) and select Lock Node Selection. This locks the obscuring node, allowing you to select what’s behind it.

A second way, which I find more effective, is to use the jump bars running across the top of the Editor to navigate the scene graph hierarchy and select the node you want, as shown below:

Navigating the Scene Graph

You should see the following:

Rest Pose

Finally, you’re going to do some housekeeping to ensure that the ninja and the shadow always stays centralized, regardless of the screen size. (Currently, the ninja would be off-center on the iPad 2.)

Switch to GameScene.swift and add the following code within and at the top of the GameScene class:

//1
var shadow: SKNode!
var lowerTorso: SKNode!

In didMoveToView(), add the following code:

//2
lowerTorso = childNodeWithName("torso_lower")
lowerTorso.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame) - 30)
 
//3
shadow  = childNodeWithName("shadow")
shadow.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame) - 100)

Let’s go through what you just did:

  1. You create two SKNode properties to reference the shadow node and lower torso node (the root node of the ninja), respectively.
  2. You obtain a reference to the lower torso node by its name, “torso_lower”, and assign its value to the lowerTorso property. Next, you set its position to the center of the screen with an offset of -30 units.
  3. Similarly, you grab a reference to the shadow node by its name, “shadow”, and assign its value to the shadow property. Finally, you set its position to the center of the screen with an offset of -100 units.

Build and run the project, and you’ll see your ninja in its rest stance, ready for a fight!

Ninja is ready!

With the joint hierarchy all set up and your ninja ready for action, you can begin using Sprite Kit’s inverse kinematics feature to deploy some serious martial arts.

What Is Inverse Kinematics?

Before you continue, it’s important to appreciate what inverse kinematics is all about, so the following is a quick primer to get you started.

Forward Kinematics

Forward Kinematics

Fundamentally, kinematics is the study of the motion of an object through space and time.

For an animated character represented by a skeletal hierarchy, forward kinematics is about how the tip of the last bone in a joint hierarchy (called the end-effector) moves in space when you rotate each bone lying along the chain leading up to it.

Take the example of an arm. If I rotate my upper arm about the shoulder joint in combination with extending my lower arm from my elbow joint, I would be able to geometrically determine the new position of my hand by means of forward kinematics.

Note: End-effector is a term commonly used in robotics and character animation to describe the point at the end of a skeleton chain that interacts with the environment.

Inverse Kinematics

Inverse Kinematics

However, it’s often useful to look at the positioning of the joint hierarchy from the reverse point of view.

For example, given a desired goal position of the end-effector, in this case my hand, I would solve for all the joint angles along the chain leading up to the end-effector that would allow me to achieve the desired hand position.

This approach, as you might have guessed already, is known as inverse kinematics, and it’s particularly valuable for achieving goal-oriented actions such as touching, kicking or punching at various points in space. One of the most common uses of inverse kinematics in 3D games is to adjust a character’s limbs to position its feet properly when walking over uneven terrain.

In Sprite Kit, simply specifying a single end-effector goal position in 2D space allows you to position the entire joint hierarchy automatically, as opposed to the forward kinematics approach of fiddling with multiple joint angles.

For the purposes of this tutorial, that’s all the background you need to get started. If you’re interested in delving deeper into the mathematics of inverse kinematics, I highly recommend reading this and this.

In the meantime, let’s learn what you can do with Sprite Kit’s inverse kinematics feature!

Inverse Kinematics Actions

Let’s bring the ninja to life by making his front arm reach out to a position upon a tap event.

Add the following properties to the GameScene class:

var upperTorso: SKNode!
var upperArmFront: SKNode!
var lowerArmFront: SKNode!

These properties will hold references to the upper torso, upper arm and lower arm nodes, respectively.

Next, add the following code at the end of didMoveToView():

upperTorso = lowerTorso.childNodeWithName("torso_upper") 
upperArmFront = upperTorso.childNodeWithName("arm_upper_front")
lowerArmFront = upperArmFront.childNodeWithName("arm_lower_front")

Here, you traverse down the parent-child hierarchy manually from the lower torso to the right lower arm, grabbing the appropriate child nodes by their node names and storing them in the variables you declared earlier.

Defining an Inverse Kinematics Action

Next, add the following methods to the GameScene class:

func punchAtLocation(location: CGPoint) {
  // 1
  let punch = SKAction.reachTo(location, rootNode: upperArmFront, duration: 0.1)
  // 2
  lowerArmFront.runAction(punch)
}
 
// 3
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
  for touch: AnyObject in touches {
    let location = touch.locationInNode(self)
    punchAtLocation(location)
  }
}

There are a few important things going on here, so let’s run through them step by step:

  1. reachTo() is a brand new Sprite Kit action in iOS 8 responsible for performing inverse kinematics actions for a joint hierarchy reaching out to a point in space.


    It takes in three arguments: (1) the desired end position to reach; (2) the highest node of the hierarchy that you want to rotate; and (3) the duration of the animation.


    In your case, you set your root node to the upper arm node, since you want to move the upper arm and its descendant nodes. The animation will last for 0.1 seconds.

  2. Note: reachTo() can take a maximum velocity as an argument instead of an animation duration. In addition, there is a variant method, reachToNode(), that takes in a node instead of a position. For more details, refer to the documentation.

  3. Next, you run the action on the end node that is going to reach out to touch the end position in space, which in this case is the lower arm.
  4. Upon a touch event, you run the punch action with the tap location as the end position that you want the lower arm to reach.

Build and run the project. Tap on various locations near the ninja, and you’ll see the arm joints move in an attempt to reach to the tap position.

reachToNode action

This ninja looks like he has some mental problems!

Setting Up an End-Effector

While the arm joints move following your taps at various locations, there is something fundamentally strange about the actions—it seems the elbow is reaching out to the desired point instead of the fist! Why would that be happening?

Recall that each bone node has an anchor point that has been adjusted with an offset so that it’s flush with one end of the node, enabling it to rotate about its parent.

In your case, when you set the inverse kinematics action to run on the lower arm node, Sprite Kit uses the anchor point as the node reference point or end-effector. The lower arm’s anchor point is the elbow, so this is why the elbow seems to be the point reaching out to the target position.

Let’s fix this by attaching a child end-effector “fist” node at the lower tip of the lower arm.

Since the fist is already visually included with the lower arm, this node will simply be a point node without a texture.

Switch to GameScene.sks. Select the Object Library at the bottom of the Utilities pane in Xcode. From the Object Library, drag an Empty Node onto the front lower arm in the scene. Make sure to align the node with the furthest tip of the hand, as shown below:

Drag an Empty Node

With the new node selected, set its name to fist_front and its Parent to arm_lower_front in the SKNode Inspector on the right.

Setting the Node Properties

Sprite Kit will add the fist as a child, as shown below:

End-effector added

In terms of joint hierarchy, the entire front arm now looks like this: arm_upper_front (root) -> arm_lower_front -> fist_front (end-effector).

Switch back to GameScene.swift. Add the following line near the top of the GameScene class:

var fistFront: SKNode!

In addition, add the following line to the end of didMoveToView():

fistFront = lowerArmFront.childNodeWithName("fist_front")

Finally, replace the following line in punchAtLocation():

lowerArmFront.runAction(punch)

With this:

fistFront.runAction(punch)

Now that you’ve switched the end-effector node to the fist node, build and run the project. Try tapping various locations again, and you’ll see your ninja reaching out with his fist instead of his elbow. It’s a karate chop!

IKReach

Defining Joint Constraints

While tapping at various locations, you may have noticed that inverse kinematics attempts to solve for the set of joint angles that will get your end-effector as near to the target position as possible.

In some cases, however, you may get strange-looking artifacts like the following:

That looks painful!

That looks painful!

In the case above, while the joint angles indeed enable the fist to achieve the target position (illustrated by a red dot), Sprite Kit has bent the elbow joint beyond its realistic limit. Ouch!

This highlights one of the key characteristics of the inverse kinematics problem: Given a desired end-effector position, there could be more than one set of joint positions that will get the end-effector to the goal position. Imagine trying to reach out to touch a nearby object—I’m sure you can find more than one way to twist or extend your upper or lower arm and yet still touch the object!

Fortunately, you can guide the inverse kinematics engine to favor more realistic joint configurations by defining joint constraints to limit joint angles to a suitable lower and upper range.

You’ll define joint constraints for the lower arms so they don’t over-extend, but you’ll continue to allow the upper arms to rotate through the default range of 0-360 degrees.

Switch to GameScene.sks. Select the arm_lower_front node. Under IK Constraints in the SKNode Inspector on the right, keep the Min Angle‘s value 0 and set the Max Angle to 160, as shown below:

Setting IK Constraints

This ensures that the zRotation values of the lower arm node don’t cross the lower limit of 0 degrees (lower arm fully extended) and the upper limit of 160 degrees (lower arm fully retracted, with a gap angle of 20 degrees between the upper and lower arm).

Select the arm_lower_back node and repeat the same steps to enforce similar constraints.

Now, build and run the project. Tap on various locations to test the new constraints. No more over-extension!

Creating a Punching Motion

At this point, your ninja can reach out to any arbitrary tap position, but you know ninjas can do better than that. In this section, you’ll equip your ninja with the ability to punch at a location. Oooofff!

Start by adding the following properties to GameScene inside GameScene.swift:

let upperArmAngleDeg: CGFloat = -10
let lowerArmAngleDeg: CGFloat = 130

The upperArmAngleDeg and lowerArmAngleDeg properties hold the default rest pose angle values of the upper arm and lower arm joints, respectively. Recall that these are the same values you defined earlier when you set up a rest pose for the ninja.

Finally, replace punchAtLocation with the following implementation:

func punchAtLocation(location: CGPoint) {
  // 1
  let punch = SKAction.reachTo(location, rootNode: upperArmFront, duration: 0.1)
 
  // 2
  let restore = SKAction.runBlock {
    self.upperArmFront.runAction(SKAction.rotateToAngle(self.upperArmAngleDeg.degreesToRadians(), duration: 0.1))
    self.lowerArmFront.runAction(SKAction.rotateToAngle(self.lowerArmAngleDeg.degreesToRadians(), duration: 0.1))
  }
 
  // 3
  fistFront.runAction(SKAction.sequence([punch, restore]))
}

That’s quite a bit of code, so let’s dissect it line by line:

  1. The punch action is the same reaching action you defined before—it animates the joint hierarchy to reach out to a desired position.
  2. The restore action animates the restoration of the joint hierarchy to its rest pose. Unfortunately, inverse kinematics actions don’t have reversed actions, so here, you resort to using the traditional forward kinematics to rotate the joint angles back to their rest angles.


    Within this action, you run rotateToAngle() actions concurrently on both the upper and lower arm nodes to restore them to their rest angles as defined by upperArmAngleDeg and lowerArmAngleDeg, respectively. degreesToRadians is defined in a category on CGFloat to convert values between degrees and radians. You can find the function in CGFloat+Extensions.swift, included in the SKTUtils source folder.

  3. Finally, you concatenate the two actions, punch and restore, into a sequenced action, which you then run on the fist.

Build and run the project. The ninja will now punch to wherever you tap!

punching

Punching With Both Fists

Now that your ninja can punch, wouldn’t it be cool if he could use both arms? In this section, you’ll give him the ability to alternate between his left and right fists for consecutive punches.

To do so, you’ll first need to set up the back arm the same way you did the front arm.

Switch to GameScene.sks and add a fist end-effector as a child to arm_lower_back.

Name the fist fist_back. If you’ve set this up properly, you should see the following:

Adding a back fist

Next, switch to GameScene.swift and add the following properties to GameScene:

var upperArmBack: SKNode!
var lowerArmBack: SKNode!
var fistBack: SKNode!

Next, add the following code at the end of didMoveToView():

upperArmBack = upperTorso.childNodeWithName("arm_upper_back")
lowerArmBack = upperArmBack.childNodeWithName("arm_lower_back")
fistBack = lowerArmBack.childNodeWithName("fist_back")

The three properties now hold references to all the back arm nodes.

You want to alternate which fist punches, so add the following property to GameScene:

var rightPunch = true

This Boolean’s role is to keep track of whether it’s time to punch using the left or the right fist.

Finally, you’re going to do a little refactoring of punchAtLocation() into two smaller functions to neatly handle punching with either set of arm nodes.

Replace punchAtLocation() with the following code:

// 1
func punchAtLocation(location: CGPoint, upperArmNode: SKNode, lowerArmNode: SKNode, fistNode: SKNode) {
  let punch = SKAction.reachTo(location, rootNode: upperArmNode, duration: 0.1)
  let restore = SKAction.runBlock {
    upperArmNode.runAction(SKAction.rotateToAngle(self.upperArmAngleDeg.degreesToRadians(), duration: 0.1))
    lowerArmNode.runAction(SKAction.rotateToAngle(self.lowerArmAngleDeg.degreesToRadians(), duration: 0.1))   
  }
 
  fistNode.runAction(SKAction.sequence([punch, restore]))
}
 
func punchAtLocation(location: CGPoint) {
  // 2  
  if rightPunch {
    punchAtLocation(location, upperArmNode: upperArmFront, lowerArmNode: lowerArmFront, fistNode: fistFront)
  }
  else {
    punchAtLocation(location, upperArmNode: upperArmBack, lowerArmNode: lowerArmBack, fistNode: fistBack) 
  }
  // 3
  rightPunch = !rightPunch
}

Let’s go through the essence of what’s happening here:

  1. This first function is similar to the version you had previously, except that it now lets you specify the arm nodes as arguments. This enables you to use the same function for both the left and right arms.
  2. In the second function, you simply check if it’s time to use the left or the right arm based on the value of the rightPunch Boolean, and execute the actions accordingly.
  3. Finally, you toggle the rightPunch flag such that when the function is called again on the next tap, the Boolean flag is flipped accordingly, allowing you to alternate between the two arms.

Build and run the project. Your ninja will punch with both fists!

Punching with two fists

Facing the Target

You may have noticed that your ninja isn’t quite as adept at handling target positions behind him.

iOS Simulator Screen Shot Sep 28, 2014, 9.05.16 PM

That looks more like a dance move than a punch! Since this isn’t a dance simulator (although that sounds like a fun game idea!), let’s fix this by making the ninja always face the target position.

In touchesBegan(), add the following code just before the line that calls punchAtLocation():

lowerTorso.xScale = 
  location.x < CGRectGetMidX(frame) ? abs(lowerTorso.xScale) * -1 : abs(lowerTorso.xScale)

If you detect the tap on the left half of the screen, you negate the scale of the ninja (rooted at the lower torso), which has the effect of flipping the entire ninja horizontally.

Build the project again and run it. Upon a tap, the ninja will now face the direction of the tap location.

iOS Simulator Screen Shot Sep 28, 2014, 9.00.51 PM

As a bonus, you can make the ninja behave more even realistically by using Sprite Kit’s SKConstraints to make his head track the target.

SKConstraints are a new feature introduced in Sprite Kit in iOS 8. They allow you to ensure that certain relationships are true between nodes, before a scene is rendered.

For example, you can set up a constraint to make sure a node stays within a certain rectangle, to orient one node toward another, or to limit the z rotation of a node. You will combine a few constraints here to easily make the ninja’s face look toward where he’s punching.

Begin by adding the following properties to GameScene:

var head: SKNode!
let targetNode = SKNode()

Next, add the following code to the bottom of didMoveToView():

head = upperTorso.childNodeWithName("head")

This grabs a reference to the head node and stores it in head.

Now, add the following line to the end of the for loop in touchesBegan, just after the call to punchAtLocation():

targetNode.position = location

targetNode now stores the location of the latest tap location.

Finally, you’ll make use of SKConstraints to ensure that the head node is always oriented so that it “looks” at the target node. Add the following code at the end of didMoveToView():

// 1
let orientToNodeConstraint = SKConstraint.orientToNode(targetNode, offset: SKRange(constantValue: 0.0))
// 2
let range = SKRange.rangeWithLowerLimit(CGFloat(-50).degreesToRadians(), 
                                        upperLimit: CGFloat(80).degreesToRadians())
// 3
let rotationConstraint = SKConstraint.zRotation(range)
// 4
rotationConstraint.enabled = false
orientToNodeConstraint.enabled = false
// 5
head.constraints = [orientToNodeConstraint, rotationConstraint]

Let’s go through the code line by line:

  1. You create an orientToNode constraint, passing in targetNode as the node toward which to orient.
  2. Here, you define an angle range from -50 degrees to 80 degrees, converted to radians.
  3. You define a rotation constraint that limits the zRotation property of the head node to the angle range defined in step 2.
  4. You disable the two constraints by default, as there may not be any target node yet.
  5. Finally, you add the two constraints to the head node.

To get the constraints to work, you’ll enable the constraints upon the first tap. Add the following property to GameScene:

var firstTouch = false

Next, add the following code to the beginning of touchesBegan():

if !firstTouch {
  for c in head.constraints! {
    var constraint = c as SKConstraint
    constraint.enabled = true
  }
  firstTouch = true
}

On the very first tap event, the code above loops through the list of constraints defined for the head node and enables all of them. It then sets the firstTouch flag to true, which ensures that the code won’t be executed again on subsequent taps.

Build and run the project once more. Voila! Your ninja now maintains constant eye contact with his target position as he punches.

puncheverywhere

Hitting Moving Targets

At this point, your ninja is raring for “real” things to punch, so let’s spawn some flying shurikens from either side of the screen for the ninja to hit.

Begin by adding the following function to the GameScene class:

func addShuriken() {
  // 1
  let shuriken = SKSpriteNode(imageNamed: "projectile")
  // 2 
  let minY = lowerTorso.position.y - 60 + shuriken.size.height/2
  let maxY = lowerTorso.position.y  + 140 - shuriken.size.height/2
  let rangeY = maxY - minY
  let actualY = (CGFloat(arc4random()) % rangeY) + minY
  // 3
  let left = arc4random() % 2
  var actualX = (left == 0) ? -shuriken.size.width/2 : size.width + shuriken.size.width/2
  // 4 
  shuriken.position = CGPointMake(actualX, actualY)
  shuriken.name = "shuriken"
  shuriken.zPosition = 1
  addChild(shuriken)
  // 5 
  let minDuration = 4.0
  let maxDuration = 6.0
  let rangeDuration = maxDuration - minDuration
  let actualDuration = (Double(arc4random()) % rangeDuration) + minDuration
  // 6 
  let actionMove = SKAction.moveTo(CGPointMake(size.width/2, actualY), duration: actualDuration)
  let actionMoveDone = SKAction.removeFromParent()
  shuriken.runAction(SKAction.sequence([actionMove, actionMoveDone]))
  // 7  
  let angle = left == 0 ? CGFloat(-90).degreesToRadians() : CGFloat(90).degreesToRadians()
  let rotate = SKAction.repeatActionForever(SKAction.rotateByAngle(angle, duration: 0.2))
  shuriken.runAction(SKAction.repeatActionForever(rotate))
}

Let’s briefly run through the code in this function:

  1. You create a brand new sprite node from the projectile.png image.
  2. You set the spawn height of the shuriken to a value between 60 units below and 130 units above the lower torso. This ensures the shuriken will be within reach of the ninja.
  3. You set the x-position of the shuriken to be either slightly left or slightly right of the screen.
  4. You then set the position of the shuriken based on the values determined in steps 2 and 3. You also assign the hard-coded name “shuriken” to the node before adding it as a child to the scene.
  5. You randomize the move duration of the shuriken to be between 4 and 6 seconds to add some sense of variance to the game.
  6. You define a sequence of two actions to run on the shuriken. The first action moves the shuriken toward the center of the screen based on the duration defined in step 5. The second action removes the shuriken once it reaches the center of the screen.
  7. Concurrently, you rotate the shuriken continuously in the direction of its motion for a more realistic effect.

You’ll want to spawn shurikens periodically for the life of the game. Add the following properties to GameScene to keep track of the time between spawns:

var lastSpawnTimeInterval: NSTimeInterval = 0
var lastUpdateTimeInterval: NSTimeInterval = 0

Then add the following methods to GameScene, as well:

func updateWithTimeSinceLastUpdate(timeSinceLast: CFTimeInterval) {
  lastSpawnTimeInterval = timeSinceLast + lastSpawnTimeInterval
  if lastSpawnTimeInterval > 0.75 {
    lastSpawnTimeInterval = 0
    addShuriken()   
  }
}
 
override func update(currentTime: CFTimeInterval) {
  var timeSinceLast = currentTime - lastUpdateTimeInterval
  lastUpdateTimeInterval = currentTime
  if timeSinceLast > 1.0 {
    timeSinceLast = 1.0 / 60.0
    lastUpdateTimeInterval = currentTime
  }
  updateWithTimeSinceLastUpdate(timeSinceLast)
}

updateWithTimeSinceLastUpdate adds the time since the last update to lastSpawnTimeInterval. Once it is greater than 0.75 seconds, you call addShuriken() to spawn a new shuriken and reset the time. You call this method inside update() each frame, with a bit of math to ensure the time between frames doesn’t get out of hand.

This code is similar to the monster-spawning code in the Sprite Kit Swift Tutorial for Beginners on this site. You can refer to that tutorial if you’d like more details.

Build and run the project, and you’ll see shurikens fly in at your ninja from both sides of the screen!

Shurikens

The shurikens look dangerous, but they simply run into the ninja as if he weren’t even there. Not to worry; you’ll fix this by adding some code to check for possible intersections between the end-effector and any flying shuriken.

Add the following function to the GameScene class:

func intersectionCheckActionForNode(effectorNode: SKNode) -> SKAction {
  let checkIntersection = SKAction.runBlock {
 
    for object: AnyObject in self.children {
      // check for intersection against any sprites named "shuriken"
      if let node = object as? SKSpriteNode {
        if node.name? == "shuriken" {
          if node.intersectsNode(effectorNode) {
            // play a hit sound
            self.runAction(SKAction.playSoundFileNamed("hit.mp3", waitForCompletion: false))
 
            // show a spark effect
            let spark = SKSpriteNode(imageNamed: "spark")
            spark.position = node.position
            spark.zPosition = 60
            self.addChild(spark)
            let fadeAndScaleAction = SKAction.group([
              SKAction.fadeOutWithDuration(0.2),
              SKAction.scaleTo(0.1, duration: 0.2)])
            let cleanUpAction = SKAction.removeFromParent()
            spark.runAction(SKAction.sequence([fadeAndScaleAction, cleanUpAction]))
 
            // remove the shuriken
            node.removeFromParent()
          }
          else {
            // play a miss sound
            self.runAction(SKAction.playSoundFileNamed("miss.mp3", waitForCompletion: false))
          }
        }
      }
    }
  }
  return checkIntersection
}

intersectionCheckActionForNode() takes in an end-effector node and returns an action that runs a block; you’ll see in the next section why you define the function in this manner. Within this block, you check to see if the end-effector intersects with any shurikens present in the scene.

In the event of an intersection, you play a “hit” sound, show a little spark effect and remove the shuriken sprite from the scene. Otherwise, you simply play a “miss” sound.

Next, replace the last line of punchAtLocation(_:upperArmNode:lowerArmNode:fistNode:) with the two lines below:

let checkIntersection = intersectionCheckActionForNode(fistNode)
fistNode.runAction(SKAction.sequence([punch, checkIntersection, restore]))

You’ve simply added an additional action right after the punch action, which runs a block to check for possible intersections of the fist node with the shurikens in the scene.

Since you perform the intersection test exactly at the moment when the reaching motion is complete, your ninja has to be rather precise when executing a punch, which should be the case for all ninjas anyway. :]

Build and run the project. Your ninja can now handle those shurikens like a boss!

punchall1

The shurikens still don’t hurt the ninja; you’ll implement that in a later section.

Creating a Kicking Motion

Your ninja is now a punching machine, but there’s no doubt he can do more to showcase his well-roundedness. Let’s equip him with the ability to kick at shurikens below a certain height.

You’ll begin by setting up the leg joint hierarchy for inverse kinematics. In particular, you’ll:

  • Define an end-effector node for the back leg, which will be the leg the ninja will use for kicking.
  • Set up joint constraints for each back leg joint.
  • Define inverse kinematics actions for the joint hierarchy to reach the tap location for taps made below a certain height.

These steps are similar to what you’ve done for the arms, so let’s get on with it!

Switch to GameScene.sks. Drag an Empty Node onto the back lower leg (leg_lower_back) in the scene. Make sure to align the node with the farthest tip of the foot. Keeping the node selected, set its name to foot_back and its parent to leg_lower_back in the SKNode Inspector on the right. Once done, you’ll have something like this:

foot-effector

Next, you’ll set the IK Constraints for the leg’s nodes.

Don’t do this yet; I want to explain things first.

For leg_upper_back, you’ll constrain the rotation angle to between -45 and 160 degrees, as illustrated below:

upperconstraint

As for leg_lower_back, you’ll constrain the rotation angle to between -45 and 0 degrees, as shown below:

lowerconstraint

Strangely, Scene Editor only allows positive values for the min and max angles. It also doesn’t allow the min angle to be larger than the max angle, which means it would consider a normalized range of 315 (-45) to 160 for the upper leg to be invalid, as well. Nonetheless, you will overcome this limitation by defining the constraints programmatically.

Now you can go ahead and implement this.

Inside GameScene.swift, add the following properties to GameScene:

var upperLeg: SKNode!
var lowerLeg: SKNode!
var foot: SKNode!

And add the following code to initialize these new properties in didMoveToView():

upperLeg = lowerTorso.childNodeWithName("leg_upper_back")
lowerLeg = upperLeg.childNodeWithName("leg_lower_back")
foot = lowerLeg.childNodeWithName("foot_back")
 
lowerLeg.reachConstraints = SKReachConstraints(lowerAngleLimit: CGFloat(-45).degreesToRadians(), upperAngleLimit: 0)
upperLeg.reachConstraints = SKReachConstraints(lowerAngleLimit: CGFloat(-45).degreesToRadians(), upperAngleLimit: CGFloat(160).degreesToRadians())

In the code above, you obtain references to the three leg nodes and save them in their respective properties. You then set the reachConstraints property of the lower and upper legs to the limits described previously. That’s about it!

Next, you’ll define a function that runs a kicking action on the foot node. Before you do so, add the following properties to GameScene:

let upperLegAngleDeg: CGFloat = 22
let lowerLegAngleDeg: CGFloat = -30

These two properties hold the rest angles of the upper and lower leg joints, respectively.

Next, add the following function to GameScene:

func kickAtLocation(location: CGPoint) {
  let kick = SKAction.reachTo(location, rootNode: upperLeg, duration: 0.1)
 
  let restore = SKAction.runBlock {
    self.upperLeg.runAction(SKAction.rotateToAngle(self.upperLegAngleDeg.degreesToRadians(), duration: 0.1))
    self.lowerLeg.runAction(SKAction.rotateToAngle(self.lowerLegAngleDeg.degreesToRadians(), duration: 0.1))
  }
 
  let checkIntersection = intersectionCheckActionForNode(foot)
 
  foot.runAction(SKAction.sequence([kick, checkIntersection, restore]))
}

This function is similar to the one you constructed for the arms, except it’s tailored for the leg nodes. Notice how you’re able to reuse intersectionCheckActionForNode(), this time for the foot end-effector node.

Finally, you’ll run the leg action for tap locations below a certain height. Within the for loop in touchesBegan(), replace the following line:

punchAtLocation(location)

With the code below:

let lower = location.y < lowerTorso.position.y + 10
if lower {
  kickAtLocation(location)
}
else {
  punchAtLocation(location)
}

Here, you simply run the kicking action if the tap position is below the lower torso’s height plus 10 units; otherwise you do the usual punching action.

Build and run the project. Your ninja can now dynamically punch and kick shurikens that are within range!

kick

Finishing Touches

You are almost done! Let’s tweak this project to make it into a playable game. To spice things up, you’ll give the ninja three lives, make him take damage from missed shurikens and allow him to earn points for each shuriken he hits.

Add the following properties to GameScene:

var score: Int = 0
var life: Int = 3

These properties will store the score and number of lives remaining, respectively.

Add the following lines after the code above:

let scoreLabel = SKLabelNode()
let livesLabel = SKLabelNode()

You’ll use these label nodes to display the score and remaining lives, respectively.

Next, add the following code to didMoveToView() to set up the properties of the label nodes and add them to the scene:

// setup score label
scoreLabel.fontName = "Chalkduster"
scoreLabel.text = "Score: 0"
scoreLabel.fontSize = 20
scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
scoreLabel.verticalAlignmentMode = SKLabelVerticalAlignmentMode.Top
scoreLabel.position = CGPoint(x: 10, y: size.height -  10)
addChild(scoreLabel)
 
// setup lives label
livesLabel.fontName = "Chalkduster"
livesLabel.text = "Lives: 3"
livesLabel.fontSize = 20
livesLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Right
livesLabel.verticalAlignmentMode = SKLabelVerticalAlignmentMode.Top
livesLabel.position = CGPoint(x: size.width - 10, y: size.height - 10)
addChild(livesLabel)

With the label nodes set up, add the following lines within the innermost if block in intersectionCheckActionForNode(), right before the line node.removeFromParent():

self.score++
self.scoreLabel.text = "Score: \(Int(self.score))"

This increments the score by 1 whenever the ninja successfully destroys a shuriken with a punch or a kick.

Now, let’s handle the lives. Every time a shuriken hits the ninja, you’ll decrement the ninja’s life by 1. If he has no remaining lives, you’ll show a “Game Over” screen briefly and restart the scene.

Begin by creating a new scene to display the “Game Over” message. Create a new file with the iOS\Source\Swift class template, name the file GameOverScene, click Next and then click Create.

Replace the contents of GameOverScene.swift with the following code:

import SpriteKit
 
class GameOverScene: SKScene {
  override func didMoveToView(view: SKView) {
 
    let myLabel = SKLabelNode(fontNamed:"Chalkduster")
    myLabel.text = "Game Over"
    myLabel.fontSize = 65
    myLabel.position = CGPoint(x:CGRectGetMidX(frame), y:CGRectGetMidY(frame))
    addChild(myLabel)
 
    runAction(SKAction.sequence([
      SKAction.waitForDuration(1.0),
      SKAction.runBlock({
        let transition = SKTransition.fadeWithDuration(1.0)
        let scene = GameScene(fileNamed:"GameScene")
        let skView = self.view! as SKView
        scene.scaleMode = .AspectFill
        scene.size = skView.bounds.size
        self.view?.presentScene(scene, transition: transition)
      })]))
  }
}

The code above displays a label showing a “Game Over” message. It then runs an action on the scene that presents a new GameScene with a fading transition after a delay of one second.

Now, switch back to GameScene.swift. In addShuriken(), add the following code right after the line that creates actionMoveDone:

let hitAction = SKAction.runBlock({
  // 1
  if self.life > 0 {
    self.life--
  }
  // 2
  self.livesLabel.text = "Lives: \(Int(self.life))"
 
  // 3      
  let blink = SKAction.sequence([SKAction.fadeOutWithDuration(0.05), SKAction.fadeInWithDuration(0.05)])
 
  // 4
  let checkGameOverAction = SKAction.runBlock({
    if self.life <= 0 {
      let transition = SKTransition.fadeWithDuration(1.0)
      let skView = self.view! as SKView
      let gameOverScene = GameOverScene.sceneWithSize(skView.bounds.size)
      self.view?.presentScene(gameOverScene, transition: transition)
    }
  })
  // 5
  self.lowerTorso.runAction(SKAction.sequence([blink, blink, checkGameOverAction]))
})

In the code you just added, you create an additional action to be run when the shuriken reaches the center of the screen. The action runs a block that does the following:

  1. It decrements the number of lives.
  2. It accordingly updates the label depicting the number of lives remaining.
  3. It defines a blink action with fade-in and fade-out durations of 0.05 seconds each.
  4. It defines another action running a block that checks if the number of remaining lives has hit zero. If so, the game is over, so the code presents an instance of GameOverScene.
  5. It then runs the actions in steps 3 and 4 in sequence on the lower torso, the root of the ninja.

Finally, add hitAction to the sequence of actions you run on each shuriken. Replace the following line in addShuriken:

shuriken.runAction(SKAction.sequence([actionMove, actionMoveDone]))

With this:

shuriken.runAction(SKAction.sequence([actionMove, hitAction, actionMoveDone]))

Build and run the project. You’ll see the new number of lives and score labels. In addition, your ninja is no longer immune, which makes the game a bit more challenging!

finalscreen

Gratuitous Background Music

Last but not least, every ninja game needs good background music, so switch to GameViewController.swift and add the following import at the top of the file:

import AVFoundation

This gives you access to the AVFoundation framework, which is necessary to play background music.

Add the following property to the GameViewController class:

var audioPlayer: AVAudioPlayer?

Next, you’ll write a function that plays your background music. Add the following function to the GameViewController class:

func startBackgroundMusic() {  
  if let path = NSBundle.mainBundle().pathForResource("bg", ofType: "mp3") {
    audioPlayer = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: path), fileTypeHint: "mp3", error: nil)
    if let player = audioPlayer {
      player.prepareToPlay()
      player.numberOfLoops = -1
      player.play()
    }
  }
}

The function looks for the background music file bg.mp3 in the resource path, loads it into memory and plays it indefinitely.

Finally, add the following line to the bottom of viewDidLoad() in GameViewController to play the music upon loading the main view:

startBackgroundMusic()

That’s it! Build and run the project, and strike shurikens to the beat of your new background music.

Ninja

Where to Go From Here?

You can download the complete project for this tutorial here.

At this point, you should have a solid grasp of the key concepts of inverse kinematics in Sprite Kit.

Inverse kinematics is a powerful technique that brings a new level of dynamism to traditional character animation. There are many possible goal-oriented scenarios where inverse kinematics would apply an additional touch of realism—from having a snake whip its neck around to face the user, to a goalkeeper adjusting both hands to catch an oncoming ball, to a wizard dexterously waving a wand in mid-air—it’s all up to your imagination!

In case you’re wondering, 3D inverse kinematics is also available in Scene Kit, Apple’s 3D Graphics API. The feature allows you to blend inverse kinematics on top of existing animations, bringing an even greater sense of realism.

If you want to learn more about creating Sprite Kit games like this, check out our book iOS Games by Tutorials.

I hope you enjoyed this tutorial, and I look forward to seeing how you use inverse kinematics to spice up character animations in your own games!

Credits: Art by Vicki Wenderlich from gameartguppy.com. Background music from Kevin MacLeod. Hit Sound Effect from Mike Koenig, and Miss Sound Effect from Mark DiAngelo.

Sprite Kit and Inverse Kinematics with Swift is a post from: Ray Wenderlich

The post Sprite Kit and Inverse Kinematics with Swift appeared first on Ray Wenderlich.

iOS 8 Handoff Tutorial

$
0
0
Learn how to add handoff to your iOS 8 apps!

Learn how to add handoff to your iOS 8 apps!

Note from Ray: This is an abbreviated version of a chapter from iOS 8 by Tutorials released as part of the iOS 8 Feast to give you a sneak peek of what’s inside the book. We hope you enjoy!

Handoff is a new feature in iOS 8 and OS X Yosemite. Handoff lets you continue an activity uninterrupted when you switch from one device to another – without the need to reconfigure either device.

You can add handoff feature to your iOS and OS X apps in iOS 8 and Yosemite. In this tutorial you’ll learn the basic workings of Handoff and how to use it on iOS with a non-document-based app.

Handoff Overview

Before you dive into the code, it’s important to get a high level overview of some important Handoff concepts:

  • Getting Started: Learn if your devices are Handoff-compatible and run a basic test to make sure things are working.
  • User Activities: Learn about the core unit of work with Handoff: user activities.
  • Activity Types: Learn how you can set an “activity type” on a user activity and what that means.

Getting Started

Not only can Handoff transfer your current activity from an iOS to an OS X device, but it can also transfer your current activity between iOS devices.

At the time of writing this tutorial, Handoff does not work on the iOS simulator. Therefore, to follow along with this tutorial, you will need two Handoff-compatible iOS devices.

Device Compatibility: iOS

To check whether your iOS device is Handoff compatible, go to Settings and select General from the list. If you see Handoff & Suggested Apps in the list, your device is Handoff compatible. The following screenshots show General settings menu of an iPhone 5s (Handoff compatible) and an iPad 3rd Gen (not Handoff compatible), respectively.

iPod Touch 5th Gen Settings (Handoff compatible) and iPad 3rd Gen Settings (not Handoff compatible)

Handoff functionality depends on few things:

  1. An iCloud account: You must be logged in to the same iCloud account on each device you wish to use Handoff.
  2. Bluetooth LE 4.0: Handoff broadcasts activities via Bluetooth LE signals, so both the broadcasting and receiving devices must have Bluetooth LE 4.0 support.
  3. iCloud paired: Devices should have been already paired through iCloud. When you sign into your iCloud account on Handoff compatible devices, each device is paired with other Handoff compatible devices. This is where the magic happens.

At this time, make sure that you have two Handoff compatible devices running iOS 8 or later that are logged onto the same iCloud account.

User Activities

Handoff is based on the concept of a user activity, which is a stand-alone collective unit of information that can be handed off without any dependencies on any other information.

The NSUserActivity class represents an instance of a user activity. It encapsulates the state of the application in a way that can be continued on other devices in a related application.

There are three ways to interact with NSUserActivity objects:

  1. Create user activity: The originating app creates an NSUserActivity and calls becomeCurrent() on it to start the broadcasting process. Here’s a quick example:
    let activity = NSUserActivity(activityType: "com.razeware.shopsnap.view")
    activity.title = "Viewing"
    activity.userInfo = ["shopsnap.item.key": ["Apple", "Orange", "Banana"]]
    self.userActivity = activity;
    self.userActivity?.becomeCurrent()

    You can use the userInfo dictionary of NSUserActivity to pass native data types or NSCoding-compliant custom objects to the receiving device. Native data types include NSArray, NSData, NSDate, NSDictionary, NSNull, NSNumber, NSSet, NSString, NSUUID, and NSURL. Passing NSURL can be a bit tricky; check out the Best Practices section of this tutorial before using NSURL with Handoff.

  2. Update user activity: Once an instance of NSUserActivity becomes current, the OS periodically invokes updateUserActivityState(activity:) on your topmost view controller to give you a chance to update the user activity. Here’s an example:
    override func updateUserActivityState(activity: NSUserActivity!) {
      let activityListItems = // ... get updated list of items
      activity.addUserInfoEntriesFromDictionary(["shopsnap.item.key": activityListItems])
      super.updateUserActivityState(activity)
    }

    Notice that you don’t set userInfo to a new dictionary or update it directly. Instead, you should use the convenience method addUserInfoEntriesFromDictionary().

    Later in this tutorial, you’ll learn how to force the user activity to refresh on demand, or how to get a similar callback at the app delegate level.

  3. Receive user activity: When your receiving app launches with a user activity from Handoff, the app delegate calls application(:willContinueUserActivityWithType:). Note this method is not passed an instance of NSUserActivity, because it takes a while until Handoff downloads and transfers the NSUserActivity data to your app.

    Later on, the following delegate callback invokes once the user activity has been downloaded:

    func application(application: UIApplication!, 
                     continueUserActivity userActivity: NSUserActivity!,
                     restorationHandler: (([AnyObject]!) -> Void)!) 
                     -> Bool {
     
      // Do some checks to make sure you can proceed
      if let window = self.window {
        window.rootViewController?.restoreUserActivityState(userActivity)
      }
      return true
    }

    You can then use the data stored in the NSUserActivity object to re-create the user’s activity. This is where you will update your app so that it can continue the associated activity.

Activity Types

When you create a user activity, you must specify an activity type for the activity. An activity type is simply a unique string, usually in reverse DNS notation, like com.razeware.shopsnap.view.

Each app that is capable of receiving a user activity must declare the activity types that it will accept. This is much like declaring the URL schemes your app supports. For non-document based apps, activity types are listed under the NSUserActivityTypes key at the top level of Info.plist as shown below:

Setting NSUserActivityTypes in the Info.plist of a non-document based app

For an app to support a given activity, there are three requirements:

  • Same team. Both apps must originate from the same developer with the same developer Team ID.
  • Same activity type. The receiving app must have a NSUserActivityTypes entry for the activity type created by the sending app.
  • Be signed. Both apps must be distributed through the App Store or be signed with a Developer certificate.

Now that you’ve learned about the basics of user activities and activity types, let’s dive in to an example!

The Starter Project

Start by downloading the starter project for this Handoff tutorial. Once you’ve downloaded it, open up the project in Xcode and run it in an iPhone simulator.

App Screenshots

It is called ShopSnap. You can build a simple shopping list in this app. A shopping item is represented by a String and you store the shopping list as an Array of strings. Tapping the + button adds a new item to the list and swiping removes an item.

You’ll define two distinct user activities for this app:

  • Viewing the list. If the user is currently viewing the list, you’ll Handoff the entire array of items.
  • Adding or editing an item. If the user is currently adding a new item, you’ll Handoff an “edit” activity for a single item instead.

Setting Your Team

For Handoff to work, both the sending and receiving app must be signed by the same team. Since this app is both the sender and the receiver, this is simple!
Select your ShopSnap project, and in the general tab, switch the Team to your team:

Setting your team

Build and run on one of your Handoff-compatible iOS devices to make sure it runs OK, then continue on.

Configuring Activity Types

The next step is to configure the activity types your app supports. Open Supporting Files\Info.plist and click on the + button that appears next to Information Property List to add a new item under the Information Property List dictionary:

Configuring activity types

Enter NSUserActivityTypes for the key name and make it an Array type, as shown below:

Enter NSUserActivityTypes

Add two items under NSUserActivityTypes (Item 0 and Item 1) and set their types to String. Enter com.razeware.shopsnap.view for Item 0, and com.razeware.shopsnap.edit for Item 1.

Add two items under NSUserActivityTypes

These are arbitrary activity types specific and unique to your app. Since you’ll refer to them from multiple places in the app, it’s good practice to add them as constants in a separate file.

Right-click on the ShopSnap group in the project navigator, select New File \ iOS \ Source \ Swift File. Name the class Constants.swift and ensure your new class is added to the ShopSnap target.

Add the following code to your class:

let ActivityTypeView = "com.razeware.shopsnap.view"
let ActivityTypeEdit = "com.razeware.shopsnap.edit"
 
let ActivityItemsKey = "shopsnap.items.key"
let ActivityItemKey  = "shopsnap.item.key"

Now you can use these constants for the two different activity types. You’ve also defined some constants for the keys you’ll be using in the user activity’s userInfo dictionary for convenience.

Quick End-to-End Test

Let’s run a quick end-to-end test to make sure that your devices can communicate properly.

Open ListViewController.swift and add the following two functions:

// 1.
func startUserActivity() {
  let activity = NSUserActivity(activityType: ActivityTypeView)
  activity.title = "Viewing Shopping List"
  activity.userInfo = [ActivityItemsKey: ["Ice cream", "Apple", "Nuts"]]
  userActivity = activity
  userActivity?.becomeCurrent()
}
 
// 2.
override func updateUserActivityState(activity: NSUserActivity!) {
  activity.addUserInfoEntriesFromDictionary([ActivityItemsKey: ["Ice cream", "Apple", "Nuts"]])
  super.updateUserActivityState(activity)
}

This is a quick test where you hard code a user activity, to make sure that you can receive it OK on the other end.

Here is what the code above does:

  1. startUserActivity() is a helper function that creates an instance of NSUserActivity with a hardcoded shopping list. Then it starts broadcasting that activity by calling becomeCurrent().
  2. After you call becomeCurrent(), OS will periodically call updateUserActivityState(). UIViewController inherits this method from UIResponder, and you should override this to update the state of your userActivity here.
    Here you update the shopping list with the same hardcoded values as before, since this is just a test. Note that addUserInfoEntriesFromDictionary is the preferred way of mutating userInfo dictionary of NSUserActivity. You should always call super.updateUserActivityState() at the end.

Now you just need to start this method. Add the following line to the beginning of viewDidLoad():

startUserActivity()

That’s the minimum you need to start broadcasting – let’s move on to receiving. Open AppDelegate.swift and add the following code:

func application(application: UIApplication!, 
                 continueUserActivity userActivity: NSUserActivity!, 
                 restorationHandler: (([AnyObject]!) -> Void)!) 
                 -> Bool {
 
  let userInfo = userActivity.userInfo as NSDictionary
  println("Received a payload via handoff: \(userInfo)")
  return true
}

This method on AppDelegate is called when everything goes well and a userActivity is successfully transferred. Here you log a message with the userInfo dictionary of the userActivity. You return true to indicate you handled the user activity.

Let’s try this out! There’s a little bit of coordination required to get this working on two devices, so follow along carefully.

  1. Install and run the app on your first device.
  2. Install and run the app on your second device. Make sure that you are debugging the app in Xcode so you can see your println() output.
  3. Note: It is also useful to have the console logs open in this step so you can see any output from iOS, in case there is an issue with the connection. To see the console logs, go to Window\Devices, select your device, and select the icon in the lower left to expand the console area.

    Console Log

  4. Put the second device to sleep by pressing the power button. On the same device, press the Home button. If everything works fine, you should see the ShopSnap app icon appear at the left bottom corner of the screen. From there you should be able to launch the app, and see the log message in Xcode’s console:
Received a payload via handoff: {
    "shopsnap.items.key" = (
    "Ice cream",
    Apple,
    Nuts
  );
}

If you don’t see the app icon on the lock screen, close and re-open the app on the originating device. This forces the OS to restart broadcasting again. Also check the device console to see if there are any error messages from Handoff.

Lock screen

Creating the View Activity

Now that you have a basic working Handoff app, it is time to extend it. Open ListViewController.swift and update startUserActivity() by passing the actual array of items instead of hardcoded values. Update the method to the following:

func startUserActivity() {
  let activity = NSUserActivity(activityType: ActivityTypeView)
  activity.title = "Viewing Shopping List"
  activity.userInfo = [ActivityItemsKey: items]
  userActivity = activity
  userActivity?.becomeCurrent()
}

Similarly, update updateUserActivityState(activity:) in ListViewController.swift to pass the array of items instead of hardcoded values:

override func updateUserActivityState(activity: NSUserActivity!) {
  activity.addUserInfoEntriesFromDictionary([ActivityItemsKey: items])
  super.updateUserActivityState(activity)
}

Note: Whenever updateUserActivityState(activity:) is called, the userInfo dictionary is usually empty. You don’t have to empty the dictionary, just update it with appropriate values.

Now, update viewDidLoad() in ListViewController.swift to start the userActivity after successfully retrieving items from previous session (and only if it’s not empty), as follows:

override func viewDidLoad() {
  title = "Shopping List"
  weak var weakSelf = self
  PersistentStore.defaultStore().fetchItems({ (items:[String]) in
    if let unwrapped = weakSelf {
      unwrapped.items = items
      unwrapped.tableView.reloadData()
      if items.isEmpty == false {
        unwrapped.startUserActivity()
      }
    }
  })
  super.viewDidLoad()
}

Of course, if the app starts with an empty list of items, now the app will never start broadcasting the user activity. You need to fix this by starting the user activity once the user adds an item to the list for the first time.

To do this, update the implementation of the delegate callback detailViewController(controller:didFinishWithUpdatedItem:) in ListViewController.swift as follows:

func detailViewController(#controller: DetailViewController,
                          didFinishWithUpdatedItem item: String) {
    // ... some code
    if !items.isEmpty {
      startUserActivity()
    }
}

There are three possibilities here:

  • The user has updated an existing item.
  • The user has deleted an existing item.
  • The user has added a new item.

The existing code handles all possibilities; you only need to add the check to start an activity if there is a non-empty list of items.

Build and run on both devices again. At this point you should be able to add a new item on one device and then hand it over to the other device!

Finishing Touches

When user starts adding a new item or editing an existing item, the user is not technically viewing the list of items. So you want to stop broadcasting current activity. Similarly, there is no reason to continue broadcasting it all the items in the list are deleted. Add the following helper method in ListViewController.swift:

func stopUserActivity() {
  userActivity?.invalidate()
}

In stopUserActivity(), you invalidate the existing NSUserActivity. This makes Handoff stop broadcasting.

With stopUserActivity() in place, it is time to call it from appropriate places.

Update implementation of prepareForSegue(segue:, sender:) in ListViewController.swift and as follows:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    // ... some code
    stopUserActivity()
}

When the user selects a row or taps the Add button, ListViewController prepares to segue to detail view. You invalidate the current list-viewing activity.

Still in the same file, update the implementation of tableView(_:commitEditingStyle:forRowAtIndexPath:) as follows:

override func tableView(tableView: UITableView, 
                        commitEditingStyle editingStyle: UITableViewCellEditingStyle,
                        forRowAtIndexPath indexPath: NSIndexPath) {
  // ... some code
  if items.isEmpty {
    stopUserActivity()
  } else {
    userActivity?.needsSave = true
  }
}

When the user deletes an item from the list you need to update the user activity accordingly. If all the items have been removed from the list, you stop broadcasting. Otherwise you set needsSave to true on the userActivity. When you do that, the OS immediately calls back on updateUserActivityState(activity:), where you update userActivity.

To wrap up this section, there is a situation where the user has just returned from DetailViewController by tapping the Cancel button. This triggers an exit segue. You need to re-start the userActivity. Update the implementation of unwindDetailViewController(unwindSegue:) as follows:

@IBAction func unwindDetailViewController(unwindSegue: UIStoryboardSegue) {
  // ... some code
  startUserActivity()
}

Build and run and verify that everything works fine so far. Try adding a few items to the list and verify they pass between devices.

Creating the Edit Activity

Now you need to take care of DetailViewController in a similar fashion. This time, however, you’ll broadcast a different activity type.

Open DetailViewController.swift and modify textFieldDidBeginEditing(textField:) as follows:

func textFieldDidBeginEditing(textField: UITextField!) {
  // Broadcast what we have, if there is anything!
  let activity = NSUserActivity(activityType: ActivityTypeEdit)
  activity.title = "Editing Shopping List Item"
  let activityItem = (countElements(textField.text!) > 0) ? textField.text : ""
  activity.userInfo = [ActivityItemKey: activityItem]
  userActivity = activity
  userActivity?.becomeCurrent()
}

The above method creates an “Editing” activity with the current contents of the item’s string.

As user continues editing the item you need to update the user activity accordingly. Still in DetailViewController.swift, update the implementation of textFieldTextDidChange(notification:) as shown below:

func textFieldTextDidChange(notification: NSNotification) {
  if let text = textField!.text {
    item = text
  }
 
  userActivity?.needsSave = true
}

Now that you have indicated the activity needs to be updated, implement restoreUserActivityState(activity:) to update it whenever the OS asks for it:

override func updateUserActivityState(activity: NSUserActivity!) {
  let activityListItem = (countElements(textField!.text!) > 0) ? textField!.text : ""
  activity.addUserInfoEntriesFromDictionary([ActivityItemKey: activityListItem])
  super.updateUserActivityState(activity)
}

Here you simply update the current item to the text in the text field.

Build and run. At this point if you start adding a new item or editing an existing item on one device, you can hand over the edit process to another device.

Finishing Touches

Since needsSave is a lightweight operation, in the code above you can set it as often as you like and continuously update userInfo with each keypress.

There is one small design detail you may have picked up on. The view controllers are laid out as a split view on the iPad and in landscape mode on the iPhone. It’s possible to switch between items in the list without resigning the keyboard. If that happens, textFieldDidBeginEditing(textField:) won’t be called, resulting in your user activity never being updated to the new text.

To fix this, update item’s didSet observer in DetailViewController.swift as shown below:

var item: String? {
  didSet {
    if let textField = self.textField {
      textField.text = item
    }
    if let activity = userActivity {
      activity.needsSave = true
    }
  }
}

The DetailViewController’s item property is set when the user taps an item in the ListViewController. A simple fix for this situation is to let the view controller know that it has to update the activity when the item changes.

Finally, you’ll need to invalidate userActivity when the user leaves the DetailViewController so the edit activity is no longer broadcasted.

Simply add this line to the beginning of textFieldShouldReturn(_:)in DetailViewController.swift:

userActivity?.invalidate()

Build and run your project to make sure the app still works as usual.
Next, you will handle the incoming activity.

Receiving the Activities

When the user launches your app through Handoff, the app delegate does most of the processing of the incoming NSUserActivity.

Assuming that everything goes well and the data transfers successfully, iOS then calls application(_:continueUserActivity:restorationHandler:). This is your first chance to interact with the NSUserActivity instance.
You already have an implementation in place from previous sections. Update it as follows:

func application(application: UIApplication!, 
                 continueUserActivity userActivity: NSUserActivity!,
                 restorationHandler: (([AnyObject]!) -> Void)!)
                 -> Bool {
 
  if let window = self.window {
    window.rootViewController?.restoreUserActivityState(userActivity)
  }
  return true
}

You pass the userActivity to the rootViewController of the app’s window and return true. This tells the OS you handled the Handoff action successfully. From this point on, you are on your own to forward calls and restore the activity.

The method you call on the rootViewController is restoreUserActivityState (activity:). This is a standard mehod that is decalred at UIResponder level. The OS uses this method to tell a receiver to restore an instance of NSUserActivivty. It is OK for you to call this method and pass on the userActivity.

Your task now is to walk down the view controller hierarchy and pass the activity from the parent to child view controllers until reach the point where the activity is consumed:

image22-69

The root view controller is a TraitOverrideViewController, and its job is to manage the size classes of the application; it won’t be interested in your user activity.
Open TraitOverrideViewController.swift and add the following:

override func restoreUserActivityState(activity: NSUserActivity!) {
  let nextViewController = childViewControllers.first as UIViewController
  nextViewController.restoreUserActivityState(activity)
  super.restoreUserActivityState(activity)
}

Here you grab the first child view controller contained by the TraitOverrideViewController and pass the activity down to it. It’s safe to do this, since you know your app’s view controller will only contain one child.

The next view controller in the hierarchy is a SplitViewController, where things get a little more interesting.
Open SplitViewController.swift and add the following:

override func restoreUserActivityState(activity: NSUserActivity!) {
  // What type of activity is it?
  let activityType = activity.activityType
 
  // This is an activity for ListViewController.
  if activityType == ActivityTypeView {
    let controller = viewControllerForViewing()
    controller.restoreUserActivityState(activity)
 
  } else if activityType == ActivityTypeEdit {
    // This is an activity for DetailViewController.
    let controller = viewControllerForEditing()
    controller.restoreUserActivityState(activity)
  }
 
  super.restoreUserActivityState(activity)
}

SplitViewController knows about both ListViewController and DetailViewController. If the NSUserActivity is a List Viewing activity type, you’ll pass it to ListViewController. However, if it’s an Editing activity type you’ll pass it to DetailViewController.

You’ve passed the activities to all the correct places – now it’s time to get some data from those activities.

Open ListViewController.swift and implement restoreUserActivityState(activity:) as follows:

override func restoreUserActivityState(activity: NSUserActivity) {
  // Get the list of items.
  if let userInfo = activity.userInfo {
    if let importedItems = userInfo[ActivityItemsKey] as? NSArray {
      // Merge it with what we have locally and update UI.
      for anItem in importedItems {
        addItemToItemsIfUnique(anItem as String)
      }
      PersistentStore.defaultStore().updateStoreWithItems(items)
      PersistentStore.defaultStore().commit()
      tableView.reloadData()
    }
  }
  super.restoreUserActivityState(activity)
}

In the above method you finally get to continue a viewing activity. Since you want to maintain a unique list of shopping items, you only add those items that are unique to your local list, then save and update the UI once you’re done.

Build and run. At this point you should be able to see the list of items that are received from another device via Handoff.

Editing activities are handled in a very similar manner. Open DetailViewController.swift and implement restoreUserActivityState(activity:) as follows:

override func restoreUserActivityState(activity: NSUserActivity) {
  if let userInfo = activity.userInfo {
    var activityItem: AnyObject? = userInfo[ActivityItemKey]
    if let itemToRestore = activityItem as? String {
      item = itemToRestore
      textField?.text = item
    }
  }
  super.restoreUserActivityState(activity)
}

This retrieves the information about the edit activity and updates the text field appropriately.
Build and run again to see it in action!

Finishing Touches

When the user indicates that they want to continue a user activity on another device by swiping up on the app icon, the OS launches the corresponding app. Once the app is launched, the OS calls on application(_, willContinueUserActivityWithType:). Open AppDelegate.swift and add the following method:

func application(application: UIApplication,
                 willContinueUserActivityWithType userActivityType: String!)
                 -> Bool {
  return true
}

At this point your app hasn’t yet downloaded the NSUserActivity instance and its userInfo payload. For now, you’ll simply return true. This forces the app to accept the activity each time the user initiates the Handoff process. If you want to alert your user that the activity is on its way, this is the place to do it.

At this point the OS has started transferring data from one device to another. You have already covered the case where everything goes well. But it is conceivable that the Handoff activity will fail at some point.

Add the following method to AppDelegate.swift to handle this case:

func application(application: UIApplication!, 
                 didFailToContinueUserActivityWithType userActivityType: String!,
                 error: NSError!) {
 
  if error.code != NSUserCancelledError {
    let message = "The connection to your other device may have been interrupted. Please try again. \(error.localizedDescription)"
    let alertView = UIAlertView(title: "Handoff Error", message: message, delegate: nil, cancelButtonTitle: "Dismiss")
    alertView.show()
  }
}

If you receive anything except NSUserCancelledError, then something went wrong along the way and you won’t be able to restore the activity. In this case, you display an appropriate message to the user. However, if the user explicitly canceled the Handoff action, then there’s nothing else for you to do here but abort the operation.

Versioning Support

One of the best practices when working with Handoff is versioning. One strategy to deal with this is to add a version number to each handoff that you send, and only accept handoffs from your version number (or potentially earlier). Let’s try this.

Open Constants.swift and add the following constants:

let ActivityVersionKey = "shopsnap.version.key"
let ActivityVersionValue = "1.0"

The above version key and value are arbitrary key-value you picked for this version of the app.

If you recall from the previous section, the OS periodically and automatically calls restoreUserActivityState(activity:). The implementations of this method were very focused and were limited to the scope of the object that implemented it. For example, ListViewController overrode this method to update userActivity with list of all items, whereas DetailViewController overrode to update with the current item that was being edited.

When it comes to something that is generic to your userActivity and applies to all of your user activities regardless, like versioning, the best place to do that is in the AppDelegate.

Whenever restoreUserActivityState(activity:) is called, the OS calls application(application:, didUpdateUserActivity userActivity:) in the app delegate right after that. You’ll use this method to add versioning support to your Handoff.

Open AppDelegate.swift and add the following:

func application(application: UIApplication, 
                 didUpdateUserActivity userActivity: NSUserActivity) {
  userActivity.addUserInfoEntriesFromDictionary([ActivityVersionKey: ActivityVersionValue])
}

Here you simply update the userInfo dictionary with the version of your app.

Still in AppDelegate.swift, update the implementation of application(_:, continueUserActivity: restorationHandler:) as follows:

func application(application: UIApplication!,
                 continueUserActivity userActivity: NSUserActivity!,
                 restorationHandler: (([AnyObject]!) -> Void)!)
                 -> Bool {
 
  if let userInfo: NSDictionary = userActivity.userInfo {
    if let version = userInfo[ActivityVersionKey] as? String {
      // Pass it on.
      if let window = self.window {
        window.rootViewController?.restoreUserActivityState(userActivity)
      }
      return true
    }
  }
  return false
}

Here you do a sanity check on the version of the userAcitivty and pass it on only if it matches the version you know about. Build and run your app once again to ensure the app runs as usual.

Handoff Best Practices

Before you go, I thought I’d leave you with a few thoughts on Handoff best practices.

  • NSURL: Using NSURL in an NSUserActivity userInfo dictionary can be tricky. The only NSURLs you can pass safely in Handoff are web site URLs that use HTTP or HTTPS and iCloud documents. You can’t pass local file URLs as the receiver won’t translate and map the URL properly at the receiver’s end. The best way to achieve file links is to pass a relative path and re-construct your URL on the receiving side.
  • Platform specific values: Avoid using platform specific values like the content offset of a scroll view; it’s always better to use relative landmarks. For example, if your user is viewing some items in a table view, pass the index path of the top most visible item in the table view in your user activity object instead of passing the content offset or visible rect of the table view.
  • Versioning: Think about using versioning and future-proofing updates of your app. You could add new data formats or remove values entirely from your userInfo dictionary in future versions of the app. Versioning gives you more control over how user activities are actioned in current and future versions of your app.

Where To Go From Here?

Here is the final example project that you developed in the above tutorial.

If you’re curious to learn more about Handoff, streaming, and document based Handoff, be sure to check out Apple’s Handoff Programming Guide for more information now that you know the basics.

If you enjoyed this tutorial, check out our book iOS 8 by Tutorials, which is chock-full of tutorials like this.

If you have any questions or comments about this tutorial, please join the forum discussion below!

iOS 8 Handoff Tutorial is a post from: Ray Wenderlich

The post iOS 8 Handoff Tutorial appeared first on Ray Wenderlich.

Introducing the raywenderlich.com Podcast Season 2!

$
0
0
The raywenderlich.com Podcast Season 2: More episodes, more tech!

The raywenderlich.com Podcast Season 2: More episodes, more tech, more guests!

Now that Season I of The raywenderlich.com Podcast is complete, we’re delighted to announce Season II!

Season II brings with it some major changes and improvements to the format, based entirely on your feedback:

  • More frequent episodes: Instead of having episodes once per month, we’re now moving to weekly episodes!
  • Shorter episodes: We’re also moving to a shorter format of just 30 minutes per episode.
  • Tech Talk focus: Since we’re moving to shorter episodes, we’re going to refocus the podcast on just Tech Talks for App Developers. In Season II, we’ll be covering interesting topics such as App Marketing, Realm, Core Data, GPUImage, FMDB, Unity, and more!
  • More guests: Every episode in Season II will have a special guest to chat with us about that episodes tech topic. We have some real superstars lined up this season, including Jeremy Olson, Brad Larson, Gus Meuller, Jonathan Blocksom, and more!
  • Just Mic and Jake: We’re saying goodbye to Tammy and Felipe. Let’s face it… four hosts + one guest would get a little crowded for a 30 minute podcast.

If you’re a Tammy & Felipe fan, don’t worry – they’ve started their own podcast called Roundabout: Creative Chaos, so you can still follow their adventures! Their new podcast is focused on creative technology, and talking with others who’ve turned their passion into a career. We’ll miss Tammy & Felipe, and we wish them all the best with their new podcast!

The first episode of The raywenderlich.com Podcast, Season II will come out next Monday. It will be focused on The Successful App Launch, with special guest Jeremy Olson – Apple Design Award Winner, and co-author of iOS 7 by Tutorials.

If you haven’t subscribed to the podcast yet, you can Subscribe in iTunes or follow the podcast RSS feed.

We hope you enjoy Season II of The raywenderlich.com Podcast – stay tuned for what should be an amazing season! :]

Introducing the raywenderlich.com Podcast Season 2! is a post from: Ray Wenderlich

The post Introducing the raywenderlich.com Podcast Season 2! appeared first on Ray Wenderlich.

Introduction to Realm

$
0
0
Learn how to create a map-based app with Realm as the data storage engine!

Learn how to create a map-based app with Realm as the data storage engine!

Note from Ray: This is a brand new Swift tutorial released as part of the iOS 8 Feast. Enjoy!

Realm is a cross-platform mobile database just released to the public in July of 2014. It’s a data persistence solution designed specifically for mobile applications.

Realm is extremely simple to integrate in your projects, and most common functions – such as querying the database – consist of a single line of code!

Unlike wrappers around Core Data such as MagicalRecord, Realm does not rely on Core Data or even a SQLite backend.

The Realm developers claim that their proprietary data storage solution is even faster than SQLite and Core Data. Here’s some example Core Data code to fetch a set of records with a predicate and then sort the results:

let fetchRequest = NSFetchRequest(entityName: "Specimen")
let predicate = NSPredicate(format: "name BEGINSWITH [c]%@", searchString)
fetchRequest.predicate = predicate
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let error = NSError()
let results = managedObjectContext?.executeFetchRequest(fetchRequest, error:&error)

What takes seven lines with Core Data can be achieved with only two lines in Realm:

let predicate = NSPredicate(format: "name BEGINSWITH [c]%@", searchString);
let specimens = Specimen.objectsWithPredicate(predicate).arraySortedByProperty("name", ascending: true)

Working with Realm results in more concise code — which makes it easier to write and read your code.

This tutorial will introduce you to the basic features of Realm on iOS; you’ll learn how to link in the Realm framework, create models, perform queries, and update and delete records.

Getting Started

Here’s the scenario: you’ve accepted a position as an intern in the National Park Service and your job is to document the species found in the biggest national parks in the United States. You need an assistant to keep notes and document our findings, but the agency doesn’t have an assistant to spare, nor the budget to hire a new one. Instead, you’ll create a virtual assistant for yourself — an app named “Partner” — that you can carry around in your pocket!

Download the starter project for this tutorial here.

Open the starter project in Xcode. MapKit is already set up in your project, along with some basic functionality to create, update and delete new specimens in your app. Right now your app only contains instances of UITableView and MKMapView to provide the map functionality.

Note: If you’re interested in learning more about MapKit, check out our Introduction to MapKit tutorial, which provides an in-depth look at how to work with MapKit.

The starter project won’t build yet since it’s missing Realm. Download the the latest Realm distribution from this URL: http://static.realm.io/downloads/cocoa/latest.

Unzip the Realm archive. Inside the folder you’ll see another folder named iOS. Open this folder, and drag the Realm.framework file into the Frameworks folder of your Xcode project to keep everything organized:

realm-framework

Be sure to check the Copy items if needed option and click Finish to add the framework to your project.

Back to the Realm archive folder, open the folder named Swift. There should be a file named RLMSupport.swift here containing some Swift conventions for Realm classes, such as a Generator on RLMArray that lets you use Realm arrays like native arrays.

Drag this file into the RWRealmStarterProject folder of the Xcode project. Again, make sure you select Copy Items if Needed.

Thats it! Build and run the project to ensure everything compiles. If not, re-check the steps above carefully. You should see a basic screen like so:

realm-first

Introducing Realm Browser

There’s a nice utility included in the Realm distribution package that you’ll want to install to make your life a little easier.

The Realm Browser lets you read and edit Realm databases. It’s really useful while developing as the Realm database format is proprietary and not easily human-readable.

realm-browser

You’ll find the Realm Browser app in the browser folder of the Realm archive folder. Alternatively, you can access the Realm GitHub repository and build the project in the tools/RealmBrowser directory.

Concepts and Major Classes

In order to better understand what Realm does, here’s an overview of the Realm classes and conceptl you’ll use in this tutorial:

RLMRealm: RLMRealm is the heart of the framework; it’s your access point to the underlying database, similar to a Core Data managed object context. For your coding convenience, there’s a singleton defaultRealm which you’ll use in this tutorial. There’s also an in-memory realm instance that you can use when you don’t need to persist the results to disk, and you can create other realms as needed to support concurrent operations.

RLMObject: This is your realm model. The act of creating a model defines the schema of the database; to create a model you simply subclass RLMObject and define the fields you want to persist as properties.

Relationships: You create one-to-many relationships between objects by simply declaring a property of the type of the RLMObject you want to refer to. You can create many-to-one and many-to-many relationships via a property of type RLMArray, which leads you to…

RLMArray: This class has an API similar to that of NSArray; you can use it to store multiple RLMObject instances and define relationships. Its has other hidden powers, too; it can sort RLMObject instances, query the database, and can perform aggregate queries.

Write Transactions: Any operations in the database such as creating, editing, or deleting objects must be performed within transactions which are delineated by the beginWriteTransaction() and commitWriteTransaction() operations.

Queries: To retrieve objects from the database you’ll need to use queries. The simplest form of a query is calling allObjects() on an RLMObject. If your data retrieval needs are more complex you can make use of predicates, chain your queries, and order your results as well.

Now that you’ve had an introduction to Realm, it’s time to get your feet wet and build the rest of the project for this tutorial.

Creating Your First Model

Finally, it’s time to create your first Realm model!

Right-click the Models group in the Xcode project navigator and select New File…. Select iOS\Source\Swift File and click Next. Name the file Specimen and ensure that you select the RWRealmStarterProject target and click Create.

Open Specimen.swift and add replace the file’s contents with the following:

import UIKit
import Realm
 
class Specimen: RLMObject {
  dynamic var name = ""
  dynamic var specimenDescription = ""
  dynamic var latitude: Double = 0.0
  dynamic var longitude: Double = 0.0
  dynamic var created = NSDate()
}

The code above adds a few properties: name and specimenDescription store the specimen name and the specimen description respectively. Specific datatypes in Realm, such as strings, must be initialized with a value. In this case you initialize them with an empty string.

latitude and longitude store the coordinates for the specimen. Here you set the type to Double and initialize them with 0.0.

Finally, created stores the creation date of the Specimen. NSDate() returns the current date, so you can initialize the property with that value.

Now that you’ve created your first model in Realm, how about using what you’ve learned in a small challenge?

Specimens will be separated into different Categories. The challenge is to create a category model by yourself; name the file Category.swift and give your new model a single String property name.

If you want to check your work, the solution is below:

Solution Inside: Solution SelectShow>

You now have a Category model which you need to relate to the Specimen model somehow.

Recall the note above that stated you could create relationships between models by simply declaring a property with the appropriate model to be linked.

Open Specimen.swift and add the following declaration below the line where you define the created property:

dynamic var category = Category()

This sets up a one-to-many relationship between the Specimen and the Category models. This means each specimen can belong to only one Category, but each Category may have many specimens.

You have your basic data models in place — it’s time to add some records to your database!

Adding Records

When the user adds a new specimen, they’ll have a chance to enter the specimen name and select a category. Open CategoriesTableViewController.swift. This view controller will present the list of categories in a table view so the user can select one.

You’ll need to populate this table view with some default categories. You can store these Category instances in an instance of RLMArray. This is an array that stores RLMObjects — but has some cool functionality you will use a little bit later! :]

CategoriesTableViewController has a categories array as a placeholder for now. Find the following code at the top of the class definition:

var categories = []

…and replace it with the following line:

var categories = RLMArray(objectClassName: Category.className())

When you create an RLMArray you need to define what models it will store. In the code above you simply create the RLMArray with the class name in the initializer RLMArray(objectClassName: Category.className()); this indicates that this instance of RLMArray will store Category models.

You’ll want to give your user some default categories to choose from the first time the app runs.

Add the following helper method to the class definition:

func populateDefaultCategories() {
  categories = Category.allObjects() //1
 
  if categories.count == 0 { //2
    let realm = RLMRealm.defaultRealm() //3
    realm.beginWriteTransaction() //4
 
    //5
    let defaultCategories = ["Birds", "Mammals", "Flora", "Reptiles", "Arachnids" ]
    for category in defaultCategories {
      //6
      let newCategory = Category()
      newCategory.name = category
      realm.addObject(newCategory)
    }
 
    realm.commitWriteTransaction() // 7
    categories = Category.allObjects() //8
  }
}

Taking each numbered line in turn:

  1. Categories.allObjects() returns all rows for the specified object; in this case, you’re asking for all Category objects in the database.
  2. If count here is equal to 0 this means the database has no category records, which is the case the first time you run the app.
  3. You access the default realm singleton and store it in realm for easy access.
  4. This starts a transaction on the default realm — you’re now ready to add some records to the database.
  5. Here you create the list of default category names and then iterate through them.
  6. For each category name, you create a new instance of Category, populate name and add the object to the realm.
  7. When you’ve added all the categories, call commitWriteTransaction() to close the transaction and commit the records to the database.
  8. Finally, you fetch all of the categories you just created and store them in categories.

Operations that you perform inside a transaction will only run when you call commitWriteTransaction(). You can perform a simple set of create operations like you did in the code above, or you can perform multiple complex operations together like creating, updating, or deleting multiple objects at the same time.

Add the following line to the end of viewDidLoad():

populateDefaultCategories()

This calls the helper method to populate your test categories when the view loads.

Now that you have some data, you’ll need to update the table view data source methods to show the categories. Find tableView(_:cellForRowAtIndexPath:) and replace the method with the following implementation:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("CategoryCell", forIndexPath: indexPath) as UITableViewCell
 
  let category = categories.objectAtIndex(UInt(indexPath.row)) as Category
  cell.textLabel?.text = category.name
 
  return cell
}

This implementation retrieves the category object from the categories RLMArray and then sets the cell’s text label to show the category name.

Next, add the following line to CategoriesTableViewController class definition with the other properties:

var selectedCategory: Category!

You’ll use this property to store the currently selected category.

Find tableView(_:willSelectRowAtIndexPath:) and replace the method with the following:

override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath {
  selectedCategory = categories[UInt(indexPath.row)] as Category
  return indexPath
}

This method will now store the user’s selection to the property you declared above.

Build and run your app. Zoom and pan the map to somewhere interesting and create a new annotation by tapping on the + button in the top-right. Tap on the map pin to select it, and then tap on the annotation data to edit the details. Then tap the Category text field to see the list of Categories as shown below:

IntroductionToRelam_Categories

You can select a category, but that will only save it to the property and not anywhere else in the database. It’s all well and good to see the Categories show up in the app, but it’s always reassuring to actually see the records in the database. You can do this via the Realm Browser.

Working With the Realm Browser

One thing you don’t know at this point is where your Realm database lives. There’s a nice trick you can use to find it.

Open MapViewController.swift and add the following line to viewDidLoad() just after the call to super.viewDidLoad():

println(RLMRealm.defaultRealm().path)

This line simply prints the database location to the console. It’s a short step to then browse the database using the Realm Browser.

Build and run your app; you’ll see that it reports the location of the database in the Xcode console, as shown by the example below:

/Users/bill/Library/Developer/CoreSimulator/Devices/98817AD7-B6F0-403E-AC68-7551AB2B0607/data/Containers/Data/Application/13B349A1-0F8F-48AD-8BB6-1FD174E49571/Documents/default.realm

The easiest way to go to the database location is to open Finder, press Cmd-Shift-G and paste in the path your app reported. Use the path reported in your Xcode console; don’t copy the example path above, since it will undoubtedly be different on your system!

Once you open the folder in Finder, you might see one or two files. One of them will be default.realm, which is your database file. The second file, which may or may not be present, is default.realm.lock which prevents modification from other apps while the database is in use.

Double-click default.realm to open it with Realm Browser:

IntroductionToRealm_RealBrowserCategories

Note: Some complications may occur when the default.realm is already open in another application — in this case, your app running in the Simulator — and you try to open it in Realm Browser as well.

The .lock file is there to prevent this; the best way to open this database in Realm Browser is to exit the Simulator, delete the .lock file, then open the database from Realm Browser. When you’ve finished inspecting the database, exit Realm Browser then build and run the App in the simulator again.

Once the database is open in Realm Browser, you’ll see your Category class with a 5 next to it. This means that this class contains five records. Click a class to inspect the individual fields contained within.

realm-browser-data

Adding Categories

Now you can implement the logic to set the category of a specimen object.

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

var selectedCategory: Category!

You’ll use this to store the selected Category.

Next, find unwindFromCategories() and add the following lines to the end of the method:

selectedCategory = categoriesController.selectedCategory
categoryTextField.text = selectedCategory.name

This method is called when the user selects a category from CategoriesTableViewController, which you set up in the previous step. Here, you retrieve the selected category and store it locally to selectedCategory and then fill in the text field with the category name.

Now that you have your categories taken care of, you can create your first specimen!

Still in AddNewEntryController.swift, add one more property to the class:

var specimen: Specimen!

This property will store the new specimen object.

Next, add the helper method below to the class:

func addNewSpecimen() {
  let realm = RLMRealm.defaultRealm() //1
 
  realm.beginWriteTransaction() //2
  let newSpecimen = Specimen() //3
  //4
  newSpecimen.name = nameTextField.text
  newSpecimen.category = selectedCategory
  newSpecimen.specimenDescription =  descriptionTextField.text
  newSpecimen.latitude = selectedAnnotation.coordinate.latitude
  newSpecimen.longitude = selectedAnnotation.coordinate.longitude
 
  realm.addObject(newSpecimen) //9
  realm.commitWriteTransaction() //10
 
  specimen = newSpecimen
}

Here’s what the code above does:

  1. You first get the default Realm.
  2. Here you start the transaction to add your new Specimen.
  3. Next, you create a new Specimen instance.
  4. Then you assign the specimen values. The values come from the text input fields in the user interface, the selected categories, and the coordinates from the map annotation.
  5. Here you add the new object to the realm.
  6. Finally, you commit the write transaction by calling commitWriteTransaction().

You’ll need some sort of validator to make sure all the fields are populated correctly in your Specimen. There’s an existing validateFields() method to do this that checks for a specimen name and description. Since you’ve just added the ability to assign a category, you’ll need to check for that field too.

Find the line in validateFields() that looks like this:

if (nameTextField.text.isEmpty || descriptionTextField.text.isEmpty) {

Change that line to this:

if (nameTextField.text.isEmpty || descriptionTextField.text.isEmpty || selectedCategory == nil) {

This verifies that all fields have been filled in and that you’ve selected a category as well.

Next, add the following method to the class:

override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject!) -> Bool {
  if validateFields() {
    if specimen == nil {
      addNewSpecimen()
    }
    return true
  } else {
    return false
  }
}

In the above code you call the method to validate the fields; if everything is filled in, then add the new specimen.

Build and run your app; tap the + button to create a new Specimen. Fill in the name and description, select a category, and tap Confirm to add your specimen to the database.

RW_Realm_Snow

The view controller dismisses — but nothing appears to happen. What’s the deal?

Ah — you’ve posted the record to your Realm — but you haven’t yet populated the map with your newly discovered specimen!

Retrieving Records

Now that you’ve added a specimen to the database, you want it to show up on the map.

If you want to check out your new record, open up Realm Browser and have a look at the data. Remember to exit the Simulator first.

RW_Realm_Browser_SnowLeopard

You’ll see your one lonely Specimen record, with all fields filled along with the latitude and longitude from MKAnnoation. You’ll also see the link to the Category of your Specimen — that means your one-to-many Category relationship is working as expected. Click the Category in your Specimen record to view the Category record itself.

Now you need to populate the map in the app.

Open SpecimenAnnotation.swift and add a property to the class:

var specimen: Specimen?

This will hold the specimen object for the annotation.

Next, replace the initializer with the following:

init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String, specimen: Specimen? = nil) {
  self.coordinate = coordinate
  self.title = title
  self.subtitle = subtitle
  self.specimen = specimen
}

The change here is to add an option to pass in a specimen object. The specimen will have a default value of nil which means you can omit that argument if you like. That means the rest of the app can continue to call the initializer with just the first three arguments as usual if there’s no specimen.

Now open MapViewController.swift add a new property to the class:

var specimens = RLMArray(objectClassName: Specimen.className())

Since you want to store a collection of Specimens in this property, you’ll need to declare it as type RLMArray. Remember, when you initialize an RLMArray object, you need to specify the class of the RLMObject instances it will hold.

Now you’ll need some sort of mechanism to retrieve all of the Specimen records. Still in MapViewController.swift, add the following method to the class:

func populateMap() {
  mapView.removeAnnotations(mapView.annotations) // 1
 
  specimens = Specimen.allObjects()  // 2
  // Create annotations for each one
  for specimen in specimens {
    let aSpecimen = specimen as Specimen
    let coord = CLLocationCoordinate2D(latitude: aSpecimen.latitude, longitude: aSpecimen.longitude);
    let specimenAnnotation = SpecimenAnnotation(coordinate: coord,
      title: aSpecimen.name,
      subtitle: aSpecimen.category.name,
      specimen: aSpecimen) // 3
    mapView.addAnnotation(specimenAnnotation) // 4
  }
}

Taking each numbered comment in turn:

  1. First, you clear out all the existing annotations on the map to start fresh.
  2. Next, you fetch all the specimens from Realm.
  3. You then create a SpecimenAnnotation with the coordinates of the specimen, as well as its name and category.
  4. Finally, you add the annotation to the MKMapView.

Now you need to call this method from somewhere. Find viewDidLoad() and add this line to the end of that method:

populateMap()

That will ensure the map will be populated with the specimens whenever the map view controller loads.

Next you just need to modify your annotation with the specimen name and category. Find unwindFromAddNewEntry() and replace the method with the following implementation:

@IBAction func unwindFromAddNewEntry(segue: UIStoryboardSegue) {
  let addNewEntryController = segue.sourceViewController as AddNewEntryController
  let addedSpecimen = addNewEntryController.specimen as Specimen
  let addedSpecimenCoordinate = CLLocationCoordinate2D(latitude: addedSpecimen.latitude, longitude: addedSpecimen.longitude)
 
  if (lastAnnotation != nil) {
    mapView.removeAnnotation(lastAnnotation)
  } else {
    for annotation in mapView.annotations {
      let currentAnnotation = annotation as SpecimenAnnotation
      if currentAnnotation.coordinate.latitude == addedSpecimenCoordinate.latitude && currentAnnotation.coordinate.longitude == addedSpecimenCoordinate.longitude {
        mapView.removeAnnotation(currentAnnotation)
        break
      }
    }
  }
 
  let annotation = SpecimenAnnotation(coordinate: addedSpecimenCoordinate, title: addedSpecimen.name, subtitle: addedSpecimen.category.name, specimen: addedSpecimen)
 
  mapView.addAnnotation(annotation)
  lastAnnotation = nil;
}

This method is called once you’ve returned from AddNewEntryController and there’s a new specimen to add to the map. When you add a new specimen to the map, it gets the generic annotation icon; now that you have a category, you want to change that to the category-specific icon. Here you simply remove the last annotation added to the map (the generic-looking one) and replace it with an annotation that shows the name and category.

Build and run your app; create some new specimens of different categories and see how the map updates:

RW_Realm_map

A different view

You might have noticed the “Log” button in the top-left of the map view. In addition to the map, the app also has a text-based table view listing of all annotations called the Log view. The table view there is always blank, so it’s time to populate that with some data.

Open LogViewController.swift and replace the specimens property with the following:

var specimens = RLMArray(objectClassName: Specimen.className())

In the code above, you replace the placeholder array with an RLMArray which will hold Specimens just as you did in MapViewController.

Next, find viewDidLoad() and add this line after the call to super.viewDidLoad():

specimens = Specimen.allObjects().arraySortedByProperty("name", ascending: true) // add this line

This line will populate specimens with all the specimens in the database, sorted by name.

Next, replace tableView(_:cellForRowAtIndexPath:) with the following implementation:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  var cell = tableView.dequeueReusableCellWithIdentifier("LogCell") as LogCell
  var specimen: Specimen!
 
  specimen = specimens[UInt(indexPath.row)] as Specimen
 
  cell.titleLabel.text = specimen.name
  cell.subtitleLabel.text = specimen.category.name
 
  switch specimen.category.name {
  case "Uncategorized":
    cell.iconImageView.image = UIImage(named: "IconUncategorized")
  case "Reptiles":
    cell.iconImageView.image = UIImage(named: "IconReptile")
  case "Flora":
    cell.iconImageView.image = UIImage(named: "IconFlora")
  case "Birds":
    cell.iconImageView.image = UIImage(named: "IconBird")
  case "Arachnid":
    cell.iconImageView.image = UIImage(named: "IconArachnid")
  case "Mammals":
    cell.iconImageView.image = UIImage(named: "IconMammal")
  default:
    cell.iconImageView.image = UIImage(named: "IconUncategorized")
  }
 
  return cell
}

This method will now show the specimen name and specimen category.

Build and run your app; tap Log and you’ll see all of your entered Specimens in the table view like so:

RW_Realm_LogView

Deleting Records

You’ve learned how to create records in Realm — but what if you add something by accident, or want to remove something you’ve added previously? You’ll need some mechanism to delete records from Realm. You’ll find that it’s a pretty straightforward operation.

Open LogViewController.swift and add the following helper method:

func deleteRowAtIndexPath(indexPath: NSIndexPath) {
  let realm = RLMRealm.defaultRealm() //1
  let objectToDelete = specimens[UInt(indexPath.row)] as Specimen //2
  realm.beginWriteTransaction() //3
  realm.deleteObject(objectToDelete) //4
  realm.commitWriteTransaction() //5
 
  specimens = Specimen.allObjects().arraySortedByProperty("name", ascending: true) //6
 
  tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) //7
}

Here’s what’s going on in this method:

  1. First, you grab the default realm.
  2. Next, you find the object you want to delete from the Specimens array.
  3. You then start a write transaction.
  4. Here you call deleteObject() and pass the object you wish to delete as the parameter to the call.
  5. Then you commit the write transaction to post the delete operation to the database.
  6. Since you’ve removed a Specimen, you need to reload the master list just as you did in viewDidLoad.
  7. Finally, you update the UITableViewCell so it removes the deleted row.

Next, find tableView(_:commitEditingStyle:forRowAtIndexPath:) and add the following line inside the if statement block:

deleteRowAtIndexPath(indexPath)

When the table view calls this delegate method to signal a deletion, all you need to do is call your helper method.

Build and run your app; view the Log and swipe right on the row you wish to delete. Kill the Simulator and open the database in Realm Browser to verify that the record has been deleted:

RW_Realm_LogView_deleting

Fetching With Predicates

You really want your app to rock — so you’ll need a handy search feature. Your starter project contains an instance of UISearchController — you’ll just need to add a few modifications specific to your app in order to make it work with Realm.

Open LogViewController.swift and replace the searchResults property with the following:

var searchResults = RLMArray(objectClassName: Specimen.className())

Since the search results will be Specimen objects, you want the property to be of type RLMArray.

Now add the method below to the class:

func filterResultsWithSearchString(searchString: String) {
  let predicate = NSPredicate(format: "name BEGINSWITH [c]%@", searchString) // 1
  let scopeIndex = searchController.searchBar.selectedScopeButtonIndex
  switch scopeIndex {
  case 0:
    searchResults = Specimen.objectsWithPredicate(predicate).arraySortedByProperty("name", ascending: true) //2
  case 1:
    searchResults = Specimen.objectsWithPredicate(predicate).arraySortedByProperty("distance", ascending: true) //3
  case 2:
    searchResults = Specimen.objectsWithPredicate(predicate).arraySortedByProperty("created", ascending: true) //4
  default:
    searchResults = Specimen.objectsWithPredicate(predicate)
  }
}

Here’s what the above function does:

  1. First you create a predicate which searches for names that start with searchString. The [c] that follows BEGINSWITH indicates a case insensitive search.
  2. If the first segmented button is selected, sort the results by name ascending.
  3. If the second button is selected, sort the results by distance ascending.
  4. If the third button is selected, sort the results by created date ascending.
  5. If none of the buttons are selected, don’t sort the results — just take them in the order they’re returned from the database.

Since the search results table view calls the same data source methods, you’ll need a small change to tableView(_:cellForRowAtIndexPath:) to handle both the main log table view and the search results. In that method, find the line that assigns to the specimen variable:

specimen = specimens[UInt(indexPath.row)] as Specimen

Delete that one line and replace it with the following:

if (searchController.active) {
  specimen = searchResults[UInt(indexPath.row)] as Specimen
} else {
  specimen = specimens[UInt(indexPath.row)] as Specimen
}

The above code checks that the searchController is active; if so, it retrieves the specimen from the search results; if not, then it retrieves the specimen from the specimens array instead.

Finally you’ll need to add a function to sort the returned results when the user taps a button in the scope bar.

Replace the empty scopeChanged method with the code below:

@IBAction func scopeChanged(sender: AnyObject) {
  let scopeBar = sender as UISegmentedControl
 
  switch scopeBar.selectedSegmentIndex {
  case 0:
    specimens = Specimen.allObjects().arraySortedByProperty("name", ascending: true)
  case 1:
    break
  case 2:
    specimens = Specimen.allObjects().arraySortedByProperty("created", ascending: true)
  default:
    specimens = Specimen.allObjects().arraySortedByProperty("name", ascending: true)
  }
  tableView.reloadData()
}

In the code above you check which scope button is pressed — A-Z, Distance or Date Added — and call arraySortedByProperty(_:ascending:) accordingly. By default, the list will sort by name.

Note that the case for sorting by distance (case 1) is empty – the app doesn’t have distance yet, so there’s nothing to do here yet but that’s a preview of things to come!

Build and run your app; try a few different searches and see what you get for results!

RW_Realm_Sort

Updating Records

You’ve covered the addition of records and the deletion of records — all that’s left is updating.

If you tap in a cell in LogViewController you will segue to the AddNewEntryViewController but with the fields empty. Of course the first step to letting the user edit the fields is to show the existing data!

Open AddNewEntryViewController.swift and add the following helper method to the class:

func fillTextFields() {
  nameTextField.text = specimen.name
  categoryTextField.text = specimen.category.name
  descriptionTextField.text = specimen.specimenDescription
 
  selectedCategory = specimen.category
}

This method will fill in the user interface with the specimen data. Remember, AddNewEntryViewController has up to this point only been used for adding new specimens so those fields have always started out empty.

Next, add the following lines to the end of viewDidLoad():

if (specimen == nil) {
  title = "Add New Specimen"
} else {
  title = "Edit \(specimen.name)"
  fillTextFields()
}

The above code sets the navigation bar title to say whether the user is adding a new specimen or updating an existing one. If it’s an existing specimen, you also call your helper method to fill in the fields.

Now you’ll need a method to update the Specimen record with the user’s changes. Add the following method to the class:

func updateSpecimen() {
  let realm = RLMRealm.defaultRealm()
  realm.beginWriteTransaction()
 
  specimen.name = nameTextField.text
  specimen.category = selectedCategory
  specimen.specimenDescription = descriptionTextField.text
 
  realm.commitWriteTransaction()
}

As usual, the method begins with getting the default Realm and then the rest is wrapped between beginWriteTransaction() and commitWriteTransaction(). Inside the transaction, you simply update the three data fields.

Six lines of code to update the Specimen record is all it takes! :]

Now you need to call the above method when the user taps Confirm. Find shouldPerformSegueWithIdentifier(_:sender:) and add the following lines just before the return true inside the first if block:

else {
  updateSpecimen()
}

This will call your helper method to update the data when appropriate.

Now open LogViewController.swift and replace prepareForSegue(_:sender:) with the following implementation:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
  if (segue.identifier == "Edit") {
    let controller = segue.destinationViewController as AddNewEntryController
    var selectedSpecimen : Specimen!
    let indexPath = tableView.indexPathForSelectedRow()
 
    if searchController.active {
      let searchResultsController = searchController.searchResultsController as UITableViewController
      let indexPathSearch = searchResultsController.tableView.indexPathForSelectedRow()
      selectedSpecimen = searchResults[UInt(indexPathSearch!.row)] as Specimen
    } else {
      selectedSpecimen = specimens[UInt(indexPath!.row)] as Specimen
    }
 
    controller.specimen = selectedSpecimen
  }
}

You need to pass the selected specimen to the AddNewEntryController instance. The complication with the if/else is because getting the selected specimen is slightly different depending on whether the user is looking at search results or not.

Build and run your app; open the Log view and tap on an existing specimen. You should see the details with all the fields filled in, ready for editing.

RW_Realm_Updating

Including Ignored Properties

There’s one more thing to make your app perfect — and possibly put you in line for an Apple Design Award!

RW_Realm_Award

Hey — developers can dream, can’t we? :]

Remember how you can’t sort the Log view by distance? There’s a bit of code to put in place before that can happen — but the results will be worth it.

Open Specimen.swift and add one more property to the class:

dynamic var distance: Double = 0

This property will hold the distance between the annotation and the user’s location. However, there’s no real need to persist distance because the user’s location will change each time they use the app. You want the distance as part of the model, but you don’t want Realm to persist the field.

Realm supports properties like this and calls them ignored properties. Add the following method to the class next:

func ignoredProperties() -> NSArray {
  let propertiesToIgnore = [distance]
  return propertiesToIgnore
}

To define your list of ignored property you simply implement the method ignoredProperties() and return an array of properties that you don’t want persisted.

Since you don’t store the distance, it’s clear you’ll need to calculate it yourself.

Open MapViewController.swift and add the following method:

func updateLocationDistance() {
  let realm = RLMRealm.defaultRealm()
 
  for specimen in specimens {
    let currentSpecimen = specimen as Specimen
    let currentLocation = CLLocation(latitude: currentSpecimen.latitude, longitude: currentSpecimen.longitude)
    let distance = currentLocation.distanceFromLocation(mapView.userLocation.location)
    realm.beginWriteTransaction()
    currentSpecimen.distance = Double(distance)
    realm.commitWriteTransaction()
  }
}

For each specimen, you calculate the distance between it and the user’s current location. Even though you don’t persist the distance, you still need to store it in the record and wrap the change in a write transaction.

Next, add the following code to the end of prepareForSegue(_:sender:)

else if (segue.identifier == "Log") {
  updateLocationDistance()
}

Here, you’re calling the helper method to calculate the distance before the user reaches the Log screen.

Next, open LogViewController.swift and find tableView(_:cellForRowAtIndexPath:). Add the following lines near the end of the method, just before the return statement:

if specimen.distance < 0 {
  cell.distanceLabel.text = "N/A"
} else {
  cell.distanceLabel.text = String(format: "%.2fkm", specimen.distance / 1000)
}

Finally, find scopeChanged() and replace the break statement in case 1 with the following:

specimens = Specimen.allObjects().arraySortedByProperty("distance", ascending: true)

Build and run your app, and…CRASH!

'RLMException', reason: 'Column count does not match interface - migration required’

Oh, man — what went wrong?

When you added the distance property to the Specimen model you changed the schema — and you didn’t tell Realm how to handle the addition of a new field. The process to migrate a database from one version to another is beyond the scope of this tutorial. This isn’t limited to Realm; Core Data also requires that you migrate the database when fields are added, changed or removed.

For the purposes of this tutorial, you can simply remove the app from the Simulator and build and run your app again. This forces the app to create a brand new database — with your new schema in use.

Delete the app from the simulator, then build and run your app. Add some new specimens and bring up the Log view, where you’ll see the relative distance to each as below:

RWP_Realm_Distance

You might need to simulate a location to calculate a valid distance. From the Simulator menu, select Debug\Location and pick one of the items in the list.

Where to Go From Here?

You can download the finished project here.

In this tutorial you’ve learned how to create, update, delete and fetch records from the Realm database, how to use predicates, and sort the results by their properties.

Some of you may ask, “Since Realm is a relatively new project, should I use it in production apps?”

Realm has only recently been released to the public, but it’s been used in production since 2012. I have personally implemented a relatively complicated project using Realm, and even though the app is still under development it’s working really well!

Realm is stable if you’re using Objective-C; however even with the recent release of Xcode 6 GM, Swift is still very much in flux so I’d suggest waiting a little longer for Swift and Realm to be more stable before using them in production apps.

There are many other features of Realm that weren’t touched on in this tutorial:

  • Migrations: In this tutorial you saw that modifying the Realm schema caused issues. To learn how to migrate databases between versions, check out the Realm documentation regarding migrations to see how you can implement this mechanism in your app.
  • Other Realms: You’ve only worked with the “default” realm, but there are other realms that you can use, such as an in-memory default realm that don’t persist anywhere. You can also use multiple realms if you need to use different databases to hold different kinds of data.
  • Concurrency: There’s an art to accessing a realm from different threads; read through the documentation to see what rules you need to follow.

You can learn about the above topics and much more in the official documentation, which I’ve found to be quite good.

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

Introduction to Realm is a post from: Ray Wenderlich

The post Introduction to Realm appeared first on Ray Wenderlich.

Viewing all 4373 articles
Browse latest View live


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