- •Contents at a Glance
- •About the Authors
- •About the Technical Reviewer
- •Acknowledgments
- •Preface
- •What This Book Is
- •What You Need
- •Developer Options
- •What You Need to Know
- •What’s Different About Coding for iOS?
- •Only One Active Application
- •Only One Window
- •Limited Access
- •Limited Response Time
- •Limited Screen Size
- •Limited System Resources
- •No Garbage Collection, but…
- •Some New Stuff
- •A Different Approach
- •What’s in This Book
- •What’s New in This Update?
- •Are You Ready?
- •Setting Up Your Project in Xcode
- •The Xcode Workspace Window
- •The Toolbar
- •The Navigator View
- •The Jump Bar
- •The Utility Pane
- •Interface Builder
- •New Compiler and Debugger
- •A Closer Look at Our Project
- •Introducing Xcode’s Interface Builder
- •What’s in the Nib File?
- •The Library
- •Adding a Label to the View
- •Changing Attributes
- •Some iPhone Polish—Finishing Touches
- •Bring It on Home
- •The Model-View-Controller Paradigm
- •Creating Our Project
- •Looking at the View Controller
- •Understanding Outlets and Actions
- •Outlets
- •Actions
- •Cleaning Up the View Controller
- •Designing the User Interface
- •Adding the Buttons and Action Method
- •Adding the Label and Outlet
- •Writing the Action Method
- •Trying It Out
- •Looking at the Application Delegate
- •Bring It on Home
- •A Screen Full of Controls
- •Active, Static, and Passive Controls
- •Creating the Application
- •Implementing the Image View and Text Fields
- •Adding the Image View
- •Resizing the Image View
- •Setting View Attributes
- •The Mode Attribute
- •Interaction Checkboxes
- •The Alpha Value
- •Background
- •Drawing Checkboxes
- •Stretching
- •Adding the Text Fields
- •Text Field Inspector Settings
- •Setting the Attributes for the Second Text Field
- •Creating and Connecting Outlets
- •Closing the Keyboard
- •Closing the Keyboard When Done Is Tapped
- •Touching the Background to Close the Keyboard
- •Adding the Slider and Label
- •Creating and Connecting the Actions and Outlets
- •Implementing the Action Method
- •Adding Two Labeled Switches
- •Connecting and Creating Outlets and Actions
- •Implementing the Switch Actions
- •Adding the Button
- •Connecting and Creating the Button Outlets and Actions
- •Implementing the Segmented Control Action
- •Implementing the Action Sheet and Alert
- •Conforming to the Action Sheet Delegate Method
- •Showing the Action Sheet
- •Spiffing Up the Button
- •Using the viewDidLoad Method
- •Control States
- •Stretchable Images
- •Crossing the Finish Line
- •The Mechanics of Autorotation
- •Points, Pixels, and the Retina Display
- •Autorotation Approaches
- •Handling Rotation Using Autosize Attributes
- •Configuring Supported Orientations
- •Specifying Rotation Support
- •Designing an Interface with Autosize Attributes
- •Using the Size Inspector’s Autosize Attributes
- •Setting the Buttons’ Autosize Attributes
- •Restructuring a View When Rotated
- •Creating and Connecting Outlets
- •Moving the Buttons on Rotation
- •Swapping Views
- •Designing the Two Views
- •Implementing the Swap
- •Changing Outlet Collections
- •Rotating Out of Here
- •Common Types of Multiview Apps
- •The Architecture of a Multiview Application
- •The Root Controller
- •Anatomy of a Content View
- •Building View Switcher
- •Creating Our View Controller and Nib Files
- •Modifying the App Delegate
- •Modifying BIDSwitchViewController.h
- •Adding a View Controller
- •Building a View with a Toolbar
- •Writing the Root View Controller
- •Implementing the Content Views
- •Animating the Transition
- •Switching Off
- •The Pickers Application
- •Delegates and Data Sources
- •Setting Up the Tab Bar Framework
- •Creating the Files
- •Adding the Root View Controller
- •Creating TabBarController.xib
- •The Initial Test Run
- •Implementing the Date Picker
- •Implementing the Single-Component Picker
- •Declaring Outlets and Actions
- •Building the View
- •Implementing the Controller As a Data Source and Delegate
- •Implementing a Multicomponent Picker
- •Declaring Outlets and Actions
- •Building the View
- •Implementing the Controller
- •Implementing Dependent Components
- •Creating a Simple Game with a Custom Picker
- •Writing the Controller Header File
- •Building the View
- •Adding Image Resources
- •Implementing the Controller
- •The spin Method
- •The viewDidLoad Method
- •Final Details
- •Linking in the Audio Toolbox Framework
- •Final Spin
- •Table View Basics
- •Table Views and Table View Cells
- •Grouped and Plain Tables
- •Implementing a Simple Table
- •Designing the View
- •Writing the Controller
- •Adding an Image
- •Using Table View Cell Styles
- •Setting the Indent Level
- •Handling Row Selection
- •Changing the Font Size and Row Height
- •Customizing Table View Cells
- •Adding Subviews to the Table View Cell
- •Creating a UITableViewCell Subclass
- •Adding New Cells
- •Implementing the Controller’s Code
- •Loading a UITableViewCell from a Nib
- •Designing the Table View Cell in Interface Builder
- •Using the New Table View Cell
- •Grouped and Indexed Sections
- •Building the View
- •Importing the Data
- •Implementing the Controller
- •Adding an Index
- •Implementing a Search Bar
- •Rethinking the Design
- •A Deep Mutable Copy
- •Updating the Controller Header File
- •Modifying the View
- •Modifying the Controller Implementation
- •Copying Data from allNames
- •Implementing the Search
- •Changes to viewDidLoad
- •Changes to Data Source Methods
- •Adding a Table View Delegate Method
- •Adding Search Bar Delegate Methods
- •Adding a Magnifying Glass to the Index
- •Adding the Special Value to the Keys Array
- •Suppressing the Section Header
- •Telling the Table View What to Do
- •Putting It All on the Table
- •Navigation Controller Basics
- •Stacky Goodness
- •A Stack of Controllers
- •Nav, a Hierarchical Application in Six Parts
- •Meet the Subcontrollers
- •The Disclosure Button View
- •The Checklist View
- •The Rows Control View
- •The Movable Rows View
- •The Deletable Rows View
- •The Editable Detail View
- •The Nav Application’s Skeleton
- •Creating the Top-Level View Controller
- •Setting Up the Navigation Controller
- •Adding the Images to the Project
- •First Subcontroller: The Disclosure Button View
- •Creating the Detail View
- •Modifying the Disclosure Button Controller
- •Adding a Disclosure Button Controller Instance
- •Second Subcontroller: The Checklist
- •Creating the Checklist View
- •Adding a Checklist Controller Instance
- •Third Subcontroller: Controls on Table Rows
- •Creating the Row Controls View
- •Adding a Rows Control Controller Instance
- •Fourth Subcontroller: Movable Rows
- •Creating the Movable Row View
- •Adding a Move Me Controller Instance
- •Fifth Subcontroller: Deletable Rows
- •Creating the Deletable Rows View
- •Adding a Delete Me Controller Instance
- •Sixth Subcontroller: An Editable Detail Pane
- •Creating the Data Model Object
- •Creating the Detail View List Controller
- •Creating the Detail View Controller
- •Adding an Editable Detail View Controller Instance
- •But There’s One More Thing. . .
- •Breaking the Tape
- •Creating a Simple Storyboard
- •Dynamic Prototype Cells
- •Dynamic Table Content, Storyboard-Style
- •Editing Prototype Cells
- •Good Old Table View Data Source
- •Will It Load?
- •Static Cells
- •Going Static
- •So Long, Good Old Table View Data Source
- •You Say Segue, I Say Segue
- •Creating Segue Navigator
- •Filling the Blank Slate
- •First Transition
- •A Slightly More Useful Task List
- •Viewing Task Details
- •Make More Segues, Please
- •Passing a Task from the List
- •Handling Task Details
- •Passing Back Details
- •Making the List Receive the Details
- •If Only We Could End with a Smooth Transition
- •Split Views and Popovers
- •Creating a SplitView Project
- •The Storyboard Defines the Structure
- •The Code Defines the Functionality
- •The App Delegate
- •The Master View Controller
- •The Detail View Controller
- •Here Come the Presidents
- •Creating Your Own Popover
- •iPad Wrap-Up
- •Getting to Know Your Settings Bundle
- •The AppSettings Application
- •Creating the Project
- •Working with the Settings Bundle
- •Adding a Settings Bundle to Our Project
- •Setting Up the Property List
- •Adding a Text Field Setting
- •Adding an Application Icon
- •Adding a Secure Text Field Setting
- •Adding a Multivalue Field
- •Adding a Toggle Switch Setting
- •Adding the Slider Setting
- •Adding Icons to the Settings Bundle
- •Adding a Child Settings View
- •Reading Settings in Our Application
- •Retrieving User Settings
- •Creating the Main View
- •Updating the Main View Controller
- •Registering Default Values
- •Changing Defaults from Our Application
- •Keeping It Real
- •Beam Me Up, Scotty
- •Your Application’s Sandbox
- •Getting the Documents Directory
- •Getting the tmp Directory
- •File-Saving Strategies
- •Single-File Persistence
- •Multiple-File Persistence
- •Using Property Lists
- •Property List Serialization
- •The First Version of the Persistence Application
- •Creating the Persistence Project
- •Designing the Persistence Application View
- •Editing the Persistence Classes
- •Archiving Model Objects
- •Conforming to NSCoding
- •Implementing NSCopying
- •Archiving and Unarchiving Data Objects
- •The Archiving Application
- •Implementing the BIDFourLines Class
- •Implementing the BIDViewController Class
- •Using iOS’s Embedded SQLite3
- •Creating or Opening the Database
- •Using Bind Variables
- •The SQLite3 Application
- •Linking to the SQLite3 Library
- •Modifying the Persistence View Controller
- •Using Core Data
- •Entities and Managed Objects
- •Key-Value Coding
- •Putting It All in Context
- •Creating New Managed Objects
- •Retrieving Managed Objects
- •The Core Data Application
- •Designing the Data Model
- •Creating the Persistence View and Controller
- •Persistence Rewarded
- •Managing Document Storage with UIDocument
- •Building TinyPix
- •Creating BIDTinyPixDocument
- •Code Master
- •Initial Storyboarding
- •Creating BIDTinyPixView
- •Storyboard Detailing
- •Adding iCloud Support
- •Creating a Provisioning Profile
- •Enabling iCloud Entitlements
- •How to Query
- •Save Where?
- •Storing Preferences on iCloud
- •What We Didn’t Cover
- •Grand Central Dispatch
- •Introducing SlowWorker
- •Threading Basics
- •Units of Work
- •GCD: Low-Level Queueing
- •Becoming a Blockhead
- •Improving SlowWorker
- •Don’t Forget That Main Thread
- •Giving Some Feedback
- •Concurrent Blocks
- •Background Processing
- •Application Life Cycle
- •State-Change Notifications
- •Creating State Lab
- •Exploring Execution States
- •Making Use of Execution State Changes
- •Handling the Inactive State
- •Handling the Background State
- •Removing Resources When Entering the Background
- •Saving State When Entering the Background
- •A Brief Journey to Yesteryear
- •Back to the Background
- •Requesting More Backgrounding Time
- •Grand Central Dispatch, Over and Out
- •Two Views of a Graphical World
- •The Quartz 2D Approach to Drawing
- •Quartz 2D’s Graphics Contexts
- •The Coordinate System
- •Specifying Colors
- •A Bit of Color Theory for Your iOS Device’s Display
- •Other Color Models
- •Color Convenience Methods
- •Drawing Images in Context
- •Drawing Shapes: Polygons, Lines, and Curves
- •The QuartzFun Application
- •Setting Up the QuartzFun Application
- •Creating a Random Color
- •Defining Application Constants
- •Implementing the QuartzFunView Skeleton
- •Creating and Connecting Outlets and Actions
- •Implementing the Action Methods
- •Adding Quartz 2D Drawing Code
- •Drawing the Line
- •Drawing the Rectangle and Ellipse
- •Drawing the Image
- •Optimizing the QuartzFun Application
- •The GLFun Application
- •Setting Up the GLFun Application
- •Creating BIDGLFunView
- •Updating BIDViewController
- •Updating the Nib
- •Finishing GLFun
- •Drawing to a Close
- •Multitouch Terminology
- •The Responder Chain
- •Responding to Events
- •Forwarding an Event: Keeping the Responder Chain Alive
- •The Multitouch Architecture
- •The Four Touch Notification Methods
- •The TouchExplorer Application
- •The Swipes Application
- •Automatic Gesture Recognition
- •Implementing Multiple Swipes
- •Detecting Multiple Taps
- •Detecting Pinches
- •Defining Custom Gestures
- •The CheckPlease Application
- •The CheckPlease Touch Methods
- •Garçon? Check, Please!
- •The Location Manager
- •Setting the Desired Accuracy
- •Setting the Distance Filter
- •Starting the Location Manager
- •Using the Location Manager Wisely
- •The Location Manager Delegate
- •Getting Location Updates
- •Getting Latitude and Longitude Using CLLocation
- •Error Notifications
- •Trying Out Core Location
- •Updating Location Manager
- •Determining Distance Traveled
- •Wherever You Go, There You Are
- •Accelerometer Physics
- •Don’t Forget Rotation
- •Core Motion and the Motion Manager
- •Event-Based Motion
- •Proactive Motion Access
- •Accelerometer Results
- •Detecting Shakes
- •Baked-In Shaking
- •Shake and Break
- •Accelerometer As Directional Controller
- •Rolling Marbles
- •Writing the Ball View
- •Calculating Ball Movement
- •Rolling On
- •Using the Image Picker and UIImagePickerController
- •Implementing the Image Picker Controller Delegate
- •Road Testing the Camera and Library
- •Designing the Interface
- •Implementing the Camera View Controller
- •It’s a Snap!
- •Localization Architecture
- •Strings Files
- •What’s in a Strings File?
- •The Localized String Macro
- •Real-World iOS: Localizing Your Application
- •Setting Up LocalizeMe
- •Trying Out LocalizeMe
- •Localizing the Nib
- •Localizing an Image
- •Generating and Localizing a Strings File
- •Localizing the App Display Name
- •Auf Wiedersehen
- •Apple’s Documentation
- •Mailing Lists
- •Discussion Forums
- •Web Sites
- •Blogs
- •Conferences
- •Follow the Authors
- •Farewell
- •Index
664 |
CHAPTER 19: Whee! Gyro and Accelerometer! |
hardware, but it does simulate the shake event, so the version of the application in 19 ShakeAndBreak - Motion Method will work with the simulator.
Go have some fun with it. When you’re finished, come on back, and you’ll see how to use the accelerometer as a controller for games and other programs.
Accelerometer As Directional Controller
Commonly, instead of using buttons to control the movement of a character or object in a game, the accelerometer is used. In a car-racing game, for example, twisting the iOS device like a steering wheel might steer your car, while tipping it forward might accelerate, and tipping back might brake.
Exactly how you use the accelerometer as a controller will vary greatly depending on the specific mechanics of the game. In the simplest cases, you might just take the value from one of the axes, multiply it by a number, and tack that on to the coordinates of the controlled objects. In more complex games where physics are modeled more realistically, you would need to make adjustments to the velocity of the controlled object based on the values returned from the accelerometer.
The one tricky aspect of using the accelerometer as a controller is that the delegate method is not guaranteed to call back at the interval you specify. If you tell the motion manager to read the accelerometer 60 times a second, all that you can say for sure is that it won’t update more than 60 times a second. You’re not guaranteed to get 60 evenly spaced updates every second. So, if you’re doing animation based on input from the accelerometer, you must keep track of the time that passes between updates and factor that into your equations to determine how far objects have moved.
Rolling Marbles
For our next trick, we’re going to let you move a sprite around iPhone’s screen by tilting the phone. This is a very simple example of using the accelerometer to receive input. We’ll use Quartz 2D to handle our animation.
NOTE: As a general rule, when you’re working with games and other programs that need smooth animation, you’ll probably want to use OpenGL ES. We’re using Quartz 2D in this application for the sake of simplicity and to reduce the amount of code that’s unrelated to using the
accelerometer. The animation won’t be quite as smooth as if we were using OpenGL, but it will
be a lot less work.
In this application, as you tilt your iPhone, the marble will roll around as if it were on the surface of a table (see Figure 19–7). Tip it to the left, and the ball will roll to the left. Tip it farther, and it will move faster. Tip it back, and it will slow down and then start going in the other direction.
www.it-ebooks.info
CHAPTER 19: Whee! Gyro and Accelerometer! |
665 |
Figure 19–7. The Ball application lets you roll a marble around the screen.
In Xcode, create a new project using the Single View Application template, and call this one Ball. In the 19 - Ball folder in the project archive, you’ll find an image called ball.png. Drag that to your project.
Now, single-click the Ball folder, and select File New New File…. Select Objective-C class from the Cocoa Touch category, click Next, and name the new class BIDBallView. Select UIView in the Subclass of popup, and click Create to save the class files. We’ll get back to editing this class a little later.
Select BIDViewController.xib to edit the file in Interface Builder. Single-click the View icon, and use the identity inspector to change the view’s class from UIView to BIDBallView. Next, switch to the attributes inspector, and change the view’s Background to Black Color. Then save the nib.
Now it’s time to edit BIDViewController.h. All we need to do here is prepare for Core Motion, so make the following changes:
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>
@interface BIDViewController : UIViewController
@property (strong, nonatomic) CMMotionManager *motionManager;
@end
www.it-ebooks.info
666 |
CHAPTER 19: Whee! Gyro and Accelerometer! |
Next, switch to BIDViewController.m, and add the following lines toward the top of the file:
#import "BIDViewController.h"
#import "BIDBallView.h"
#define kUpdateInterval |
(1.0f / 60.0f) |
@implementation BIDViewController
@synthesize motionManager;
.
.
.
Next, populate viewDidLoad with this code:
- (void)viewDidLoad { [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.motionManager = [[CMMotionManager alloc] init]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; motionManager.accelerometerUpdateInterval = kUpdateInterval; [motionManager startAccelerometerUpdatesToQueue:queue withHandler:
^(CMAccelerometerData *accelerometerData, NSError *error) {
[(BIDBallView *)self.view setAcceleration:accelerometerData.acceleration]; [self.view performSelectorOnMainThread:@selector(update)
withObject:nil waitUntilDone:NO];
}];
}
The viewDidLoad method here is similar to some of what we’ve done elsewhere in this chapter. The main difference is that we are declaring a much higher update interval of 60 times per second. In the block that we tell the motion manager to execute when there are accelerometer updates to report, we pass the acceleration object into our view, and then call a method named update, which updates the position of the ball in the view based on acceleration and the amount of time that has passed since the last update. Since that block can be executed on any thread, and the methods belonging to UIKit objects (including UIView) can be safely used only from the main thread, we once again force the update method to be called in the main thread.
Writing the Ball View
Note that when you entered the code for viewDidLoad in the previous step, you probably saw some error as a result of BIDBallView not being complete. Since we’re doing the bulk of our work in the BIDBallView class, we had better write it, huh? Select BIDBallView.h, and make the following changes:
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>
@interface BIDBallView : UIView
www.it-ebooks.info
CHAPTER 19: Whee! Gyro and Accelerometer! |
667 |
@property (strong, nonatomic) UIImage *image; @property CGPoint currentPoint;
@property CGPoint previousPoint;
@property (assign, nonatomic) CMAcceleration acceleration; @property CGFloat ballXVelocity;
@property CGFloat ballYVelocity; - (void)update;
@end
Let’s look at the properties and talk about what we’re doing with each of them. The first is a UIImage that will point to the sprite that we’ll be moving around the screen.
UIImage *image;
After that, we keep track of two CGPoint variables. The currentPoint property will hold the current position of the ball. We’ll also keep track of the last point where we drew the sprite. That way, we can build an update rectangle that encompasses both the new and old positions of the ball, so that it is drawn at the new spot and erased at the old one.
CGPoint currentPoint;
CGPoint previousPoint;
Next is an acceleration struct, which is how we will get the accelerometer information from our controller.
CMAcceleration acceleration;
We also have two variables to keep track of the ball’s current velocity in two dimensions. Although this isn’t going to be a very complex simulation, we do want the ball to move in a manner similar to a real ball. We’ll calculate the ball movement in the next section.
We’ll get acceleration from the accelerometer and keep track of velocity on two axes with these variables.
CGFloat ballXVelocity;
CGFloat ballYVelocity;
Let’s switch over to BIDBallView.m and write the code to draw and move the ball around the screen. First, make the following changes at the top of BIDBallView.m:
#import "BIDBallView.h"
@implementation BIDBallView
@synthesize image; @synthesize currentPoint; @synthesize previousPoint; @synthesize acceleration; @synthesize ballXVelocity; @synthesize ballYVelocity;
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
self.image = [UIImage imageNamed:@"ball.png"];
self.currentPoint = CGPointMake((self.bounds.size.width / 2.0f) + (image.size.width / 2.0f),
(self.bounds.size.height / 2.0f) + (image.size.height / 2.0f));
www.it-ebooks.info
668 |
CHAPTER 19: Whee! Gyro and Accelerometer! |
ballXVelocity = 0.0f; ballYVelocity = 0.0f;
}
return self;
}
.
.
.
Now, uncomment the commented-out drawRect: method and give it this simple implementation:
-(void)drawRect:(CGRect)rect {
//Drawing code
[image drawAtPoint:currentPoint];
}
Then add these methods to the end of the class:
.
.
.
#pragma mark -
- (CGPoint)currentPoint { return currentPoint;
}
- (void)setCurrentPoint:(CGPoint)newPoint { previousPoint = currentPoint; currentPoint = newPoint;
if (currentPoint.x < 0) { currentPoint.x = 0; ballXVelocity = 0;
}
if (currentPoint.y < 0){ currentPoint.y = 0; ballYVelocity = 0;
}
if (currentPoint.x > self.bounds.size.width - image.size.width) { currentPoint.x = self.bounds.size.width - image.size.width; ballXVelocity = 0;
}
if (currentPoint.y > self.bounds.size.height - image.size.height) { currentPoint.y = self.bounds.size.height - image.size.height; ballYVelocity = 0;
}
CGRect currentImageRect = CGRectMake(currentPoint.x, currentPoint.y, currentPoint.x + image.size.width,
currentPoint.y + image.size.height);
CGRect previousImageRect = CGRectMake(previousPoint.x, previousPoint.y, previousPoint.x + image.size.width,
currentPoint.y + image.size.width);
www.it-ebooks.info