I've basically got a Master-Detail app that utilizes RestKit
to pull data from a remote server.
The DetailViewController
acts as a data source for a PageViewController
that has three viewControllers. My question only deals with two of those so I will focus on that. I'll call the two important viewControllers
: FirstViewController
and SecondViewController
.
FirstViewController
displays the detail content of the item selected in the master view, basically an image and some text. Scrolling to the SecondViewController
displays a UITableView
that contains items related to the content in the main view. See this link How to implement UIPageViewController that utilizes multiple ViewControllers for an image of a similar storyboard to the app.
To clarify a little more imagine a recipe app in which you choose a recipe to view in the master scene. The detail scene displays the recipe selected. Scrolling to the next page displays a table with recipes related to the one chosen.
With the background established here is my question:
I am trying to set things up so that a user could select a cell in the tableview
and the DetailViewController
would reload with the data from the selected cell. For example the user sees a related recipe they are interested in, selects the cell, and the view reloads to the main page in the PageViewController
with the data from the selected cell.
I felt the easiest way of doing this might just be to pass some needed variables into the DetailViewController and call viewDidLoad
like so.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Grab index object at index path
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
//Initialize DVC class to utilize variables and methods
DetailViewController *vc = detailViewControllerId;
vc.articleId = [object valueForKey:@"articleId"];
vc.imageUrl = [object valueForKey:@"image"];
//Reload the DetailViewController
[vc viewDidLoad];
}
This works with one exception:
Calling viewDidLoad
has done what I hoped. viewDidLoad
starts a sequence of API calls to grab the data from the server and then populates the content viewControllers
of the PageViewController
. However I need something that will essentially reload it from the beginning blowing out past data.
Does anyone have any better suggestions to accomplish what I am trying to do? Thanks
==========EDIT===============
Here is some more info that may be needed.
Starting from the beginning a user selects a cell in the MasterViewController
which triggers a segue to the DetailViewController
. I am using prepareForSegue
to pass needed data into the DetailViewController
.
The DetailViewController
acts as the data source for the PageViewController
so when viewDidLoad
is called in the DetailViewController
a series of methods is called populate the data source elements required in the PageViewController
. Here is the basics.
DetailViewController.h
@interface DetailViewController : UIViewController<UINavigationControllerDelegate, UIPageViewControllerDataSource>
{
}
@property (strong, nonatomic) UIPageViewController *pageViewController;
@property (strong, nonatomic) NSURL *imageUrl;
@property (strong, nonatomic) NSData *passingPhotoData; //used to pass to the pageViewController;
@property (strong, nonatomic) NSAttributedString *passingString; //passes text to pageViewController;
@property (strong, nonatomic) NSArray *vc1;
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSString *articleId;
@end
DetailViewController.m
-(void)viewDidLoad
{
//There is other things in the method but these are the only significant line for the question.
//The method call below is to start a call to the server to get the information needed about the cell that was selected.
vc1 = [[NSArray alloc] init];
[self getDetailInfoFromServer]
}
-(void)getDetailInfoFromServer
{
//Here I make a call to the server using Restkit… it is not significant for the question so I'll leave it out.
//The data that is retrieved from this server call is everything that I will use to populate the FirstViewController which is again an image and some text.
//If the call to the server was successful RestKit maps out the data in Core Data and I move on to the next method.
[self getInfoRelatedToDetailView]
}
-(void)getInfoRelatedToDetailView
{
//The data that is retrieved from this server call is everything that I will use to populate the SecondViewController.
//The SecondViewController is a tableView and the data retrieved will be an array of images and the associated text.
//If the call to the server was successful Restkit will again map the data into Core Data entities and I move on to the next method.
[self getTextForFVC]
}
-(void)getTextForFVC
{
//In this method I fetch the text from Core Data that was received back in getDetailInfoFromServer.
//It is all HTML which I parse to a string using DTCoreText and finally set passingString equal to the result.
//passingString is one of the variables I use to pass needed info to the FirstViewController.
self.passingString = parsedString
//At this point I start the setup of the pageViewController, this is just standard stuff that goes with a PageViewController
FirstViewController *selectedController = [self populateFirstViewController];
vc1 = @[selectedController];
// Create page view controller
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"PageViewController"];
self.pageViewController.dataSource = self;
[self.pageViewController setViewControllers:vc1 direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
//Notice above the call to a method populateFirstViewController That is where I go next.
-(FirstViewController *)populateFirstViewController
{
//This is a simple method that gets called whenever the PageViewController delegate methods deem it necessary to load the FirstViewController.
FirstViewController *fvc = [self.storyboard instantiateViewControllerWithIdentifier:@"FirstController"];
fvc.imageData = self.passingPhotoData; //This variable was set earlier but I left out the code
fvc.nodeText = self.passingString;
return nvc;
}
-(SecondViewController *)populateSecondViewController
{
//This method is like the one above, it is called when the PVC delegate methods deem it necessary to load the page
SecondViewController *svc =[self.storyboard instantiateViewControllerWithIdentifier:@"SecondController"];
//Here are some of the variables I pass in
svc.managedObjectContext = self.managedObjectContext;
cvc.detailViewControllerId = self; //This line allows me to call the viewDidLoad method for the DetailViewController from the SecondViewController.
return cvc;
}
Once all this take place the PageViewController
appears with the data received from the server. The FirstViewController handles populating the UIImageView
and UITextField
. Likewise the SecondViewController handles populating the UITableView
.
Now for the meat of the question… A user sees something they are interested in the table and selects it. I use the didSelectRowAtIndexPath
to get information I need for the calls to the server and then need the PageViewController
to reload. Given that the DetailViewController
is the data source for the PVC
, I need the data source to refresh/reload. I thought it would be easy to just call viewDidLoad
to start the process but I'm not sure it is the appropriate way. What might be the best way to do this?
Sorry for the Novel, just wanted to clarify for those that have put in their input.
The usual way to pass data back to a previous controller is through delegation. You should have a delegate protocol in the controller that shows the related recipes (in your example), and the initial detail controller should set itself as the delegate. When the user selects one of the related items, call the delegate method, which would pass back the data needed to reload the detail controller.
Now to your exceptions,
You get that error because your property declaration is wrong -- it should be,
@property (strong, nonatomic) DetailViewController *vc;
And in your code you should refer to it with self.vc, not create a local variable (which is what you're doing in the posted code) with the same name as the property,
self.vc = detailViewControllerId;
It's not really clear what you're doing with that line anyway, since you don't say what detailViewControllerId is.