Objective-C: Pass data between two not connected View Controllers - objective-c

I have a ViewController A with a UILabel and a ViewController B with a button, so I want to update the label once I press the button. I could use delegates for this, but my ViewControllers are not connected and I can't use something like setDelegate, I don't create any instance of one in another. So basically they are created somewhere else. Is there any way to do that?

Variant 1: if ControllerA and ControllerB life-time differs
Use NSUserDefaults. On ControllerB button click store data into NSUserDefaults, in ControllerA read data from NSUserDefaults and show in label (NSUserDefaults is also observable, so can track changes in run-time)
Variant 2: if ControllerA and ControllerB both currently in run-time
Use NSNotificationCenter. On ControllerB button click post a NSNotification with data in userInfo, and ControllerA in notification handler extracts data from userInfo and assign to label.

Use Coordinator pattern.
Class that will create/get the 2 instances of both VCs,
and with delegates will move the data between them.
The advantage of Coordinator pattern is that your VCs can be re-used on others places (same or other project), and also the code is cleaner.

To expand on Apsperi's answer:
Notification method of changing the view controller:
In the view controller with the button on button press call:
[[NSNotificationCenter defaultCenter] postNotificationName:#"refresh"
object:self userInfo:nil];
Then in the viewdidload or viewdidappear method of the label view controller place the "receiver" for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mymethotochangetext) name:#"refresh" object:nil];
The NSUserDefault method:
In the view controller with the button save a NSUserDefualt:
[[NSUserDefaults standardUserDefaults] setObject:somestring
forKey:#"myuniquekeyname"];
[[NSUserDefaults standardUserDefaults] synchronize];
Then in the view controller with the label you can get the saved string when the view loads and place it in the text field:
self.label.text=[[NSUserDefaults standardUserDefaults]
stringForKey:#"myuniquekeyname"];

Related

Load Title ViewController When applicationDidBecomeActive:

I've created an app that has two viewcontrollers. The app opens to a title screen (general UIViewController titled 'Title') with a segue connection to the second view that is a custom class (OSViewController titled 'MapView'). As it is, the app suspends when entered into the background state so it opens right where you left off which is typically in MapView.
I want to know what I need to do to have the app start at the title screen when it becomes active. Preferably, I'd like it to open to the title screen if it is inactive for more than 1 minute. From what I've been reading, it seems like I would make a call in applicationDidBecomeActive: method in my AppDelegate to code this in. Please provide me the code to put in the applicationDidBecomeActive: method (if that's the right place to put it) that will reopen my app to the title screen when transitioning from the inactive state to the active state. My app is almost finished but I'd like to fix this issue and I don't have a lot of experience dealing with app states. Thanks in advance for your time.
If you need more information just ask.
You can also register a class as an observer of the "didBecomeActive" notification. You should place this in the viewDidLoad or the init method of your class.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
In this case, willBecomeActive: is a method that you have defined in your class that get's called when the app becomes active again. That might look something like this:
- (void)willBecomeActive:(NSNotification *)notification {
if (self.navigationController.topViewController == self) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
You'll also need to add this in your viewDidUnload method
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
EDIT:
Thanks #AMayes for the advice. I don't believe key/value observing is necessary in this instance.

NSTableView reload data from different controller

I am having problems updating the content of two tables, which are placed in two different tabs of a NSTabView and each controlled by a different controller (Controller1 and Controller3), from a third controller (Controller2).
I tried three different approaches:
Approach #1:
I created in Controller1 (the controller of the table invitesTableView) the method refreshTable :
- (void)refreshTable {
invites = //fill my mutable array
NSLog(#"%#",invites);
[self.invitesTableView reloadData];
NSLog(#"invite's table view updated");
}
which I later call from Controller2 doing :
Controller1 *controller1 = [[Controller1 alloc] init];
[controller1 refreshTable];
NSLog prints the content of the array invites correctly, but the table is not updated. I should say that, at application launch, I call the very same method in Controller1 and the content of the array invites is correctly loaded in the table.
Approach #2: When in Controller2 I do:
Controller1 *controller1 = [[Controller1 alloc] init];
controller1.invites = //fill my mutable array
NSLog(#"%#",controller1.invites);
[controller1.invitesTableView reloadData];
But again the content of the table is not updated, even if the NSLog shows the correct content.
Approach #3: As Controller1 controls the content of a NSTabViewItem, I integrated in Controller1 the method:
- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)inviteTab
and it works just fine, because I call the method refreshTable as soon as open the tab controlled by Controller1. The problem is that I have different tables in different tabs but I can set only one controller as the delegate of the NSTabView, so I can not update the table controlled by Controller3 in a different table.
I don't think there is an easy solution to approach #3, apart from merging Controller1 and Controller3, but I think I am just missing something with respect to the first two approaches.
Hope I explained myself clearly. Can anyone help? Thanks in advance!
You can use notifications/observer to reload tableview.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadTable:)
name:#"reloadNotification"
object:nil];
- (void)reloadTable:(NSNotification *)notif {
[self.tableName reloadData];
}
In your first tab use this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadNotification"
object:nil];

return to the main menu from any UIViewcontroller

I'm wondering if there is any way to return from UIViewController like 3-4 step backward, I've a main screen which will navigate to other UIViewController via presentModalViewController, on the next view, it will have a UINavigationBar which will navigate to a 4-5 level deeps. i wanna to put a button that let the user go back to the home directly without returning for all the view he enter.
thx in advance.
Have your root level view controller register as an observer of a notification, such as "POP_TO_ROOT". When it receives this notification, call a method to dismiss your modal view controller (or whatever is first on the stack).
In your viewcontroller stack, any of the views 4 or 5 levels in can just post a notification "POP_TO_ROOT".
EDIT: add code
In your main "screen" before you call presentModalViewController, do this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handlePopToRoot)
name:#"POP_TO_ROOT"
object:nil];
and add this method:
- (void) handlePopToRoot {
[[NotificationCenter defaultCenter] removeObserver:self
name:#"POP_TO_ROOT"
object:nil];
[self.navigationController dismissModalViewControllerAnimated: YES];
}
Then, down deep in your viewcontroller hierarchy, when you want to pop all the way out,
you just need to post a notification:
[[NSNotificationCenter defaultCenter] postNotification:#"POP_TO_ROOT" object:nil];
If I understand your question correctly, you are presenting a navigation controller (with a root view controller attached to it) from your "main view controller" modally, and you want to be able to get back to your "main view controller".
Because you will always have pointer to your navigation controller, you should be able to call
dismissModalViewControllerAnimated: from any of your view controllers and it will take you right back to the main view controller.
[[self.navigationController parentViewController] dismissModalViewControllerAnimated:YES]
Save your root View Controller in some property and call:
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated

How do you send a message to a button from different views?

I have an app that has a main view that acts as a view controller. This main view has 3 buttons on it. I have 3 subviews that I swap in and out of this main view, controlled by the 3 buttons. Each of the subviews has a button on it. When this button is pressed I want it to disable the 3 buttons on the main view until the button is pressed again. Is there a way to send a message between the views to disable the buttons?
This sounds like a toggle to me. More like a setting. If you think about it, this should go in NSUserDefaults. And when you that particular view is coming on, probably in viewWillAppear: or viewDidAppear:, do this,
BOOL controlsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"ControlsEnabledKey"];
button1.enabled = controlsEnabled;
button2.enabled = controlsEnabled;
button3.enabled = controlsEnabled;
To save the value on that button press,
BOOL controlsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:#"ControlsEnabledKey"];
[[NSUserDefaults standardUserDefaults] setBool:!controlsEnabled forKey:#"ControlsEnabledKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
note Since the boolForKey: will return NO if the key is not found, I suggest you set the value to YES when the application starts if you want the controls to be enabled at launch.
Use NSNotifications to post a notification that the buttons were pressed.
[[NSNotificationCenter defaultCenter] postNotificationName:#"Button1Pressed" object:self userInfo:info];
And then add observers such that they listen to these notifications.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(buttonPress:) name:#"Button1Pressed" object:nil];
Now implement buttonPress
Read HERE for the NSNotification manual and learn to use it .
Set the main view/controller as the delegate of the inner views, define a protocol in which you define a method, say, toggleMainButtons, have the VC conform to that protocol and implement the message. On the buttons, addTarget:self.delegate action:#selector(toggleMainButtons) forControlEvents:UIControlEventTouchUpInside.

Reloading NSTableView on re-focus

In my application, I have a NSWindow that has a NSTableView object and a few buttons. When the user presses the "new" button, an "ItemAdd" NSWindowController is activated where the user types in attributes for the item to be added to the NSTableView.
My question is this: since NSTableView requires reloadDatato update its view, how do I call reloadData after the ItemAdd window closes and focus shifts back to the NSWindow with the NSTableView.
Thanks for the help!
You could put reload data in a notification handler:
Put this in an initialization method of an object that you want the notification to get called on:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didBecomeMainWindow) name:#"NSWindowDidBecomeKeyNotification" object:nil];
Then make a method something like this:
- (void) didBecomeMainWindow
{
[tableView reloadData];
}
You can subclass the NSWindow and override the following method:
- (void)becomeKeyWindow