- •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
484 |
CHAPTER 13: Basic Data Persistence |
default. Click it to deselect it. We don’t want this attribute to be optional—a line that doesn’t correspond to a label on our interface is useless.
Selecting the Transient checkbox creates a transient attribute, which is used to specify a value that is held by managed objects while the app is running, but never saved to the data store. Since we do want the line number saved to the data store, leave the Transient checkbox unchecked.
Selecting the Indexed checkbox will cause an index in the underlying SQL database to be created on the column that holds this attribute’s data. Leave the Indexed checkbox unchecked. Since the amount of data is small, and we won’t provide the user with a search capability, there’s no need for an index.
Beneath that are more settings, allowing you to do some simple data validation by specifying minimum and maximum values for the integer, a default value, and more. We won’t be using any of these settings in this example.
Now, make sure the Line entity is selected and click the plus sign to add a second attribute. Change the name of your new attribute to lineText and change its Type to String. This attribute will hold the actual data from the text field. Leave the Optional checkbox checked for this one; it is altogether possible that the user won’t enter a value for a given field.
NOTE: When you change the Type to String, you’ll notice that the inspector shows a slightly different set of options for setting a default value or limiting the length of the string. Although we
won’t be using any of those options for this application, it’s nice to know they’re there.
Guess what? Your data model is complete. That’s all there is to it. Core Data lets you point and click your way to an application data model. Let’s finish building the application so you can see how to use our data model from our code.
Creating the Persistence View and Controller
Because we selected the Empty Application template, we weren’t provided with a view controller. Go back to the project navigator, single-click the Core Data Persistence folder, and press N or select File New New File… to bring up the new file assistant. Select UIViewController subclass from the Cocoa Touch heading, and click Next. On the next sheet, name the class BIDViewController, select a Subclass of UIViewController, make sure the box labeled Targeted for iPad is unchecked, and check the box that says With XIB for user interface to have Xcode create a nib file automatically. Click Next, and choose the directory in which to save the file. When you’re finished,
BIDViewController.h, BIDViewController.m, and BIDViewController.xib will be placed in your Core Data Persistence folder.
www.it-ebooks.info
CHAPTER 13: Basic Data Persistence |
485 |
Select BIDViewController.h, and make the following changes, which should look very familiar to you:
#import <UIKit/UIKit.h>
@interface BIDViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITextField *line1; @property (weak, nonatomic) IBOutlet UITextField *line2; @property (weak, nonatomic) IBOutlet UITextField *line3; @property (weak, nonatomic) IBOutlet UITextField *line4;
@end
Save this file. Next, select BIDViewController.xib to edit the GUI in Interface Builder. Design the view, and connect the outlets by following the instructions in the “Designing the Persistence Application View” section earlier in this chapter. Once your design is complete, select the view, bring up the attributes inspector, and select Light Gray Color from the Background popup menu. You might also find it useful to refer back to Figure 13–3. Once you’ve created the view, save the nib file.
In BIDViewController.m, insert the following code at the top of the file:
#import "BIDViewController.h"
#import "BIDAppDelegate.h"
@implementation BIDViewController
@synthesize line1; @synthesize line2; @synthesize line3; @synthesize line4;
Then insert the following code into the existing viewDidLoad and viewDidUnload methods:
- (void)viewDidLoad { [super viewDidLoad];
// Do any additional setup after loading the view from its nib.
BIDAppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [appDelegate managedObjectContext]; NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:@"Line"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init]; [request setEntity:entityDescription];
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error]; if (objects == nil) {
NSLog(@"There was an error!");
// Do whatever error handling is appropriate
}
for (NSManagedObject *oneObject in objects) {
www.it-ebooks.info
486 |
CHAPTER 13: Basic Data Persistence |
NSNumber *lineNum = [oneObject valueForKey:@"lineNum"];
NSString *lineText = [oneObject valueForKey:@"lineText"];
NSString *fieldName = [NSString stringWithFormat:@"line%d", [lineNum integerValue]];
UITextField *theField = [self valueForKey:fieldName]; theField.text = lineText;
}
UIApplication *app = [UIApplication sharedApplication]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification
object:app];
}
- (void)viewDidUnload { [super viewDidUnload];
//Release any retained subviews of the main view.
//e.g. self.myOutlet = nil;
self.line1 = nil; self.line2 = nil; self.line3 = nil; self.line4 = nil;
}
Then add the following new method down at the bottom, just before the @end marker:
- (void)applicationWillResignActive:(NSNotification *)notification { BIDAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [appDelegate managedObjectContext]; NSError *error;
for (int i = 1; i <= 4; i++) {
NSString *fieldName = [NSString stringWithFormat:@"line%d", i]; UITextField *theField = [self valueForKey:fieldName];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"
inManagedObjectContext:context]; [request setEntity:entityDescription]; NSPredicate *pred = [NSPredicate
predicateWithFormat:@"(lineNum = %d)", i]; [request setPredicate:pred];
NSManagedObject *theLine = nil;
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil) { NSLog(@"There was an error!");
// Do whatever error handling is appropriate
}
www.it-ebooks.info
CHAPTER 13: Basic Data Persistence |
487 |
if ([objects count] > 0)
theLine = [objects objectAtIndex:0];
else
theLine = [NSEntityDescription insertNewObjectForEntityForName:@"Line"
inManagedObjectContext:context];
[theLine setValue:[NSNumber numberWithInt:i] forKey:@"lineNum"]; [theLine setValue:theField.text forKey:@"lineText"];
}
[context save:&error];
}
.
.
.
Now, let’s look at the viewDidLoad method, which needs to check if there is any existing data in the persistent store. If there is, it should load the data and populate the fields with it. The first thing we do in that method is to get a reference to our application delegate, which we then use to get the managed object context that was created for us.
BIDAppDelegate *appDelegate = UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
Next, we create an entity description that describes our entity.
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"
inManagedObjectContext:context];
The next order of business is to create a fetch request and pass it the entity description so it knows which type of objects to retrieve.
NSFetchRequest *request = [[NSFetchRequest alloc] init]; [request setEntity:entityDescription];
Since we want to retrieve all Line objects in the persistent store, we do not create a predicate. By executing a request without a predicate, we’re telling the context to give us every Line object in the store.
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
We make sure we got back a valid array, and log it if we didn’t.
if (objects == nil) { NSLog(@"There was an error!");
// Do whatever error handling is appropriate
}
Next, we use fast enumeration to loop through the array of retrieved managed objects, pull the lineNum and lineText values from it, and use that information to update one of the text fields on our user interface.
for (NSManagedObject *oneObject in objects) {
NSNumber *lineNum = [oneObject valueForKey:@"lineNum"]; NSString *lineText = [oneObject valueForKey:@"lineText"];
www.it-ebooks.info
488 |
CHAPTER 13: Basic Data Persistence |
NSString *fieldName = [NSString stringWithFormat:@"line%@", lineNum];
UITextField *theField = [self valueForKey:fieldName]; theField.text = lineText;
}
Then, just as with all the other applications in this chapter, we register to be notified when the application is about to move out of the active state (either being shuffled to the background or exited completely), so we can save any changes the user has made to the data.
UIApplication *app = [UIApplication sharedApplication]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app]; [super viewDidLoad];
Let’s look at applicationWillResignActive: next. We start out the same way as the previous method, by getting a reference to the application delegate and using that to get a pointer to our application’s default context.
BIDAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [appDelegate managedObjectContext];
After that, we go into a loop that executes four times, one time for each label.
for (int i = 1; i <= 4; i++) {
We construct the name of one of the four fields by appending i to the word line and use that to get a reference to the correct field using valueForKey:.
NSString *fieldName = [NSString stringWithFormat:@"line%d", i];
UITextField *theField = [self valueForKey:fieldName];
Next, we create our fetch request:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
After that, we create an entity description that describes the Line entity we designed earlier in the data model editor and that uses the context we retrieved from the application delegate. Once we create the description, we feed it to the fetch request, so the request knows which type of entity to look for.
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"
inManagedObjectContext:context]; [request setEntity:entityDescription];
Next, we need to find out if there’s already a managed object in the persistent store that corresponds to this field, so we create a predicate that identifies the correct object for the field.
NSPredicate *pred = [NSPredicate predicateWithFormat:@"(lineNum = %d)", i];
[request setPredicate:pred];
www.it-ebooks.info
CHAPTER 13: Basic Data Persistence |
489 |
After that, we declare a pointer to an NSManagedObject and set it to nil. We do this because we don’t know yet if we’re going to load a managed object from the persistent store or create a new one. We also declare an NSError that the system will use to notify us of the specific nature of the problem if we get back a nil array.
NSManagedObject *theLine = nil;
NSError *error;
Next, we execute the fetch request against the context.
NSArray *objects = [context executeFetchRequest:request error:&error];
Then we check to make sure that objects is not nil. If it is nil, then there was an error, and we should do whatever error checking is appropriate for our application. For this simple application, we’re just logging the error and moving on.
if (objects == nil) { NSLog(@"There was an error!");
// Do whatever error handling is appropriate
}
After that, we check if an object that matched our criteria was returned. If there is one, we load it. If there isn’t one, we create a new managed object to hold this field’s text.
if ([objects count] > 0)
theLine = [objects objectAtIndex:0];
else
theLine = [NSEntityDescription insertNewObjectForEntityForName:@"Line"
inManagedObjectContext:context];
Then we use key-value coding to set the line number and text for this managed object.
[theLine setValue:[NSNumber numberWithInt:i] forKey:@"lineNum"]; [theLine setValue:theField.text forKey:@"lineText"];
}
Finally, once we’re finished looping, we tell the context to save its changes.
[context save:&error];
}
Making the Persistence View Controller the Application’s Root
Controller
Because we used the Empty Application template instead of the Single View Application template, we have one more step to take before our fancy new Core Data application will work. We need to create an instance of BIDViewController to act as our application’s root controller and add its view as a subview of our application’s main window. Let’s do that now.
First, the application delegate needs a property to point to our view controller. Select BIDAppDelegate.h, and make the following changes to declare that property:
www.it-ebooks.info
490CHAPTER 13: Basic Data Persistence
#import <UIKit/UIKit.h>
@class BIDViewController;
@interface BIDAppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) IBOutlet UIWindow *window;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (strong, nonatomic) BIDViewController *rootController;
-(void)saveContext;
-(NSURL *)applicationDocumentsDirectory;
@end
To make the root controller’s view a subview of the application’s window so that the user can interact with it, switch to BIDAppDelegate.m, and make the following changes at the top of that file:
#import "BIDAppDelegate.h"
#import "BIDViewController.h"
@implementation BIDAppDelegate
@synthesize window = _window;
@synthesize managedObjectContext = __managedObjectContext; @synthesize managedObjectModel = __managedObjectModel;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
@synthesize rootController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.rootController = [[BIDViewController alloc]
initWithNibName:@"BIDViewController" bundle:nil];
UIView *rootView = self.rootController.view; CGRect viewFrame = rootView.frame; viewFrame.origin.y += [UIApplication
sharedApplication].statusBarFrame.size.height; rootView.frame = viewFrame;
[self.window addSubview:rootView]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible];
return YES;
}
.
.
.
www.it-ebooks.info