iPhone开发之 Storyboard app with Core Data

转载自---http://maybelost.com/2011/12/tutorial-storyboard-app-with-core-data/

 

 

首先看一下完成效果图

  完成效果图

1.打开Xcode<本人xcode版本是4.2>

  选择Empty Application模板, 命名为PictureList, 复选框选中 Core Data 和 Use Automatic Reference Counting

  <可选>添加新的分组命名为Core Data, 把PictureList.xcdatamodeld拖到Core Data分组下

2.添加Entities

   1>点击Add Entity添加Users对象, 再在右边Attributes里添加username和password属性, 设置类型为String

 2>同上操作添加Pictures对象, 同样的方法为Pictures添加title<类型为String>desc<类型为String>smallPicture<类型为Binary Data>属性

   3>按下Shift键, 选中刚创建的两个对象, 然后在左边Core Data分组上右键, 依次操作New File --> Core Data --> NSManagedObject subclass

       完成之后效果如图

4.创建storyboard

 1>右键Supporting Files group依次操作 New File-->User Interface-->Storyboard 命名为PictureListStoryboard

   2>选中PictureList工程, 在右边把PictureListStoryboard设置Main Storyboard

 3>最后打开AppDelegate.m修改didFinishLaunchingWithOptions方法, 删掉除了return YES;之外的代码

5.添加默认用户到Core Data

   1>打开AppDelegate.m, 导入Users.h, 然后在didFinishLaunchingWithOptions方法添加如下代码

  
// Get a reference to the stardard user defaults
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; 
// Check if the app has run before by checking a key in user defaults
if ([prefs boolForKey:@"hasRunBefore"] != YES)
{
    // Set flag so we know not to run this next time
    [prefs setBool:YES forKey:@"hasRunBefore"];
    [prefs synchronize];
 
    // Add our default user object in Core Data
    Users *user = (Users *)[NSEntityDescription insertNewObjectForEntityForName:@"Users" inManagedObjectContext:self.managedObjectContext];
    [user setUsername:@"admin"];
    [user setPassword:@"password"];
 
    // Commit to core data
    NSError *error;
    if (![self.managedObjectContext save:&error])
       NSLog(@"Failed to add default user with error: %@", [error domain]);
}

6.添加Login View Controller

   1>在PictureList右键, 依次New file-->Cocoa Touch-->UIViewController Subclass 命名为LoginViewController

    修改LoginViewController.h  

    #import <UIKit/UIKit.h>
    @interface LoginViewController : UIViewController

    @property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
    @property (strong, nonatomic) IBOutlet UITextField *usernameField;
    @property (strong, nonatomic) IBOutlet UITextField *passwordField;
    @end

  修改LoginViewController.m
#import "LoginViewController.h"
#import "CoreDataHelper.h"
 
@implementation LoginViewController
 
//  Synthesize accessors
@synthesize managedObjectContext, usernameField, passwordField;
//  When we are done editing on the keyboard
- (IBAction)resignAndLogin:(id)sender
{
   //  Get a reference to the text field on which the done button was pressed
    UITextField *tf = (UITextField *)sender;
 
   //  Check the tag. If this is the username field, then jump to the password field automatically
   if (tf.tag == 1) {
      [passwordField becomeFirstResponder];
     //  Otherwise we pressed done on the password field, and want to attempt login
   } else {
       //  First put away the keyboard
       [sender resignFirstResponder];
       //  Set up a predicate (or search criteria) for checking the username and password
       NSPredicate *pred = [NSPredicate predicateWithFormat:@"(username == %@ && password == %@)", [usernameField text], [passwordField text]];
       //  Actually run the query in Core Data and return the count of found users with these details
       //  Obviously if it found ANY then we got the username and password right!
       if ([CoreDataHelper countForEntity:@"Users" withPredicate:pred andContext:managedObjectContext] > 0)
        //  We found a matching login user!  Force the segue transition to the next view
         [self performSegueWithIdentifier:@"LoginSegue" sender:sender];
        else
         //  We didn't find any matching login users. Wipe the password field to re-enter
          [passwordField setText:@""];
        }
  }
     //  When the view reappears after logout we want to wipe the username and password fields
     - (void)viewWillAppear:(BOOL)animated
    {
       [usernameField setText:@""];
       [passwordField setText:@""];
    } 
  @end            
2>在AppDelegate.m导入LoginViewController.h, 再didFinishLaunchingWithOptions方法添加如下代码
// Pass the managed object context to the root view controller (the login view)
LoginViewController *rootView = (LoginViewController *)self.window.rootViewController;
rootView.managedObjectContext = self.managedObjectContext;
7.添加登陆界面
 
1>打开PictureListStoryboard.storyboard
  添加一个View Controller到storyboard, 选中VC在Identity Inspector设置Class为LoginViewController
2>添加用户名和密码UILabelsText Fields, 右键点击或者按着Ctrl左键点击Login View Controller拖动到Username输入框
   ,在弹出的list里选择usernameField; 然后拖动到Password输入框, 选择passwordField
3>选中Username, 在右边的Attributes Inspector中设置Tag为1, 设置Password的Tag值为2, 并且设置Secure为选中状态
4>右键Username Field, 在弹出的list里点击Did end on Exit右边的小圆点, 拖动到Login VC, 选择resignAndLogin,为
   Password执行同样的操作
5>添加一个新的Table VC到storyboard作为登陆成功之后的界面, 右键点击Login VC拖动到刚添加的界面, 选中弹出列表里的Modal, 此时两个
界面已经创建关联, 选中刚才创建的VC, 设置Attributes Inspector<快捷键alt+cmd+4>里的Identifier为LoginSegue
8.添加Navigation Controller 和 logout
  选中Table VC, 依次Editor menu-->Embed In-->Navigator Controller, 拖动Bar Button Item到Table View navigation bar
   的左边, 然后双击修改名称为Logout, 再次添加Bar Button Item到右边, 然后修改Identifier为Add
9.创建PictureList Table
  1>右键PictureList分组, 依次New File-->Cocoa Touch-->UIViewController subclass-->设置父类为UITableViewController,
   命名:PictureListMainTable
  2>修改代码
  PictureListMainTable.h
 
#import <UIKit/UIKit.h>
@interface PictureListMainTable : UITableViewController
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSMutableArray *pictureListData;
- (void)readDataForTable;
@end
  PictureListMainTable.m
 
#import "PictureListMainTable.h"
#import "CoreDataHelper.h"
#import "Pictures.h"

@implementation PictureListMainTable
@synthesize managedObjectContext, pictureListData;
 
//  When the view reappears, read new data for table
- (void)viewWillAppear:(BOOL)animated
{
   //  Repopulate the array with new table data
   [self readDataForTable];
}
//  Grab data for table - this will be used whenever the list appears or reappears after an add/edit
- (void)readDataForTable
{
   //  Grab the data
   pictureListData = [CoreDataHelper getObjectsForEntity:@"Pictures" withSortKey:@"title" andSortAscending:YES andContext:managedObjectContext];
   //  Force table refresh
    [self.tableView reloadData];
}
#pragma mark - Actions
//  Button to log out of app (dismiss the modal view!)
- (IBAction)logoutButtonPressed:(id)sender
{
    [self dismissModalViewControllerAnimated:YES];
}
#pragma mark - Table view data source 
//  Return the number of sections in the table
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
//  Return the number of rows in the section (the amount of items in our array)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
   return [pictureListData count];
}
// Create / reuse a table cell and configure it for display
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   static NSString *CellIdentifier = @"Cell";
   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
   if (cell == nil) {
       cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    // Get the core data object we need to use to populate this table cell
    Pictures *currentCell = [pictureListData objectAtIndex:indexPath.row];
   //  Fill in the cell contents
    cell.textLabel.text = [currentCell title];
    cell.detailTextLabel.text = [currentCell desc];
    //  If a picture exists then use it
    if ([currentCell smallPicture])
    {
        cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
        cell.imageView.image = [UIImage imageWithData:[currentCell smallPicture]];
    }
    return cell;
}
//  Swipe to delete has been used.  Remove the table item
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete)
     {
          //  Get a reference to the table item in our data array
         Pictures *itemToDelete = [self.pictureListData objectAtIndex:indexPath.row];
         //  Delete the item in Core Data
         [self.managedObjectContext deleteObject:itemToDelete];
         //  Remove the item from our array
         [pictureListData removeObjectAtIndex:indexPath.row];
         //  Commit the deletion in core data
         NSError *error;
         if (![self.managedObjectContext save:&error])
             NSLog(@"Failed to delete picture item with error: %@", [error domain]);
          // Delete the row from the data source
         [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }   
}
@end

3>选中Table VC, 在Identity Inspector里设置关联Class为PictureListMainTable, 然后在Attributes Inspector里
  设置Table View, 首先style舍为Grouped, Separator设为Single Line, 然后选中prototype cell设置styleSubtitle
  在size inspector里设置Row Height的值为84
4>右键选中Logout按钮, 拖动到Picture List Main Table图标, 选择logoutButtonPressed
5>打开LoginViewController.m添加如下代码, 记得导入PictureListMainTable.h文件
  
//  When we have logged in successfully, we need to pass the managed object context to our table view (via the navigation controller)
//  so we get a reference to the navigation controller first, then get the last controller in the nav stack, and pass the MOC to it
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    UINavigationController *navController = (UINavigationController *)[segue destinationViewController];
    PictureListMainTable *piclist = (PictureListMainTable *)[[navController viewControllers] lastObject];
    piclist.managedObjectContext = managedObjectContext;
}

10.添加详细画面
  1>右键PictureList分组, 依次New File-->Cocoa Touch-->UIViewController subclass创建
    UITableViewController的子类, 命名为PictureListDetail
2>修改代码
   PictureListDetail.h
#import <UIKit/UIKit.h>
#import "Pictures.h"
@interface PictureListDetail : UITableViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) Pictures *currentPicture;
@property (strong, nonatomic) IBOutlet UITextField *titleField;
@property (strong, nonatomic) IBOutlet UITextField *descriptionField;
@property (strong, nonatomic) IBOutlet UIImageView *imageField;
@property (strong, nonatomic) UIImagePickerController *imagePicker;
@end
  PictureListDetail.h
    
#import "PictureListDetail.h"
@implementation PictureListDetail
@synthesize managedObjectContext;
@synthesize currentPicture;
@synthesize titleField, descriptionField, imageField;
@synthesize imagePicker;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
    [super viewDidLoad];
    // If we are editing an existing picture, then put the details from Core Data into the text fields for displaying
    if (currentPicture)
    {
        [titleField setText:[currentPicture title]];
        [descriptionField setText:[currentPicture desc]];
        if ([currentPicture smallPicture])
           [imageField setImage:[UIImage imageWithData:[currentPicture smallPicture]]];
    }
}
#pragma mark - Button actions
- (IBAction)editSaveButtonPressed:(id)sender
{
   // If we are adding a new picture (because we didnt pass one from the table) then create an entry
    if (!currentPicture)
        self.currentPicture = (Pictures *)[NSEntityDescription insertNewObjectForEntityForName:@"Pictures" inManagedObjectContext:self.manag    edObjectContext];
    // For both new and existing pictures, fill in the details from the form
    [self.currentPicture setTitle:[titleField text]];
    [self.currentPicture setDesc:[descriptionField text]];
    if (imageField.image)
   {
         // Resize and save a smaller version for the table
         float resize = 74.0;
         float actualWidth = imageField.image.size.width;
         float actualHeight = imageField.image.size.height;
         float divBy, newWidth, newHeight;
         if (actualWidth > actualHeight) {
           divBy = (actualWidth / resize);
            newWidth = resize;
            newHeight = (actualHeight / divBy);
         } else {
            divBy = (actualHeight / resize);
            newWidth = (actualWidth / divBy);
            newHeight = resize;
        }
        CGRect rect = CGRectMake(0.0, 0.0, newWidth, newHeight);
        UIGraphicsBeginImageContext(rect.size);
        [imageField.image drawInRect:rect];
        UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        // Save the small image version
        NSData *smallImageData = UIImageJPEGRepresentation(smallImage, 1.0);
        [self.currentPicture setSmallPicture:smallImageData];
    }
    //  Commit item to core data
    NSError *error;
    if (![self.managedObjectContext save:&error])
         NSLog(@"Failed to add new picture with error: %@", [error domain]);
       //  Automatically pop to previous view now we're done adding
      [self.navigationController popViewControllerAnimated:YES];
} 
//  Pick an image from album
- (IBAction)imageFromAlbum:(id)sender
{
    imagePicker = [[UIImagePickerController alloc] init];
    imagePicker.delegate = self;
    imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
    [self presentViewController:imagePicker animated:YES completion:nil];
}
//  Take an image with camera
- (IBAction)imageFromCamera:(id)sender
{
    imagePicker = [[UIImagePickerController alloc] init];
    imagePicker.delegate = self;
    imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
    imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
    [self presentViewController:imagePicker animated:YES completion:nil];
}
//  Resign the keyboard after Done is pressed when editing text fields
- (IBAction)resignKeyboard:(id)sender
{
   [sender resignFirstResponder];
}
#pragma mark - Image Picker Delegate Methods
 //  Dismiss the image picker on selection and use the resulting image in our ImageView
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo
{
    [imagePicker dismissModalViewControllerAnimated:YES];
    [imageField setImage:image];
}
//  On cancel, only dismiss the picker controller
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [imagePicker dismissModalViewControllerAnimated:YES];
}
@end

  3>修改PictureListMainTable.m
//  When add is pressed or a table row is selected
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    //  Get a reference to our detail view
    PictureListDetail *pld = (PictureListDetail *)[segue destinationViewController];
    //  Pass the managed object context to the destination view controller
    pld.managedObjectContext = managedObjectContext;
    //  If we are editing a picture we need to pass some stuff, so check the segue title first
    if ([[segue identifier] isEqualToString:@"EditPicture"])
    {
        //  Get the row we selected to view
        NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];
        //  Pass the picture object from the table that we want to view
        pld.currentPicture = [pictureListData objectAtIndex:selectedIndex];
    }
}

11.修改详细信息页面
  1>添加一个新的Table VC, 然后在Identity Inspector中设置关联类为PictureListDetail, 然后在
    attributes inspector中设置content为Static Cells, sections值为2, style为Grouped
  2>右键选中"Add"按钮,拖动到Picture List Detail, 选择Push, 然后选中Segue, 修改identifier
    为AddPicture,接着在Picture List Main Table中选中单个Cell,右键拖动到Picture List Detail
    选择Push,然后修改Segue值为EditPicture
  3>选中Picture List Detail里的Cell,删除Cell的名字,修改高度为95,添加一个Image View,设置为80x80px
    添加两个按钮,命名为"Pick from album","Take a photo"; 第二个Cell,删除一行,然后给第一行添加标签
    Title,添加Text Field,在Attributes Inspector中设置,第二行添加标签Description和TextField
  4>拖动Bar Button Item添加到Table VC的右上角,命名为"Save",在Attributes Inspector中设置Identifier
    为Save;右键选中Save按钮,拖动至Picture List Detail,选择editSaveButtonPressed;
  5>右键选中 Picture List Detail,拖动至Image View,选中imageField; 同样给titleField和descriptionField
    进行设置,右键Text Field,拖动Did end on exit后的circle到Picture List Detail,选择resignKeyboard
  6>右键"Pick from Album"按钮到Picture List Detail,选择imageFromAlbum;右键"Take a photo"按钮,
   选择imageFromCamera
posted @ 2012-05-06 17:58  潁川君  阅读(1816)  评论(0编辑  收藏  举报