I am developing an iPad application using storyboard. In my application I have connected one modal view controller from first view controller using segue modal presentation.The modal view is dismiss by click one button appear in the modal view.How can i pass one dictionary from modal view controller to first view controller without using segue.
You can do passing value using Notification.
// Send Data
NSDictionary *aDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
anObject, #"objectName",
anotherObject, #"objectId",
nil] autorelease];
[[NSNotificationCenter defaultCenter] postNotificationName:#"AnythingAtAll" object:nil userInfo:aDictionary];
You can retrieve the dictionary from the inbound notification that you observe. Add the observer in advance of posting the notification.
//this might be in your init method or a viewDidLoad method of your FirstViewController
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(anyAction:) name:#"AnythingAtAll" object:nil];
enter code here
// for get data
-(void)anyAction:(NSNotification *)anote
{
NSDictionary *dict = [anote userInfo];
AnyClass *objectIWantToTransfer = [dict objectForKey:#"objectName"];
}
note that you should remove your object as an observer in the dealloc method.
[[NSNotificationCenter defaultCenter] removeObserver:self]
Using delegate its possible.
Go through this
Passing Data between View Controllers
its has easy and perfect answers.
You can create a Segue not bounded to a button by ctrl+drag from the first controller to the second one (don't forget to give this segue and identifier).
Next In the IBAction of the button (set via Interface Builder or via addTarget:self action:forControlEvents: ) you can call the [self performSegueWithIdentifier:#"YourSegueIdentifier" sender:button];
You can pass the data to the second controller, as usual, in - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion {
SecondViewController *secVC = (SecondViewController*)toViewController;
secVC.property = self.property
}
Using Protocols is the best process for sending information to previous controller or any other controller
In your viewcontrooler where you presented write this in .h
#class yourclassName;
typedef void (^ yourclassName Callback) (NSDictionary*);
#protocol yourclassName Delegate
#required
- (void) yourclassNamedidReceiveData:(NSDictionary *)dataDict;
#end
in .m file
#synthesize delegate=_delegate;
#synthesize callbackBlock=_callbackBlock;
In some button action where you need to pass information back over there write this
[self yourclassNamedidReceiveData:yourDictHere]; // you can use any as per your requirement
Note: yourclassNamedidReceiveData method is declared in one class and now it is implemented in another class from where you will call this class
In your previous Controller(where you tried to present a class (suppose classA and classB ) first class is classA and presented class is classB.) in ClassA
- (void) yourclassNamedidReceiveData:(NSDictionary *)dataDict
{
}
Note* Do not forget to set delegate to classB EX:classBObject.delegate=self;
Hope this will help
Related
I have two view controllers one of them (ViewController) has a table called tableView.
I would like to refresh this table from my other view controller (pageView).
I have tried this in the pageView:
ViewController*refresh;
[refresh.tableView reloadData];
But this is not working.
The connecting segue between the two view controllers is a push segue
What should I do? Should I do it through a storyboard segue?
Option 1
#Class2
#property (nonatomic) BOOL shouldRefresh; // in .h file
- (void)viewWillAppear:(BOOL)animated // in .m file
{
[super viewWillAppear:animated];
if (_shouldRefresh) [self.tableView reloadData];
}
#Class1
// Add this in any method where you want it to refresh and push to view
ClassTwoController *viewController = [[ClassTwoController alloc] init];
[viewController setShouldRefresh:YES];
[self.navigationController pushViewController:viewController animated:YES];
*UPDATE:
Option 2
#Class 2
// Add this line in viewDidLoad in same class you want the tableView to refresh
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshTableWithNotification:) name:#"RefreshTable" object:nil];
// Add this method just beneath viewDidLoad:
- (void)refreshTableWithNotification:(NSNotification *)notification
{
[self.tableView reloadData];
}
#Class1
// Call this when ever you want to refresh the tableView in Class2
[[NSNotificationCenter defaultCenter] postNotificationName:#"RefreshTable" object:nil userInfo:nil];
/* Add this line in viewDidLoad method in ViewController (by using this line you are adding notification observer to ViewController class) */
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadTableview:) name:#"ReloadTableview" object:nil];
/* Add this method in ViewController class(this method call when you post notification in pageView) */
- (void)reloadTableview:(NSNotification *)notif
{
[tableView reloadData];
}
/* add this line when/where you want to refresh your (this line post notification which added in ViewController) */
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadTableview" object:nil];
If second view controller is already loaded then you can use NSNotification in firstviewcontroller. This notification will invoke a method in second view controller. In this method write a code to reload a tableview.
If I understand correctly, you want to reload a table view that exists in the previous view controller of your navigation stack. If so, you can access it through the navigation controller's viewControllers property.
NSUInteger indexOfTableController = self.navigationController.viewControllers.count - 2;
ViewController *tableController = (ViewController*)[self.navigationController.viewControllers objectAtIndex:indexOfTableController];
[tableController.tableView reloadData];
Most often this happens because you're not initializing the reference to your second controller properly and it stays nil. Has to be something like this.
#implementation
{
ViewController *refresh;
}
- (void) openNewController
{
refresh = [[ViewController alloc] init];
[self.navigationController pushViewController:refresh animated:true];
}
- (void) refreshNewController
{
[refresh.tableView reloadData];
}
#end
Okay for sake of example lets call the previous ViewController VC1 and the current ViewController VC2.
So you do a push segue from VC1 to VC2. What you could do is: pass a reference of the TableView to VC2 during the segue. So you would create a public UITableView tblView variable in VC2 and then in VC1 implement the prepareForSegue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"Segue_Identifier_From_StoryBoard"])
{
// get your destination view controller
VC2 *vc2 = [segue destinationViewController];
vc2.tblView = TableView;
}
}
Then in VC2, you could just call [tblView reloadData];
Just make sure you nil the tblView reference once you are done with VC2.
In ViewController1 :
> Create property for UITableview with nonatomic and retain
> Now Create table Object.
In ViewController2.m:
ViewController1 *objV1 = [ViewController1 alloc]initWithNibName....];
[objV1.yourTableView reloadData];
You can check the adress of viewcontroller.tableview and refresh.tableView and i think they would be not the same;
you can make singleton for viewcontroller and then invoke for example
viewcontroller *vc = [viewcontroller sharedInstance];
[[vc tableview] reloadData];
In your pageView create a method
-(void) reloadVCController{
ViewController*vc=[[ViewController alloc]init]];
[vc.tableview reloadData;
}
And use it where you want to call it
[self reloadVCController];
ViewController*refresh;
[refresh.tableView reloadData];
By seeing the above code you are not assigning your ViewController instance to refresh. When executing reloadData() your refresh will be nil so obviously it won't react to any action.
ViewController*refresh = originalInstance; //assign your original instance here
[refresh.tableView reloadData];
this would work and the main point to be noted here is your refresh viewcontroller should be the top most instance in stack because any UI operation should happen in main thread.
Use NSnotification center add observer in that view controller and write method for reload table.
[self.tableView reloadData];
And from current view post notify that observer.
In case you have tried all the answers, make sure that before reloading the tableView, the dataSource is actually updated with new/additional/ content.
Reloaddata needs to execute within the parentviewcontroller because it invokes the tableview delegate methods to reload the table such as CellForRowAtIndexpath, and many more.
This means you need to define a public method in the parentviewcontroller that will contain the reloaddata command and call it from the child view controller.
public Method:
Parentviewcontroller.h
#interface
-(void) reloadTable:(id) sender;
parentviewcontroller.m
#implementation
- (void) reloadTable:(id) sender {
[self.tableview reloaddata];
return;
}
childviewcontroller.h
#import parentviewcontroller.h
#class parentviewController
#interface childviewcontroller :UIViewController (or UITableViewController)
#property (nonatomic, strong) parentViewController *parent;
childviewController.m
#implementation
#synthesize parent;
/* Note the parent instance is passed from the parent to the child view controller,
it is not allocated and initialised in the child controller - many ways to do this
depending on coding style */
/* after updating the data */
[parent reloadTable];
I think the problem is that you refresh controller view is not loaded. So, reloadTable message simply goes to nil. You can force a View Controller to load its view by calling view getter:
[viewController view];
So check if your tableView is nil and if it is, initialise it with described method.
As a make-up answer to emotality's:
If you want to skip the start-up animation, just simply set a bool property in your ClassTwoController to check if it's first time loading this view
ClassTwoController.h:
#property (nonatomic, assign) BOOL notFirstTimeRun;
ClassTwoController.m:
-(void)viewWillAppear:(BOOL)animated // in .m file
{
if(self.notFirstTimeRun) {
[self.tableView reloadData];
} else {
[self playStartUpAnime];
}
}
-(void)playStartUpAnimation // in .m file
{
//Place your codes for animation here
}
in your ClassOneController.m initialise it with the check bool:
ClassTwoController *viewController = [[ClassTwoController alloc] init];
viewController.notFirstTimeRun = YES;
If you have parent-child relationship you can add weak property to child viewcontroller like this
#property (nonatomic, weak) ParentViewController *parentController;
Then when you need to reload table you just call [parentController.tableView reloadData]
Other way would be using NSNotificationCenter. In controller you want to reload table you should subscribe to notification([[NSNotificationCenter defaultCenter] addObserver...]) and implement selector you supply to this method. In another controller you should post notification with that name.
Also you can just refresh on viewWillAppear if it suits your needs.
I have a UINavigationGroup with a root view controller called MainViewController. Inside this MainViewController I'm calling another UINavigationController as a modal as following:
- (IBAction)didTapButton:(id)sender {
UINavigationController * someViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"someNavigationController"];
[self.navigationController presentViewController:someViewController animated:YES completion:nil];
}
Inside this someNavigationController, the user is going through some process so the nav controller is being pushed with some UIViewControllers. After the user completes the process, in the last UIViewController called finalStepViewController, I'm closing the modal as follow:
[self dismissViewControllerAnimated:YES completion:nil];
The modal is indeed dismissed and the user is back to the initial MainViewController.
However, I'd like to push another UIViewController to MainViewController's NavigationController (for example: a view saying that the user completed the process successfully). Preferably before the modal is dismissed.
I have tried the following things:
1. Using presentingViewController
UIViewController * successViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"successViewController"];
[self.presentingViewController.navigationController successViewController animated:YES];
Result: no error, but nothing happening either.
2. Delegate/protocol
Imported finalStepViewController.h inside MainViewController.h and appended <finalStepViewControllerDelegate>
Inside MainViewController.m added a method called parentMethodThatChildCanCall to be called from finalStepViewController.m
Added the following to finalStepViewController.h:
#protocol finalStepViewControllerDelegate <NSObject>
-(void)parentMethodThatChildCanCall;
#end
#property (assign) id <finalStepViewControllerDelegate> delegate;
and #synthesize delegate; in the model
Set the delegate property to someViewController in the above mentioned didTapButton IBAction to self. This showed a notice error saying: Assigning to id<UINavigationControllerDelegate>' from incompatible type UIViewController *const __strong'
Finally called [self.delegate parentMethodThatChildCanCall] just before closing the modal.
Result: except for the notice error, no fail but nothing happens as parentMethodThatChildCanCall is not called.
Any idea what I'm doing wrong/what I should be doing? It's my second week doing Objective-C, and most of the time I don't know what I'm doing so any help/code would be appreciated!
Thanks.
You can achieve this a lot easier using NSNotificationCenter.
In your MainViewController's -viewDidLoad add the following code
typeof(self) __weak wself = self;
[[NSNotificationCenter defaultCenter] addObserverForName:#"successfullActionName"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
SuccessViewController *viewController; // instantiate it properly
[wself.navigationController pushViewController:viewController animated:NO];
}];
Remove your controller from NSNotificationCenter upon dealloc
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
In FinalStepViewController on action that dismisses the view controller before dismiss post the notification
- (IBAction)buttonTapped:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:#"successfullActionName" object:nil];
[self dismissViewControllerAnimated:YES completion:nil];
}
This example is very crude and is not ideal, you should use constants for your notification names and in some cases store the observers returned by NSNotificationCenter to remove specific ones.
-- EDIT
I'd like to also mention that the method addObserverForName:object:queue:usingBlock: actually returns the observer as an id type object. You need to store a reference to it as an iVar in your class and remove it from the NSNotificationCenter when dealloc method is called otherwise that observer will never get deallocated.
Hello I do not have very much programming experience and I'm trying to call a method when the user leaves the app. I know that I am going to use the app delegate methods applicationDidEnterBackground, but how do I make that call a method that is in my ViewController class?
Thanks so much for any help!
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <ADBannerViewDelegate>
{
//Images and buttons
}
-(void)stop;
#end
You can use a notification here. Create a listener for this notification in the viewDidLoad of the viewController and assign the function to it.
eg:
in yourView controller add the following in the viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationEnabled) name:UIApplicationDidEnterBackgroundNotification object:nil];
You can either
observe UIApplicationDidEnterBackgroundNotification inside your ViewController, or
invoke your ViewController method inside appDelegates applicationdidEnterBackground: method. App delegate should have a pointer that points to the rootViewController, ie: your ViewController
goodluck!
edit:
...
-(void)applicationDidEnterBackground:(UIApplication *)application {
UIViewController *uvc= [UIApplication sharedApplication].keyWindow.rootViewController;
ViewController *myvc = (ViewController*) uvc;
[myvc stop];
}
.
When you close or leave your App, AppDelegate methods automatically called, you not need to call them in some specific ViewController.
For doing something when your App is in background you can implement your logic in AppDelegate's applicationDidEnterBackground: method.
Or if your App is not running(means closed) AppDelegate Method didFinishLaunchingWithOptions: is called.
I didn't get the purpose of calling the method inside UIViewController. Eventhough you can create an instance of your viewcontroller inside applicationDidEnterBackground and call the corresponding method in viewcontroller using that object
you can do this by creating an instance of that View and then calling specific method of that view
otherwise if your view is opened then you can find it by follwing code & then call specific method by using that obj as follow in your applicationDidEnterBackground
NSArray *AllViewControllers = [self.navigationController viewControllers];
for (UIViewController *aViewController in AllViewControllers) {
//NSLog(#" >> Nav Stack %#", [aViewController class]);
if ([aViewController isKindOfClass:[YourViewController class]]) {
[(YourViewController *)aViewController yourMethodToCall];
break;
}
}
I have two classes I would like them to speak with each other. Class A contains a tableView and when users hitting a table row, I fire my didSelectRowAtIndexPath method. In this method I need to inform class B about this through a delegate. I know how delegates work but having a hard time to figure how to set the delegate of A without using the prepareForSegue method.
Normally I would do this when I set up my delegate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"goToManipulator"]) {
ManipulatorViewController *secondVC = (ManipulatorViewController *) segue.destinationViewController;
[secondVC setDelegate:self];
}
}
But how can I set the delegate without the use of prepareForSegue?
Thanks in advance
EDIT :
This is how the structure of my storyboard looks like. The "receiver" viewcontroller is the one that will get the data and display in the "current name" label depending on what's been selected in the tableview from the "sender" viewcontroller, closest to the right.
http://oi62.tinypic.com/2li99w1.jpg
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ManipulatorViewController *secondVC = [[ManipulatorViewController alloc] init...];
[secondVC setDelegate:self];
//if you use push transition in UINavigationController
[self.navigationController pushViewController:secondVC animated:YES];
//if you use modal transition
[self presentViewController:secondVC animated:YES completion:nil]
}
init... means that initialization depends on your program architecture.
EDIT
If you want to get secondVC from storyboard, use
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ManipulatorViewController* secondVC = [storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
And don't forget to add identifier for your viewController in storyboard.
I understand your use case like this:
In the Receiver, you open the Sender. There you select a value, and after selecting the value you want to tell the Receiver about the new value.
You can create a protocol on Sender, that Receiver implements. Then, in the function that catches the chosen value in Sender, you call the protocol method (e.g. didSelectNewName() or something).
Of course, you need a handle to the Receiver, which you typically get via the delegate. But wether you use a segue or other method to transition from Receiver to Sender, you will all the same have the opportunity to set the delegate of the Sender.
If this is not what you are looking for, please explain exactly how you initialize the Sender, and why segue is not desirable.
Is View Controller B already instantiated when A's cells are tapped? If it is and you're not using prepareForSegue to get the other View Controller's identity, it might be better to use NSNotification Center. In View Controller A's didSelectRowAtIndex method, you can put
[[NSNotificationCenter defaultCenter] postNotificationName:#"yourNotificationName" object:nil userInfo:dictionaryWithYourData];
and it will put up a notification to your whole app that the row was selected. If you initialize a dictionary with any info you want before hand, it can be passed through userInfo. Then, in View Controller B's viewDidLoad, add
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethod:) name:#"yourNotificationName" object:nil];
to make it listen for the notification. The selector you set will accept the NSNotification as a parameter, so you can get the dictionary as follows:
- (void)yourMethod:(NSNotification *)notification
{
NSDictionary *yourData = [notification userInfo];
}
Here's what I do.
In the .m file:
#implementation ViewController{
SecondViewController *svc;
}
And then below you need to an action like this:
- (IBAction)goToView2:(id)sender {
if (!svc) {
svc = [[self storyboard] instantiateViewControllerWithIdentifier:#"View2"];
[svc setDelegate:self];
}
[[self navigationController] pushViewController:svc animated:YES];
}
Just make sure to set the correct identifier in the StoryBoard to the ViewController where protocol is declared.
I have a viewController I've built in storyboard. I also have a NSObject Subclass which acts as my model, which sends and listens for API requests and responses. When a method fires in my model, I want to present a modal View of my viewController from whatever view happens to be visible at the time.
An example would be if my API hears "show this view" I want to show viewController regardless of what view is being shown.
Conceptually, how does one do this?
EDIT: I don't know which view controller will be showing when I want to present my modal viewController. Also, I need to pass params from my model to the modalVC when it's presented.
I would send a notification from the model telling "someone" that some view needs be displayed.
NSDictionary *userInfo = #{ #"TheViewKey": viewToDisplay];
[[NSNoticationCenter defaultCenter] postNotificationName:#"NotificationThatThisViewNeedsToBeDisplayed" object:self userInfo:userInfo];
And then on the delegate (or the active view controller) would register to this notification and handle the display.
// self is the delegate and/or the view controller that will receive the notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleViewToDisplay:) name:#"NotificationThatThisViewNeedsToBeDisplayed" object:nil];
If you put in the view controller remember to remove self from the observers when the view is not visible:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"NotificationThatThisViewNeedsToBeDisplayed"];
This way your model is decoupled from the presentation.
You have the current viewController (any viewController subclass) present the new view using:
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
EDIT: To find the top view controller, you ask the UITabBarController for the selectedViewController (if you use a tabBarController) to get the 'seed', or start with the window.rootViewController.
Once you are past any tabBarControllers, then you should only have UIViewController subclasses and UINavigationControllers. You can use a loop like this:
- (UIViewController *)frontmostController:(UIViewController *)seed
{
UIViewController *ret;
if([seed isKindOfClass:[UINavigationController class]]) {
ret = [(UINavigationController *)seed topViewController];
} else
if([seed isKindOfClass:[UIViewController class]]) {
ret = seed.presentedViewController;
}
return ret ? [self frontmostController:ret] : seed;
}