custom container view controller state preservation - ios

I need to create custom container viewcontroller with label and 2 states A and B(each state presented by child viewcontroller and swap each other). Both children viewcontrollers instantiates when parent container creates and currentViewController is viewControllerA by default. So i have something like this in container viewcontroller:
#property(weak, nonatomic) id currentViewController;
#property(strong, nonatomic) id viewControllerA;
#property(strong, nonatomic) id viewControllerB;
#property(strong, nonatomic) UILabel* label;
...
// Question here
- (void)viewDidLoad
{
[super viewDidLoad];
self.viewControllerA = [ViewControllerA new];
self.viewControllerB = [ViewControllerB new];
self.currentViewController = self.viewControllerA;
[self populateCurrentViewController];
}
- (void)populateCurrentViewController
{
[self addChildViewController:self.currentViewController];
[self.view addSubview:self.currentViewController.view];
[self.currentViewController didMoveToParentViewController:self];
}
- (void)goToState: (UIViewController*)stateController
{
if(self.currentViewController != stateController)
{
[self.currentViewController willMoveToParentViewController:nil];
[self addChildViewController:state];
[self transitionFromViewController: self.currentViewController toViewController: stateController
duration: 0.25 options:UIViewAnimationOptionTransitionFlipFromLeft
animations:nil
completion:^(BOOL finished) {
[self.currentViewController removeFromParentViewController];
[stateController didMoveToParentViewController:self];
}];
}
}
- (void)goToStateA
{
[self goToState: self.viewControllerA];
self.label.text = #"A";
}
- (void)goToStateB
{
[self goToState: self.viewControllerB];
self.label.text = #"B";
}
I need to preserve state of container viewcontroller. The question is where i must instantiate and add children viewcontrollers on first creating my container viewcontroller and on restoration process? Where i must restore children viewcontrollers?
I found many candidates:
-(void)initWithCoder
-(void)awakeFromNib
-(void)viewDidLoad
-(void)viewWillAppear
and
-(void)decodeRestorableStateWithCoder
for restoration process.
A couple of thoughts on this subject. If use initWithCoder and viewDidLoad for creating children viewcontrollers and adding current viewcontroller as a child to container viewcontroller, then after restoration process i need to change current viewcontroller and swap it if necessary, what is wrong i think. If use viewWillAppear for adding current viewcontroller, then it is normal for restoration process, but this method may be invoked several times.
I think there are two ways to restore restore children viewcontrollers:
Instantiate in viewDidLoad and restore in -(void)decodeRestorableStateWithCoder:(NSCoder *)coder like [self.viewControllerA decodeRestorableStateWithCoder:coder] (i saw it here - https://stackoverflow.com/a/16647606/2492707)
Full restore in -(void)decodeRestorableStateWithCoder:(NSCoder *)coder like self.viewControllerA = [coder decodeObjectForKey: #"AKey"];
Which way is better?
Understanding of the recovery process of the UINavigationController (where visibleViewController is populated) can help me too.
Thanks in advance.

Related

Container ViewController with 3 children must have a common parent view controller error

I have a parent VC with 3 children - Settings, Location and Diary.
Settings, Location & Diary are all accessed via IBActions based on their respective buttons.
When I go between Location and Diary, everything is fine. When I click Settings, it works, but when I click back to Location or Diary I get the dreaded must have a common parent view controller error. Funny thing is the exception says that the Diary and Location VC's don't share the same parent even though in that case I am clicking between Settings and either Diary or Location.
.m properties
#interface LPMainContentViewController ()
#property (weak, nonatomic) IBOutlet UIView *containerView;
#property (weak, nonatomic) IBOutlet UIButton *locationButton;
#property (weak, nonatomic) IBOutlet UIButton *diaryButton;
#property (weak, nonatomic) IBOutlet UIButton *settingsButton;
#property (strong, nonatomic) UIViewController *locationViewController;
#property (strong, nonatomic) UIViewController *diaryViewController;
#property (strong, nonatomic) UIViewController *settingsController;
- (IBAction)goToLocationView:(UIButton *)sender;
- (IBAction)goToDiaryView:(UIButton *)sender;
- (IBAction)goToSettings:(UIButton *)sender;
#end
I use if statements in the code to determine which was the previous child VC for the transition method and to set enabled on the button to YES or NO.
- (IBAction)goToLocationView:(UIButton *)sender {
if ([_diaryViewController isViewLoaded]){
[self cycleFromViewController:_diaryViewController toViewController:_locationViewController];
_locationButton.enabled = NO;
_diaryButton.enabled = YES;
}
else if ([_settingsController isViewLoaded]){
[self cycleFromViewController:_settingsController toViewController:_locationViewController];
_locationButton.enabled = NO;
_settingsButton.enabled = YES;
}
}
- (IBAction)goToDiaryView:(UIButton *)sender {
if ([_locationViewController isViewLoaded]){
[self cycleFromViewController:_locationViewController toViewController:_diaryViewController];
_locationButton.enabled = YES;
_diaryButton.enabled = NO;
}
else if ([_settingsController isViewLoaded]){
[self cycleFromViewController:_settingsController toViewController:_diaryViewController];
_diaryButton.enabled = NO;
_settingsButton.enabled = YES;
}
}
- (IBAction)goToSettings:(UIButton *)sender {
if ([_locationViewController isViewLoaded]){
[self cycleFromViewController:_locationViewController toViewController:_settingsController];
_locationButton.enabled = YES;
_settingsButton.enabled = NO;
}
else if ([_diaryViewController isViewLoaded]){
[self cycleFromViewController:_diaryViewController toViewController:_settingsController];
_diaryButton.enabled = YES;
_settingsButton.enabled = NO;
}
}
and here's the transition method pulled straight from the Apple docs
- (void) cycleFromViewController: (UIViewController*) oldVC
toViewController: (UIViewController*) newVC
{
[oldVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];
newVC.view.frame = _containerView.bounds;
CGRect endFrame = _containerView.bounds;
[self transitionFromViewController: oldVC toViewController: newVC
duration: 0.0 options:0
animations:^{
newVC.view.frame = oldVC.view.frame;
oldVC.view.frame = endFrame;
}
completion:^(BOOL finished) {
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
}];
}
isViewLoaded is the wrong thing to check. The view will get loaded once and then stick around, so you're probably choosing the wrong "current" view controller after a few cycles. Have another property to hold the current view controller instead, it will simplify your code and also actually work:
#property (weak, nonatomic) UIViewController *currentViewController;
You'll need to set this to whatever the initial child view controller is when you first set up the container view controller.
Then, change the goToXX methods to be like the following:
- (IBAction)goToDiaryView:(UIButton *)sender {
self.settingsButton.enabled = YES;
self.locationButton.enabled = YES;
self.diaryButton.enabled = NO;
[self cycleFromViewController:self.currentViewController toViewController:self.diaryViewController];
}
Last of all, in your cycle method, make sure you keep this property updated in the completion block of the transition:
completion:^(BOOL finished) {
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
self.currentViewController = newVC;
}];
Note that it is safer to use your properties rather than the _instanceVariables. That's what they're there for.

Obj-C Delegate inside Delegate

I have subclassed a view that I am using as header view it has some buttons delegate inside it and it works perfect .
However I am presenting a modalViewController above my viewController .
(in my modalViewController I have implemented the same header , and it does get the delegates from the header) but this view it self has to delegate to the previous viewController if the back button of the header is pressed.
I have made the same functions but my viewController never gets it's delegate... :(
I am quite new to Obj-C and I don't know maybe I am doing something illegal here.
here is the code of modalViewController trying to delegate to the previous viewController
#pragma mark - header delegate
- (void)header:(header *)header backbuttonPressed:(UIButton *)sender
{
if(header == logo)
{
NSLog(#"gotBackButtonDelegate");
//delete the items array
//_itemSourceArray = nil;
[delegate allEventsDrillPage:self backbuttonPressed:sender];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
This delegate does triggers from the header view.
however in my previous viewController:
- (void)allEventsDrillPage:(allEventsDrillPage *)allEventsDrillPage backbuttonPressed:(UIButton *)sender //doesn't work :(
{
NSLog(#"got back delegate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
// [self dismissViewControllerAnimated:YES completion:nil];
_drillPage = nil;
}
never get called
I calling the modalViewController like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[_allEventsTableView deselectRowAtIndexPath:indexPath animated:YES];
[self->_allEventsTableView setNeedsDisplay];
[self->_allEventsTableView reloadData];
_drillPage = [[allEventsDrillPage alloc]initWithDictionary:((NSDictionary*) [_tableDataSource objectAtIndex:indexPath.row])];
_drillPage.delegate = self;
[self presentViewController:_drillPage animated:YES completion:nil];
}
in its .h file I did
#interface allEvents : UIViewController <headerDelegate , UITableViewDataSource , UITableViewDelegate ,allEventsDrillPageDelegate>
I don't get what am I missing here :-/ can some1 take a look please ?
if needed more info I will added just ask for it.
EDIT:
protocol of delegate inside the modalViewController
#class allEventsDrillPage;
#protocol allEventsDrillPageDelegate //define delegate protocol
- (void)allEventsDrillPage:(allEventsDrillPage*)allEventsDrillPage backbuttonPressed:(UIButton*)sender;
#end
#interface allEventsDrillPage : UIViewController
{
id<allEventsDrillPageDelegate> __weak delegate;
....
}
#property (weak, nonatomic) id <allEventsDrillPageDelegate> delegate; //define
Let's try:
- (void)header:(header *)header backbuttonPressed:(UIButton *)sender
{
if(header == logo)
{
NSLog(#"gotBackButtonDelegate");
// delete the items array
//_itemSourceArray = nil;
// my comment: you should replace "delegate" to "_delegate"
// and it works. I tested. It's OK. Wow.
[_delegate allEventsDrillPage:self backbuttonPressed:sender];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
nmh's answer is correct. I wrote up an answer earlier but his came in faster, so I thought I would take out the other parts and just add the explanations here.
You have this:
#interface allEventsDrillPage : UIViewController
{
id<allEventsDrillPageDelegate> __weak delegate;
....
}
#property (weak, nonatomic) id <allEventsDrillPageDelegate> delegate;
With this id<allEventsDrillPageDelegate> __weak delegate; you declare an ivar.
With this #property (weak, nonatomic) id <allEventsDrillPageDelegate> delegate; you are declaring a property.
Since Xcode 4.4 you get auto-synthesization.
And so this line:
_drillPage.delegate = self;
You are setting the one via the property.
And not this:
id<allEventsDrillPageDelegate> __weak delegate;
So what you have here:
[delegate allEventsDrillPage:self backbuttonPressed:sender];
You are using the ivar above, not the one via the property.
And delegate is thus nil.
And so, if you try to send a message to the delegate using nmh's solution or:
[self.delegate allEventsDrillPage:self backbuttonPressed:sender];
It should work as expected.
Addendum to dismissing modal view controller:
Instead of dismissing it from the current view controller, dismiss it from the one who presented it, so:
- (void)header:(header *)header backbuttonPressed:(UIButton *)sender
{
if(header == logo)
{
NSLog(#"gotBackButtonDelegate");
[_delegate allEventsDrillPage:self backbuttonPressed:sender];
//or self.delegate
}
}
And in:
- (void)allEventsDrillPage:(allEventsDrillPage *)allEventsDrillPage backbuttonPressed:(UIButton *)sender
{
NSLog(#"got back delegate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
[self dismissViewControllerAnimated:YES completion:nil];
_drillPage = nil;
}
Or even better:
- (void)allEventsDrillPage:(allEventsDrillPage *)allEventsDrillPage backbuttonPressed:(UIButton *)sender
{
NSLog(#"got back delegate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
[self dismissViewControllerAnimated:YES completion:^{
_drillPage = nil;
}];
}
Hope this helps.

Memory: object not release after view controller dismiss, with ARC

I have 2 view controller,
FirstViewController - > SecondViewController via
[self presentViewController:SVC animated:YES completion:nil];
on SecondViewContrller when I do
[self dismissViewControllerAnimated:YES completion:nil];
My question is, Why is the objects not release on secondViewController after I dismiss this viewcontroller. As you can see on the graph It didn't go down after dismiss. BTW whats the best way to release/dismiss a ViewController?
[EDIT]
I NSLog a message on dealloc method on every VC, When I start from FVC->SVC->[dismiss SVC]. this is my logs
This can be pretty rough stuff. I had similar issues before. Search your code and see if you have strong or wrong references to objects.
One of my top mistakes (and what I have seen on the internet hundreds of times) are delegate properties. I wrote them like #property (nonatomic, retain) id<protocol>delegate; for quite a long time as I realized that if I do so, the delegated object does not get released. One have to use assign in this case.
Hope that help you...
I have made some investigation with this behavior.
FirstViewController.m
#import "FirstViewController.h"
#import "SecondViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)dealloc {
NSLog(#"First Dealloc");
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
UIButton *pressMe=[UIButton buttonWithType:UIButtonTypeCustom];
pressMe.frame = CGRectMake(0, 0, 100, 40);
pressMe.center = self.view.center;
[pressMe setTitle:#"PressMe" forState:UIControlStateNormal];
[pressMe addTarget:self action:#selector(pressMeAction:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:pressMe];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) pressMeAction:(id) sender
{
SecondViewController *svc = [[SecondViewController alloc] init];
[self presentViewController:svc animated:YES completion:nil];
NSLog(#"Present Second");
}
#end
SecondViewController.m
is pretty the same except
-(void) pressDissmissButtonAction:(id) sender
{
[self dismissViewControllerAnimated:YES completion:nil];
NSLog(#"Dismiss Second");
}
and this is Allocation dynamics
As you can see after pressMeButtonAction invoked secondViewController allocated and after pressDissmissButtonAction invoked secondViewController is successfully deallocated.
BUT: Most of the time it deallocated immediately, but if you present and dismiss it very quickly (twice a second or so), dellocation not fired immediately, but after a while.
I Assume that this is by design implementation of ARC deallocation procedure. Not sure.
try this ...
[self presentViewController:SVC animated:YES completion:nil];
SVC = nil;
After spending many hours on this, I finally found a missing piece of the puzzle: Not only do you have to set any strong references to the ViewController to nil, you also have to invalidate any timers and be aware of block retain cycles.
Any time you use self in a block you create a retain cycle! Instead you should declare a variable like so
__unsafe_unretained ViewController *weakSelf = self;
and use it instead of self in the block.
Check all IBOutlets in your application. There might be "strong" property assigned to them. Make them "weak". For example, an IBOulet should be like this:
#property (weak, nonatomic) IBOutlet UILabel *myLabel;
Check all delegates (if any) in your application. Every delegate should be like this:
#property (nonatomic, assign) id <yourProtocol> delegate;
Note that, it takes some amount of time for ARC to recover memory.
Timers were the issue in my case. Added timer invalidate to viewWillDisappear and the view controllers were then released.

ARC and autoreleased object

I need a ViewController to be called modally to show some UIButton and other UIView on top of the current window. I want the background to be partially transparent and showing the current window below it - something similar to a UIActionSheet but with a custom design. I coded my VC to do the following: 1) during init the VC sets self.view.frame equals to [[UIApplication sharedApplication]keyWindow].frame 2) when show() is called the VC adds self.view on top of [[UIApplication sharedApplication]keyWindow] subViews 3) when an internal button calls the private method release() the VC remove self.view from its superview. Example with a single release button as follows:
#implementation ModalController
- (id)init
{
self = [super init];
if (self){
//set my view frame equal to the keyWindow frame
self.view.frame = [[UIApplication sharedApplication]keyWindow].frame;
self.view.backgroundColor = [UIColor colorWithWhite:0.3f alpha:0.5f];
//create a button to release the current VC with the size of the entire view
UIButton *releaseMyselfButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[releaseMyselfButton setTitle:#"Release" forState:UIControlStateNormal];
releaseMyselfButton.frame = CGRectMake(0, 0, 90, 20);
[releaseMyselfButton addTarget:self action:#selector(releaseMyself) forControlEvents:UIControlEventTouchDown];
//add the button to the view
[self.view addSubview:releaseMyself];
}
return self;
}
- (void) show
{
//add self.view to the keyWindow to make sure that it will appear on top of everything else
[[[UIApplication sharedApplication]keyWindow] addSubview:self.view];
}
- (void)releaseMyself
{
[self.view removeFromSuperview];
}
#end
If I create an instance of ModalController from another VC and I call show() everything goes as expected:
#interface CurrentVC ()
#property (strong, nonatomic) ModalController *myModalController;
#end
#implementation CurrentVC
- (void)viewDidLoad
{
[super viewDidLoad];
self.myModalController = [[ModalController alloc]init];
[self.myModalController show];
}
#end
To make it work I need to retain the ModalController in a property until release () is called. However I would like to have the same freedom I have with UIActionSheet and simply keep an instance of it in a local variable:
#implementation CurrentVC
- (void)viewDidLoad
{
[super viewDidLoad];
ModalController *myModalController = [[ModalController alloc]init];
[myModalController show];
}
#end
If I do this with the current code ARC will release myModalController straight after show() is called and the release button will be pointing to nil. How can I make this work without storing the object in a property? I've identified a work around but I'm not sure it's a good design option:
#interface ModalController ()
#property (strong, nonatomic) ModalController *myselfToAutorelease;
#implementation ModalController
- (id)init
{
self = [super init];
if (self){
... ... ...
self.myselfToAutorelease = self;
}
return self;
}
- (void) show
{
... ... ...
}
- (void)releaseMyself
{
[self.view removeFromSuperview];
self.myselfToAutorelease = nil;
}
What I've done is making ModalController "self sufficient" - it stores a pointer to itself during init and set it to nil when it's ready to release himself. It works but I have the feeling that this is against the ARC design principles! Is this approach correct? If not, how can I handle this differently?
Thanks in advance for your help.
Doesn't work like that.
You don't keep a reference to self.
In the main view controller you just create your object. If you need it to be around longer keep it in a property in the main view controller , when done, set the property to nil in the main view controller.

Add objects to an NSMutableArray in my base view controller from a second view controller, ios

I have been searching all morning how to do this. I have 2 View Controllers. From the root View Controller (ViewControllerA - which is a table view controller) you can go push to the second view controller (ViewControllerB).
In the ViewControllerB, there are two fields: contacts & textBody. When the user is done they can click on "Add". This will then go back to ViewControllerA. What I am trying to do now, is for every time that process occurs, all the information from ViewControllerB the user just added goes into a cell in ViewControllerA. The user can then add as many cells as they like.
What I can't do however, is get the information across the view controllers. I have been looking all morning at using the app delegate, singletons??, protocols, sharing properties, etc! But I am still stuck.
What I want to do, but can't, is for every time the user clicks "Add" on ViewControllerB, contacts & texts are put into an array. This array is then put into another array which holds all the smaller arrays which the user has created? If you have an ideas, or links to similar/sample code or tutorials, that would be much appreciated!
Try this using the delegate method as follows
Download Sample Project with XIBs
Download Sample Project With Storyboard
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController {
NSMutableArray *dataArray;
}
- (void)passData:(NSMutableArray *)array;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "ChildViewController.h"
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
dataArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSMutableArray *)array {
[dataArray addObject:array];
NSLog(#"Data Passed = %#",dataArray);
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController {
NSMutableArray *tempArray;
}
#property (strong, nonatomic) IBOutlet UITextField *txtContact;
#property (strong, nonatomic) IBOutlet UITextField *txtTextBody;
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
tempArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTxtContact:nil];
[self setTxtTextBody:nil];
[super viewDidUnload];
}
#end
With Storyboard
If you are using storyboard then create a ParentViewController segue ChildViewController and give it a identifier in my sample it showChildView
Then use the following code to set the delegate
// Calling the segue to go to the child view and setting up the delegate.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showChildView"]) {
ChildViewController *childVC = segue.destinationViewController;
childVC.delegate = self;
}
}
Then to dismiss back to the ParentViewController use the following code (from my sample)
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
I would recommend using a singleton instance of your NSMutableDictionary as they have bailed me out of your exact situation multiple times (including custom frameworks and UITabBarControllers). Here is an example I'm currently using to implement a singleton. This methodology is also ARC-safe as well
mySingleton.h
#import <Foundation/Foundation.h>
#interface mySingleton : NSObject {
}
+ (NSMutableDictionary *) myMutableDict;
#end
mySingleton.m
#import "mySingleton.h"
#implementation mySingleton
+ (NSMutableDictionary *)myMutableDict
{
static NSMutableDictionary *singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[NSMutableDictionary alloc]init];
});
return singletonInstance;
}
#end
As long as you include mySingleton.h in all of your view controllers you can access the data via [mySingleton myMutableDict]. For example: [[mySingleton myMutableDict] setObject:myObject forKey:myKey];
Good luck!
If the information is really "global" - it has only one instance across the whole app - then you should create a singleton as DB80Buckeye suggested.
If the information is something that truly belongs to ViewController1 and you want it to be modified in ViewController2 (ie ViewController2 is really part of ViewController1, it just happens to be on another screen), then you should pass that as part of the constructor of ViewController2.
-(void)view_controller_1_that_push_view_controller_2_onto_the_stack {
ViewController2* vc2 = [[ViewController2 alloc] initWithInformation:your_information];
[self.navigationController pushViewController:vc2 animated:YES];
}
#interface ViewController2
-(id)initWithInformation:(YourInformationClass*)info;
#end
Another way is to use notifications.
There are two ways to go here. The standard pattern for doing this is delegation. You don't need a singleton. ViewControllerA manages and lists your data. ViewControllerB doesn't need to know anything about all of that data so there's no reason to expose it via a singleton, etc.
Create a delegate protocol in ViewControllerB's header file. Something like this:
#protocol ViewControllerBDelegate
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody;
#end
Now, specify that ViewControllerA will implement the delegate protocol in its header:
#interface ViewControllerA : UIViewController <ViewControllerBDelegate>
Don't forget to import ViewControllerB.h at the top of ViewControllerA's header.
In ViewControllerA's implementation, implement the delegate method you specified in the protocol:
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody {
[self.someArray addObject:[[SomeObject alloc] initWithContact:contact body:textBody]];
[self.tableView reloadData];
}
That's obviously just an example -- not sure how you're managing your data structure and it's probably better to insert the cell someplace that makes sense.
Declare a delegate reference in ViewControllerB's header:
#property (weak, nonatomic) id<ViewControllerBDelegate> delegate;
When you present ViewControllerB, set ViewControllerA as the delegate.
ViewControllerB *b = [[ViewControllerB alloc] init...];
b.delegate = self;
In the selector triggered by the add button in ViewControllerB, call back on the delegate before popping the view controller off the navigation stack:
[self.delegate addContact:contact withBody:text];
where contact and text are the values the user entered.
One could also use a block instead of a delegate but the principle is the same -- have the second view controller only be responsible for taking input, in your case, and pass it back to the view controller managing the data.
Alternatively for delegate suggest using the following:
ViewControllerA.h:
#property (nonatomic, strong) ViewControllerB* viewControllerB;
In ViewControllerA.m
if (!self.viewControllerB)
{
self.viewControllerB = [[ViewControllerB alloc] initWithNibName: #"ViewControllerBr" bundle: nil];
}
[self.navigationController pushViewController: self.viewControllerB
animated: YES];
...
- (void) viewWillAppear: (BOOL) animated
if (self.viewControllerB)
{
NSString* contact = self.viewControllerB.contact;
NSLog(#"%#", contact);
}
...

Resources