Attempt to Present whose view not in window hierarchy - ios

I am having the following issue when presenting the UITableViewController.
List[12426:444700] Warning: Attempt to present <UserTableViewController: 0x7fe4ba5d8e50> on <UserViewController: 0x7fe4ba5b6db0> whose view is not in the window hierarchy!
Here's the part of my code
#implementation UserTableViewController
#synthesize users;
- (void)viewDidLoad {
// UserTableViewController
// Create Fetch Request in viewDidLoad
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
//Create NSManagedObjectContext instance
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entity];
[request setReturnsObjectsAsFaults:NO];
NSError *error;
NSArray *array = [[context executeFetchRequest:request error:&error]mutableCopy];
//setting users with array of fetched objects
[self setUsers:array];
NSLog(#"firstName is %#",array);
//[self presentViewController:UserTableViewController animated:YES completion:nil];
}
Really appreciate help.

You are attempting to present ([self presentViewController:animated:completion:]) UserTableViewController from within your UserViewController when your UserViewController instance is not part of the window hierarchy, e.g. on the navigation stack (has been pushed or presented itself).

Related

Search NSFetchedResults by changing Settings in another view

I have read a lot of content and watched several tutorials on how to search Core Data by using a search bar however I have not yet seen anything for how to change a the sort descriptor by passing a setting from a Settings view to the Table View.
I have a search bar button item that when pressed goes to a SearchSettingsVC. The views communicate by passing boolean's from one VC to the other and vise-versa. The problem is that the table is not corresponding accordingly by changing the TableVC order - (I have tried calling self.tableview beginUpdates, self.tableview reload, self fetchedResultsController among other things).
The point is to reorder the TableVC results, not to present only specific results like a predicate does
I have created a delegate for the SettingsVC so that I can pass boolean value to the SettingsVC which is then capable of returning a different changed value if there are any changes.
The problem is that I cannot manage to get the table view (or prehaps even the fetched results) to update.
Here is my code for my -(NSFetchedResultsController*) fetchedResultsController method:
// return if already initialized
if (self.fetchedResultsController != nil) {
return self.fetchedResultsController;
}
if (dateSearch == true){
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
// the entity to fetch
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Details" inManagedObjectContext:managedObjectContext];
// how to sort the data
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"addDate" ascending:YES];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// fetch the data
NSError *e = nil;
if (![self.fetchedResultsController performFetch:&e]) {
NSLog(#"fetch error(Date): %#", e );
abort();
}
NSLog(#"Dates loaded");
}
if (mostAmount == true){
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
// the entity to fetch
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Details" inManagedObjectContext:managedObjectContext];
// how to sort the data
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"amount" ascending:NO];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// fetch the data
NSError *e = nil;
if (![self.fetchedResultsController performFetch:&e]) {
NSLog(#"fetch error (Most Fuel): %#", e);
abort();
}
NSLog(#"Amount loaded");
}
else{
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
// the entity to fetch
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Details" inManagedObjectContext:managedObjectContext];
// how to sort the data
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"addDate" ascending:YES];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// fetch the data
NSError *e = nil;
if (![self.fetchedResultsController performFetch:&e]) {
NSLog(#"fetch error(Date): %#", e );
abort();
}
NSLog(#"Defualt loaded");
}
return self.fetchedResultsController;
I required a default because at the beginning when I initialise the booleans in the TableVC to false they can be updated when I go to my settings VC.
My SearchSettingsVC has UISwitches that change the values from true to false (and vice-versa) which successfully update the equivalent booleans in the TableVC as represented when I return to the SearchSettingsVC.
My prepareForSegue code in my TableVC
if ([[segue identifier] isEqualToString:#"searchSettings"]){
//pass new search settings in here
SearchSelectionSettings * settingsVC = (SearchSelectionSettings *)segue.destinationViewController;
settingsVC.delegate = self;
settingsVC.dateSearch = dateSearch;
settingsVC.mostAmount = mostAmount;
This is my closeSettings method which is located in my TableVC
#pragma mark - SettingsViewControllerDelegate methods
//record the settings changed in the settings view
//dismissViewController changes the screen
- (void)closeSettings:(id)sender {
NSLog(#"Working?");
dateSearch = ((SearchSelectionSettings *)sender).dateSearch;
mostAmount = ((SearchSelectionSettings *)sender).mostAmount;
[self dismissViewControllerAnimated:YES completion:nil];
[self FetchedResultsController];
NSIndexPath * indexPath;
[self tableView:self.tableView cellForRowAtIndexPath:indexPath];
}
Passing boolean values from one VC to other works perfectly. Getting the TableView to update according to the fetchedResults if statements does not, my switches in the SettingsVC are updated every time without any issues. Can anyone help or recommend a tutorial?
The opening lines of the fetchedResultscontroller getter, i.e.:
// return if already initialized
if (self.fetchedResultsController != nil) {
return self.fetchedResultsController;
}
mean that, once your fetchedResultsController has been created, the remaining code will not be executed when you access it. A quick way to resolve your problem would therefore be to set self.fetchedResultsController to nil in your closeSettings method, and then reload your table view. When the tableview reloads, it will access the fetchedResultsController again, and since it is now nil, the above code will be bypassed and your code will be used.
#pragma mark - SettingsViewControllerDelegate methods
//record the settings changed in the settings view
//dismissViewController changes the screen
- (void)closeSettings:(id)sender {
NSLog(#"Working?");
dateSearch = ((SearchSelectionSettings *)sender).dateSearch;
mostAmount = ((SearchSelectionSettings *)sender).mostAmount;
[self dismissViewControllerAnimated:YES completion:nil];
self.fetchedResultsController = nil;
[self.tableView reloadData];
}
Alternatively, you can modify the fetchedResultsController's fetch and then get it to reperform the fetch:
#pragma mark - SettingsViewControllerDelegate methods
//record the settings changed in the settings view
//dismissViewController changes the screen
- (void)closeSettings:(id)sender {
NSLog(#"Working?");
dateSearch = ((SearchSelectionSettings *)sender).dateSearch;
mostAmount = ((SearchSelectionSettings *)sender).mostAmount;
[self dismissViewControllerAnimated:YES completion:nil];
NSFetchRequest *request = [[NSFetchRequest fetchRequestWithEntityName:#"Details"];
NSSortDescriptor *sort;
if (dateSearch == true){
sort = [NSSortDescriptor sortDescriptorWithKey:#"addDate" ascending:YES];
NSLog(#"Dates loaded");
} else if (mostAmount == true) {
sort = [NSSortDescriptor sortDescriptorWithKey:#"amount" ascending:NO];
NSLog(#"Amount loaded");
} else {
sort = [NSSortDescriptor sortDescriptorWithKey:#"addDate" ascending:YES];
NSLog(#"Default loaded");
}
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
self.fetchedResultsController.fetchRequest = request;
// fetch the data
NSError *e = nil;
if (![self.fetchedResultsController performFetch:&e]) {
NSLog(#"fetch error (Most Fuel): %#", e);
abort();
}
[self.tableView reloadData];
}
That way, you can simplify your fetchedResultsController code so it just loads the default.

Update TableView after adding CoreData

I'm working on a CoreData App which displays the content of the CoreData in a TableView.
The Problem is, after adding a string the tableView is not Updating the Content of the CoreData.
i tried it with:
- (void)viewWillAppear:(BOOL)none
{
[self.tableView reloadData];
}
But no Chance, its not reloading.
Any guesses or should I post some more Code? thank you :)
So more Code :)
Saving string to CoreData:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
AppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSManagedObject *newProduct;
newProduct = [NSEntityDescription
insertNewObjectForEntityForName:#"Products"
inManagedObjectContext:context];
[newProduct setValue: _textField.text forKey:#"productName"];
NSError *error;
[context save:&error];
NSLog(#"Product saved");
}
And the Code of the TableViewController:
#implementation CartViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//Core Data Versuche
/*
Fetch existing events.
Create a fetch request for the Event entity; add a sort descriptor; then execute the fetch.
*/
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Products"];
[request setFetchBatchSize:20];
// Order the events by creation date, most recent first.
// NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"creationDate" ascending:NO];
// NSArray *sortDescriptors = #[sortDescriptor];
// [request setSortDescriptors:sortDescriptors];
// Execute the fetch.
NSError *error;
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSArray *fetchResult = [delegate.managedObjectContext executeFetchRequest:request error:&error];
if (fetchResult== nil) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
// Set self's events array to a mutable copy of the fetch results.
[self setCartProductsArray:[fetchResult mutableCopy]];
/*
Reload the table view if the locale changes -- look at APLEventTableViewCell.m to see how the table view cells are redisplayed.
*/
__weak UITableViewController *cell = self;
[[NSNotificationCenter defaultCenter] addObserverForName:NSCurrentLocaleDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[cell.tableView reloadData];
}];
// Ende Core Data Versuche
}
- (void)viewWillAppear:(BOOL)none
{
[self.tableView reloadData];
}
What you probably want to do is use an NSFetchedResultsController.
Make sure that when you are updating the data, you are really just updating CoreData.
When things change in CoreData and you have an NSFetchedResultsController you can subscribe to updates. Apple has great documentation on how to make this work. These updates will update your table correctly using the delegation methods. This ends up mostly being a copy and paste from the documentation.

how to pass objects between view controllers

I have a table view that lists athletes. when an athlete is selected, I wish to have the detail view controller (the controller that is pushed onto the stack) to know all the attributes about the athlete. his/her name, birthday, phone number, etc. But im unsure on how to pass this information.
allathletes.h
-(void)viewWillAppear:(BOOL)animated{
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
_managedObjectContext = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *athlete = [NSEntityDescription entityForName:#"Athlete" inManagedObjectContext:_managedObjectContext];
[request setEntity:athlete];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"last" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[_managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil){
//handle error
}
[self setAthleteArray:mutableFetchResults];
[self.tableView reloadData];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
NSString *segueIdentifier = [segue identifier];
if ([segueIdentifier isEqualToString:#"setAthlete"])
{
UINavigationController *navController = (UINavigationController *)[segue destinationViewController];
AllAthletes *athleteList = (AllAthletes *)[[navController viewControllers] lastObject];
//the line below gets an error :(
AthleteDetail.managedObjectContext = self.managedObjectContext;
}
}
Before pushing a detail view controller, set a property on it with the data to be displayed, like:
myDetailViewController.myModel = selectedModel;
In the detail view, you can set up the view using this data in viewWillAppear.
I think you're going to want to use delegates. Here is a great tutorial on how to do that: Link

Core Data Detail View with relationship

I have set up a 1 to many relationship on my core data entities. I am trying to show the detailview copy of the associated data. Currently I have the prepareforseague: method working with the original entity(Routines), however I am at a lose as to how to show the linked entity (RoutinesDetails).
FBCDRoutineViewController
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Routines"];
self.routines = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"UpdateDevice"]) {
NSManagedObject *selectedDevice = [self.routines objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
FBCDRoutineViewController *destViewController = segue.destinationViewController;
destViewController.routines = selectedDevice;
}
FBCDRoutineDetailViewController
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"RoutinesDetails"];
self.routines = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
}
- (void)viewDidLoad
{
[[self navigationController] setNavigationBarHidden:NO animated:YES];
[super viewDidLoad];
// Do any additional setup after loading the view.
if (self.routines) {
[self.testLabel setText:[self.routines valueForKey:#"routinename"]];
}
}
FBCDRoutineDetailViewController
#property (strong) NSManagedObject *routines;
This is my first time with core data and I am looking at how to show the Details entity. Am I Close to getting it working? If not can I get directed at to what I should be looking at.
Any suggestions?
If I understand your problem correctly, you want to display all RoutinesDetails objects
that are related to the Routines object passed in prepareForSegue.
Then you would declare two properties in the FBCDRoutineDetailViewController:
#property (strong) NSManagedObject *routines; // the passed object
#property (strong) NSManagedObject *routineDetails; // the displayed details objects
and fetch them like this:
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"RoutinesDetails"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"routineinfo == %#", self.routines];
[fetchRequest setPredicate:predicate];
NSError *error;
self.routineDetails = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
self.routineDetails is now the data source array for the details view.
(Remark: For displaying the result set of a Core Data request in a table view,
you might also consider to use a NSFetchedResultsController.)
I think I see several problems--mostly related to the timing of these various calls.
I believe viewDidLoad is called on your detail view before the prepareForSegue is called. So your code in viewDidLoad is trying to display data about your detail item before it has been set.
Then the code in viewDidAppear looks to be overwriting the value you set in prepareForSegue, which doesn't make sense to me (although by this time the view is already displayed and it's not going to affect the label you tried to set in viewDidLoad).
Also, executeFetchRequest: returns an NSArray, not an NSManagedObject, so assigning the result of your fetch to your NSArray property is a BAD idea.

What am I doing wrong with my FetchedResultsController that it gives me the warning for setting its delegate?

I have the following code:
- (NSFetchedResultsController *)fetchedResultsController {
if (!_fetchedResultsController) {
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ArticleInfo" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = entity;
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:descriptor];
request.fetchBatchSize = 20;
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:#"Root"];
_fetchedResultsController = fetchedResultsController;
_fetchedResultsController.delegate = self;
}
return _fetchedResultsController;
}
It reports this error back:
Assigning to 'id' from
incompatible type 'RootViewController *const __strong'
What exactly am I doing wrong to cause that?
You need to let the compiler know that your class conforms to the NSFetchedResultsControllerDelegate protocol.
You can do this by adding it to your .h file
#interface RootViewController : UITableViewController <NSFetchedResultsControllerDelegate>
or possibly more preferable add it to a class extension. So in your .m file
#interface RootViewController () <NSFetchedResultsControllerDelegate>
The reasoning why this could be considered as being a little better is that it's better information hiding - no other class needs to know that RootViewController conforms to the NSFetchedResultsControllerDelegate protocol so why make it publicly visible?

Resources