Reputation: 2811
I have a table view controller and a view controller.
StackTableViewController - list of strings
HomeViewController - empty view controller with a label
The HomeViewController label should present always the first sting of the StackTableViewController.
I need to make sure if the first string is deleted to present the new first string.
And this is where I have the problem...if I delete the first string and going back to the HomeViewController, the label is still the string I just deleted....And if I terminate the app and open it again, the correct string shown in the label.
This is How I did it so far:
this is the relevant methods in my StackTableViewController.h + .m:
@protocol StackTableViewControllerDelegate <NSObject>
@optional
-(void)didDeleteObject;
@end
@interface StackTableViewController : UITableViewController <UITableViewDataSource,UITableViewDelegate>
@property (strong,nonatomic) id<StackTableViewControllerDelegate> delegate;
@property (strong, nonatomic) NSString *currentTarget;
@end
#import "StackTableViewController.h"
#import "Target.h"
#import "StackTableViewCell.h"
#import "HomeViewController.h"
#import "CoreDataStack.h"
@interface StackTableViewController () <NSFetchedResultsControllerDelegate>
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultController;
@end
@implementation StackTableViewController
- (id)init {
self = [super initWithNibName:@"StackTableViewController" bundle:nil];
if (self) {
// Do something
[self.fetchedResultController performFetch:nil];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
Target *current = [self.fetchedResultController objectAtIndexPath:indexPath];
self.currentTarget = current.body;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
Target *current = [self.fetchedResultController objectAtIndexPath:indexPath];
self.currentTarget = current.body;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
CoreDataStack *stack = [CoreDataStack defaultStack];
[[stack managedObjectContext] deleteObject:target];
[stack saveContext];
if ([_delegate respondsToSelector:@selector(didDeleteObject)]) {
[_delegate didDeleteObject];
}
}
And this is the relevant methods in the HomeViewController.h + .m:
#import <UIKit/UIKit.h>
#import "StackTableViewController.h"
@interface HomeViewController : UIViewController {
StackTableViewController *stackTableViewController;
}
@property (strong, nonatomic) IBOutlet UILabel *homeLabel;
- (IBAction)goToStack:(id)sender;
#import "StackTableViewController.h"
@interface HomeViewController () <StackTableViewControllerDelegate>
@end
@implementation HomeViewController
- (id)init {
self = [super initWithNibName:@"HomeViewController" bundle:nil];
if (self) {
// Do something
stackTableViewController = [[StackTableViewController alloc] init];
stackTableViewController.delegate = self;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self.navigationController setNavigationBarHidden:YES];
self.homeLabel.font = [UIFont fontWithName:@"Candara-Bold" size:40];
self.homeLabel.text = stackTableViewController.currentTarget;
}
- (void)didDeleteObject {
self.homeLabel.text = stackTableViewController.currentTarget;
}
- (IBAction)goToStack:(id)sender {
StackTableViewController *vc = [[StackTableViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
}
CoreDataStack.h +.m:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@interface CoreDataStack : NSObject
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
+ (instancetype)defaultStack;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
@end
#import "CoreDataStack.h"
@implementation CoreDataStack
#pragma mark - Core Data stack
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
+ (instancetype)defaultStack {
static CoreDataStack *defaultStack;
static dispatch_once_t onceTocken;
dispatch_once (&onceTocken, ^{
defaultStack = [[self alloc] init];
});
return defaultStack;
}
- (NSURL *)applicationDocumentsDirectory {
// The directory the application uses to store the Core Data store file. This code uses a directory named "digitalCrown.Treats" in the application's documents directory.
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Treats" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Treats.sqlite"];
NSError *error = nil;
NSString *failureReason = @"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// Report any error we got.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
// Replace this 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]);
abort();
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// 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]);
abort();
}
}
}
@end
Please help me to solve this, I was tying allot of ways but probably i'm missing something that got to do with view controller lifecycle or something.
(the CoreDataStack is a singleton)
tnx!!
Upvotes: 3
Views: 214
Reputation: 442
- (IBAction)goToStack:(id)sender {
StackTableViewController *vc = [[StackTableViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
}
This method should be as below:
- (IBAction)goToStack:(id)sender {
StackTableViewController *vc = [[StackTableViewController alloc] init];
vc.delegate = self.
[self.navigationController pushViewController:vc animated:YES];
}
The reason for this is that you've setup HomeVC as delegate of StackTable in the init method, but you aren't using the same instance of StackTable and initializing a new instance.
Upvotes: 1
Reputation: 2859
You need make 2 changes:
goToStack:
add vc.delegate = self
before pushViewController:
StackTableViewController tableView: commitEditingStyle:
update currentTarget
before invoke [_delegate didDeleteObject]
Upvotes: 1
Reputation: 9354
Your problem is that you don't update StackTableViewController
property currentTarget
in commitEditingStyle
. Just update code in StackTableViewController.m
:
Also if you use same code more than 2 times - better move it to separate function
- (id)init {
self = [super initWithNibName:@"StackTableViewController" bundle:nil];
if (self) {
// Do something
[self.fetchedResultController performFetch:nil];
[self updateCurrentTarget];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
[self updateCurrentTarget];
}
- (void)updateCurrentTarget
{
if (self.fetchedResultsController.sections.count == 0 && [self.fetchedResultsController.sections.firstObject count] == 0) {
self.currentTarget = nil;
} else {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
Target *current = [self.fetchedResultController objectAtIndexPath:indexPath];
self.currentTarget = current.body;
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
CoreDataStack *stack = [CoreDataStack defaultStack];
[[stack managedObjectContext] deleteObject:target];
[stack saveContext];
[self updateCurrentTarget];
if ([_delegate respondsToSelector:@selector(didDeleteObject)]) {
[_delegate didDeleteObject];
}
}
Upvotes: 1
Reputation: 13766
There could be two issues need to be fixed here.
Delegate method didDeleteObject
is not called at all. In goToStack
method you create a new StackTableViewController
but forget to set its delegate, while you also have a global variable stackTableViewController
instance. I think you meant to push the global one onto the stack.
In commitEditingStyle
tableview delegate method, you forget to update the currentTarget
property which is used to update the label text, you can try to update that property and see what would happen.
For 1, you can change the goToStack
function like this:
[self.navigationController pushViewController:stackTableViewController animated:YES];
Which means StackTableViewController *vc = [[StackTableViewController alloc] init];
should be removed.
For 2, in commitEditingStyle
method, after [stack saveContext]
.
[stack saveContext];
//Please add the following code.
NSIndexPath *theIndexPath = [NSIndexPath indexPathForItem:0 inSection:0];
Target *current = [self.fetchedResultController objectAtIndexPath:theIndexPath];
self.currentTarget = current.body;
Upvotes: 1
Reputation: 124
What you can do is to use one Action for both an check the sender and the segue to know where is it coming from:
- (void)unwindFromModal:(UIStoryboardSegue *)unwindSegue {
if unwindSegue.identifier == @"createModal" {
NSLog(@"Got home from create page");
}
if unwindSegue.identifier == @"stackModal" {
NSLog(@"Got home from stack page");
}
}
- (IBAction)presentModal:(UIButton*)sender {
if sender.tag == 0 {
[self performSegueWithIdentifier:@"createModal" sender:self];
}
if sender.tag == 1 {
[self performSegueWithIdentifier:@"stackModal" sender:self];
}
}
You can use String Constants in the tags to make more readable and a switch if you prefer too
Upvotes: 1