Passing a Selected Object to a New View Controller via Push

1.3k views Asked by At

So I downloaded the following project to teach myself a little more about passing objects through views and such, and I've run into a little problem: https://github.com/rlgoldberg2/MyStore

My View Controllers

  • DeviceViewController - home view, options to add or edit object from tableview row
  • DeviceDetailViewController - where the editing or creation of an object occurs

On the Storyboard, I changed the transition-type between the two View Controllers from Model to Push (to enable to the glorious UINavigationBar that comes with the Navigation Controller). The adding of a new object to the UITableView works fine, but it's when I go from to the Home View and select an object to edit (from the UITableView), the object opens up blank in the Detail View - meaning the TextFields are blank. I believe I need to edit some code in the following little code snippet of the DeviceViewController to make it compatible with the Push transition?

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
 if ([[segue identifier] isEqualToString:@"UpdateDevice"]) {
     NSManagedObject *selectedDevice = [self.devices objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
     DeviceDetailViewController *destViewController = segue.destinationViewController;
     destViewController.device = selectedDevice;
 }

So how do I fix this issue? Any ideas?


In the event that I am wrong - which I most likely am, and you don't feel the need to download the project for yourself, here is the entirety of DeviceViewController.m:

//
//  DeviceViewController.m
//  MyStore
//
//  Created by Richard Goldberg on 10/11/13.
//  Copyright (c) 2013 Richard Goldberg. All rights reserved.
//

#import "DeviceViewController.h"

@interface DeviceViewController ()

@property (strong) NSMutableArray *devices;

@end

@implementation DeviceViewController

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (NSManagedObjectContext *)managedObjectContext
{
    NSManagedObjectContext *context = nil;
    id delegate = [[UIApplication sharedApplication] delegate];
    if ([delegate performSelector:@selector(managedObjectContext)]) {
        context = [delegate managedObjectContext];
    }
    return context;
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // Fetch the devices from persistent data store
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Device"];
    self.devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];

    [self.tableView reloadData];
}

#pragma mark - Table view data source


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return self.devices.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...
    NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
    [cell.textLabel setText:[NSString stringWithFormat:@"%@ %@", [device valueForKey:@"name"], [device valueForKey:@"version"]]];
    [cell.detailTextLabel setText:[device valueForKey:@"company"]];

    return cell;
}


// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}



// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle) editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSManagedObjectContext *context = [self managedObjectContext];

    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete object from database
        [context deleteObject:[self.devices objectAtIndex:indexPath.row]];

        NSError *error = nil;
        if (![context save:&error]) {
            NSLog(@"Can't Delete! %@ %@", error, [error localizedDescription]);
            return;
        }

        // Remove device from table view
        [self.devices removeObjectAtIndex:indexPath.row];
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }   
}


/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/

/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}
*/


#pragma mark - Navigation

// In a story board-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
 if ([[segue identifier] isEqualToString:@"UpdateDevice"]) {
     NSManagedObject *selectedDevice = [self.devices objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
     DeviceDetailViewController *destViewController = segue.destinationViewController;
     destViewController.device = selectedDevice;
 }

}



@end
2

There are 2 answers

1
Duncan C On

You can't use a push segue unless your view controllers are managed by a navigation controller. You should be able to select the root scene in your storyboard and select embed in Navigation Controller to make your app managed by a navigation controller.

Once you do that, your push segue should work, like your modal segues did.

In the code you posted, your DeviceViewController class has a devices property (an array of device objects, whatever they are.)

Your PrepareForSegue method is trying to set a device (singular) property in the destination view controller, not devices.

0
rdelmar On

The problem was, that when you switched from a modal to a push, you deleted and remade the connection and never entered an identifier for the new segue. That caused the if clause in prepareForSegue to evaluate as false, so the selectedDevice was never passed to the detail controller.