Pergunta

EDIT: Post heavily updated to support Mike A.'s NSFetchedResultsController methods, please see edit history for previous NSFetchRequest implementation.

I have a simple survey app with multiple ViewControllers -

StartViewController
InfoViewController
Q1ViewController
...
Q4ViewController
EndViewController
ResultsViewController
DetailsViewController

My managedObject is created on my InfoViewController:

// Identify the app delegate
MMUAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

// Use appDelegate object to identify managed object context
NSManagedObjectContext *context = [appDelegate managedObjectContext];

// Create new managed object using the MMU entity description
NSManagedObject *ManagedObjectMMU;
ManagedObjectMMU = [NSEntityDescription insertNewObjectForEntityForName:@"MMU" inManagedObjectContext:context];

// Declare managed object
self.ManagedObjectMMU = ManagedObjectMMU;

Each view has buttons and text fields and a navController. When a button is selected or text entered, upon tapping the 'next' button on the navigation bar, each value gets saved in my managedObject like so:

- (IBAction)nextBtnPressed:(UIButton *)sender {
// error checks etc before
    [self.ManagedObjectMMU setValue:timeStamp forKeyPath:@"timeStamp"];
    [self.ManagedObjectMMU setValue:timeStamp forKeyPath:@"name"];
    [self.ManagedObjectMMU setValue:timeStamp forKeyPath:@"doB"];
// next viewcontroller pushed here
}

The ManagedObjectMMU gets passed through each view until it reaches the ENDViewController where it is saved to the managedObjectContext.

// Pass managed object to next ViewController
Q1.ManagedObjectMMU = self.ManagedObjectMMU;

Saving all results:

// Save managed object to managedObjectContext
NSError *error;
[[self.ManagedObjectMMU managedObjectContext] save:&error];

My model is very basic, 10 attributes all of string type.

Data Model

Here is an example of a complete submission:

data: {
    dateOfBirth = "Sep 5, 1989";
    gender = Male;
    name = Ross;
    q1 = "Computing, Mathematics & Digital Technology";
    q2 = Yes;
    q3 = Football;
    q4 = "Commute From Home";
    q5 = Bicycle;
    status = University;
    timeStamp = "3/26/14, 2:56 PM";
}

The ResultsViewController is a UITableView of all results, each cell.textLabel.text containing the name of each submission, and the cell.detailLabel.text containing the timeStamp. I'm using NSFetchedResultsController to fetch the results from my model (please see Mike A.'s answer, I have implemented his methods).

ResultsViewController

What I want to happen is when the user taps on a cell, the fetched result object with that particular 'name' gets passed to the next view, displaying the data associated with that object (dataOfBirth, gender, q1,..., etc).

My question is: how can I pass the already fetched object to the next view to display all objects for that particular name? Can I display these specified objects on another UITableView or should I create some UITextFields and Labels to display the results? Should I pass the fetched object in the tableView:didSelectRowAtIndexPath: method or should I implement prepareForSegue? Sorry if this is a simple solution, I've searched online for days now and can't find a suitable solution! Thanks.

UPDATE:

Thanks to Mike A's suggestions and code snippets, I implemented the solution using the following code:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    }

    // Configure the cell...
    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    MMU *results = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = results.name;
    cell.detailTextLabel.text = results.timeStamp;

}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    DetailViewController *detailView = [[DetailViewController alloc]initWithNibName:@"DetailViewController" bundle:nil];

    // Pass fetched object to next ViewController
    detailView.ManagedObjectMMU = [self.fetchedResultsController objectAtIndexPath:indexPath];

    // Push the view controller.
    [self.navigationController pushViewController:detailView animated:YES];
}

And the DetailView simply has some textfields and labels to display the questions and appropriate submissions for the object passed through from the cell:

- (void)displayDetails {
    self.nameDetail.text = [_ManagedObjectMMU valueForKey:@"name"];
    self.genderDetail.text = [_ManagedObjectMMU valueForKey:@"gender"];
    self.dateOfBirthDetail.text = [_ManagedObjectMMU valueForKey:@"dateOfBirth"];
    self.statusDetail.text = [_ManagedObjectMMU valueForKey:@"status"];
    self.timeStampDetail.text = [_ManagedObjectMMU valueForKey:@"timeStamp"];

    self.q1Answer.text = [_ManagedObjectMMU valueForKey:@"q1"];
    self.q2Answer.text = [_ManagedObjectMMU valueForKey:@"q2"];
    self.q3Answer.text = [_ManagedObjectMMU valueForKey:@"q3"];
    self.q4Answer.text = [_ManagedObjectMMU valueForKey:@"q4"];
    self.q5Answer.text = [_ManagedObjectMMU valueForKey:@"q5"];
}

I couldn't have done it without Mike A, please read through his answer and the comments below it if you want to understand the solution!

Foi útil?

Solução

I would recommend using an NSFetchedResultsController in each view controller. That way you don't have to deal with arrays in every view controller, only fetch requests. I would not recommend creating them in the App Delegate though. Preferably in each view controller or in a category.

So in your view controller try creating a property for an NSFetchedResultsController and implement its getter.

NSFetchedResultsController example

@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;


- (NSFetchedResultsController *)fetchedResultsController
{
    if (!_fetchedResultsController) {

        _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:[self fetchRequest]
                                                                        managedObjectContext<# your managedObjectContext #>
                                                                          sectionNameKeyPath:nil
                                                                                   cacheName:nil];

        self.fetchedResultsController.delegate = self;

        NSError *error = nil;
        if (![self.fetchedResultsController performFetch:&error]) {
            NSLog(@"Error: %@ %@", error.localizedDescription, error.userInfo);
        }
    }

    return _fetchedResultsController;
}

and implement a method for your fetch request:

- (NSFetchRequest *)fetchRequest
{
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"MMU"];

    // predicates, sort descriptors and other options go here...

    return fetchRequest
}

And then you can update your tableview data source by using

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

and

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    }

    MMU *results = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = [NSString stringWithFormat:@"%@ ",results.name]; // Consider results.name without stringWithFormat.
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ ", results.timeStamp];

    return cell;
}

Now after you set up your view controller like this you update the model when you touch or place text somewhere. In your next view controller you can set up a new fetched results controller and a new fetch request, requesting the already changed data.

Depending on the number of view controllers you have, a better approach would be to subclass your UITableViewController to implement a fetched results controller by default and change the fetch request each time.

If this didn't help, please provide some more information about how you present your new view controllers and how you update your model.

Update

You can pass each object from view controller to view controller by implementing either prepareForSegue: or tableView:didSelectRowAtIndexPath:. In this case you will have to declare a public property in your header file for each view controller you want to pass the object to. Please note that implementing these methods you will be navigated to the next view controller by tapping a cell and not by the IBAction you provided in your example.

If you are using segues for pushing view controllers you can use the following example.

prepareForSegue: Example

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 
{
   [super prepareForSegue:segue sender:sender];

   UIViewController *nextViewController = segue.destinationViewController;
   nextViewController.mmuModel = self.mmuModel; // The initial model that you have from InfoViewController.
}

This method will share your mmuModel with the next view controller. However you will need to update the model in each view controller separately.

This will get the job done, however CoreData is not meant to be used in such a way. CoreData is a powerful framework which usually does a lot of stuff for you. Another approach is to create a new NSManagedObject in InfoViewController like you do and then instead of passing the managed object in the question view controllers you can fetch it each time using an NSFetchedResultsController like I showed you. Only this time you will have to figure out a way of filtering the results so you can get the object you are looking for. There are multiple ways to do this, one approach would be to use some sort of id and maybe a predicate.

Moreover in your data model, instead of having everything in one entity (MMU), consider separating some things and using relationships.

Also it would be a good practive to different data types for your attributes i.e. timeStamp should be a Date, gender could be a BOOL if the only options are male, female, etc etc.

I hope this helped a bit. Let me know if you're still having issues.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top