How to invoke (fake event of) the button which is in a second view controller when button in first view controller is clicked?
I can fake the event for a button which is in the same view controller by doing this:
- (IBAction)first:(id)sender {
NSLog(#"this is second");
}
- (IBAction)second:(id)sender {
[self.first sendActionsForControlEvents:UIControlEventTouchUpInside]; // works
}
But if the button is in a different view controller, I'm not able to fake it:
- (IBAction)first:(id)sender {
SecondViewController *SecondView = [[ViewController alloc] init];
[SecondView.button sendActionsForControlEvents: UIControlEventTouchUpInside]; // doesnt work
}
Assuming your 2nd VC is owned by your 1st VC, then
a) Add a property like this to a class extension of your FirstViewController, for example:
#import "SecondViewController.h"
#interface FirstViewController()
// Be aware of retain cycles.
#property (nonatomic, retain) SecondViewController *secondVC;
#end
b) When you create the SecondViewController, store it in the property we just created:
// Change this if you're not using a Storyboard.
self.secondVC = [[UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:nil]
instantiateViewControllerWithIdentifier:#"SecondViewController"];
c) Add a method to your SecondViewController:
- (void)clickMyButton:(id)sender {
[self.myButton sendActionsForControlEvents:UIControlEventTouchUpInside];
}
d) Make it happen from your FirstViewController:
[self.secondVC clickMyButton:self];
If my 1st assumption is not correct, you still can do steps A, B, and D. Step B changes a bit: You have to set self.secondVC with the existing instance that was created somewhere else.
Related
My root view has a button called update that is hidden, and a class called cHome.
When the user exits the RootView and goes to another View I call cEdit and has a class called cEdit. Is there a way for this view to set the hidden state of the update button on my rootview?, so when it calls
[self.navigationController popViewControllerAnimated:YES];
to return to the root view, the update button will have the new hidden state?
to return to the root view, the update button will have the new state?
There is one way to do what you want, you can implement you own delegate.
For example :
you create a new class file in the .h you add this code
#import <UIKit/UIKit.h>
#protocol ClassNameDelegate <NSObject>
#optional
- (void) tellUpdateButtonToHide;
#end
#interface ClassNameViewController : UIViewController
#property (weak,nonatomic) id <ClassNameDelegate>delegate;
you create your own delegate, and you have to add a property, if you like to get access to the protocol
in your .m file i just add a button in the other controller (not the rootVc) that will update the rootController
- (IBAction)tellUpdateButtonToHide:(id)sender {
[self.delegate tellUpdateButtonToHide];
}
in your rootVC .h file you import the delegateClass
#import "ClassNameViewController.h"
#interface RootVcViewController : UIViewController<ClassNameViewControllerDelegate>
and in your rootVc .m file you implement the method tellUpdateButtonToHide;
- (void)tellUpdateButtonToHide{
[self dismissViewControllerAnimated:YES completion:nil];
[self.updateButton setHidden:NO];
}
and in your prepareForSegue
if ([segue.identifier isEqualToString:#"da"] ) {
ClassNameViewController *vc =(ClassNameViewController *)segue.destinationViewController;
[vc setDelegate:self];
}
Hope it work for you
On your root view controller, you can do something as simple as this: That way, when you return to the root view controller when you later call: [self.navigationController popViewControllerAnimated:YES]; the button will no longer be hidden.
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
self.update.hidden = NO;
}
I have checked previous queries all but they are different or too advanced for me. I have buttons done programatically so I need to segue using program as well. I am not sending anything, just to go to the next ViewController. Documentation said if not passing anything just use: - (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination. But I don't understand plus (+) or class yet. Also I cannot just put gjRelaysViewController as destination, Xcode says it is not declared. It is correct for me to create an instance? I have tried prepare for segue but nothing came out of it. Thanks.
Source: gjViewControllers
#property (nonatomic, strong) UIStoryboardSegue *relaysSegue;
#property (nonatomic, strong) gjRelaysViewController *relays;
[_pushButton1 addTarget:self action:#selector(relayCircuitsOn:) forControlEvents:UIControlEventTouchDown];
-(void)relayCircuitsOn: (id) sender
{
_relaysSegue = [[UIStoryboardSegue alloc] initWithIdentifier:#"toRelays" source:self destination: _relays];
}
Destination: gjRelaysViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if(![segue.identifier isEqual: #"toRelays"])
{
return;
}
}
Ctrl-Drag in InterfaceBuilder from ViewController 1 to ViewController 2 to create a segue. Click on the segue and give it a name in the right pane.
In your code in relayCircuitsOn call
[self performSegueWithIdentifier:#"Insert the name you specified above" sender:self];
and you should be all set.
Alternatively you can do the transition in code:
Import ViewController 2 in ViewController1.m `#import "ViewController2.h"
Create a new instance of ViewController2: ViewController2 *vc = [[ViewController2 alloc] init];
Push it to the navigation stack (if you have one?): [self.navigationController pushViewController:vc animated:YES];
I have two view controllers inside a Navigation Controller.
In the first view controller I have two buttons. Both of them call the second view controller using a Push segue, but:
I need to know which button sent me in the second view controller. How?
In the second view controller I have a UIDatePicker and a Button "Ok": how can I send the chosen date to the first view controller when Ok is pressed? (And how do I receive them?)
EDIT:
I don't know if my problem is clear: now I know how to pass data from the first view controller to the second view controller with prepareForSegue, but what I really need is to pass data (the picked date) from the second view controller to the first, and how can I do it without a prepareForSegue (when Ok is pressed)?
EDIT2:
I made it. It was so simple, guys...
I decided to use modal segue:
Firstviewcontroller.h:
+(FirstViewController *)getInstance;
Firstviewcontroller.m:
static FirstViewController *instance =nil;
+(FirstViewController *)getInstance
{
return instance;
}
and in its ViewDidLoad:
instance = self;
Secondviewcontroller.m, in the OkButton IBAction:
SecondViewController *secondViewController = [SecondViewController getInstance];
//...
//modify what I need to modify in secondviewcontroller
//...
[self dismissModalViewControllerAnimated:YES];
That's it.
Thank you all anyway.
Assign Identifier to each segue in storyboard and implement
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
[vc setDelegate:self];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
}
}
For more info about How to use storyboard and pass value check this article or this discussion on stackoverflow
for the second question you can use delegate pattern
IN SecondViewController.h
#protocol SomethingDelegate <NSObject>
- (void)dateChanged:(NSString *)dateStr; //you can use NSDate as well
#end
#interface ViewController2 : UIViewController
#property(weak) id<SomethingDelegate> delegate;
#end
in .m file
-(void) OkClicked{
[_delegate dateChanged:#"YOUR_DATE_VALUE"];
}
In FirstViewController.h
#import "SecondViewController.h"
#interface FirstViewController : UIViewController<SomethingDelegate>
in .m
-(void)dateChanged:(NSString *)dateStr{
// do whatever you need with dateStr
//also i made some change in prepareForSegue method
}
Note:- take care your naming convenes for VC
just pass the button id to the second viewcontrol.
use delegates to sent the data from second viewcontroller back to first view controller
regards
Johan
I have two ViewControllers, which aren`t (directly) connected with a Segue. But I want to change a String in the second VC, which i get in the first one. My try was:
#import "secondViewController.h"
#interface firstViewController ()
#property NSString* originalString;
#end
#implementation firstViewController
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
secondViewController* svc = [secondViewController new];
svc.anotherString = self.originalString;
}
But it dosent work, because I've only created a instance of the second VC, so the value was not saved. Also I can`t use the Storyboard ID, because I use Xcode 5.
I have a menuVC from which you can get to the firstVC and the secondVC. And from the firstVC I can go back (with the navigationbackbarbutton) to the menu. so: menu->firstVC->menu. menu->secondVC->...->menu
My try with StoryboardID:
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
secondViewController* svc =[[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"secondVCSrorybradID"];
svc.anotherString = self.originalString;
}
you can pass the string to second view controller with this code.
secondViewController* svc =[self.storyboard instantiateViewControllerWithIdentifier:#"Your Second VC's Storyboad ID"];
svc.anotherString = self.originalString;
[self presentViewController:svc animated:YES completion:nil];
//you have to create anotherString property in second View Controller's .h File.
now you can get the string of originalString to second VC. Now you can get this value back second VC to first VC.
hope this helps you.
You should pass your data successively through your UIViewControllers navigation. If, for example, you have a navigation like FirstVC > SecondVC > ThirdVC :
In your FirstVC.m, use :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((SecondVCClass*) segue.destinationViewController).secondVCString = _firstVCString;
}
With secondVCString being a #property in your second ViewController.
In your SecondVC.m, use :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((ThirdVCClass*) segue.destinationViewController).thirdVCString = _secondVCString;
}
With of course thirdVCString being a #property in your third ViewController.
Edit:
As you updated your question, here is what I suggest :
In your MenuVC, add this :
#property (nonatomic, weak) NSString *importantString;
In your FirstVC and SecondVC, add this :
#property (nonatomic, weak) MenuVCClass *menu;
When you push to FirstVC or SecondVC, use prepareForSegue to set the destination's view controller menu property to your menu :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"FirstSegue"])
((FirstVC*) segue.destinationViewController).menu = self;
else if ([segue.identifier isEqualToString:#"SecondSegue"])
((SecondVC*) segue.destinationViewController).menu = self;
}
In FirstVC or SecondVC, you can change the NSString value from your menu using _menu.importantString = #"";
You should never use new to create a view controller.
If you're using storyboards but not using segues, you can still create a view controller from the storyboard and invoke it.
Use the method instantiateViewControllerWithIdentifier: to create an instance of the target view controller. The set the properties you want to set, and finally make a call to display it (present it modally, push it onto the navigation stack, or whatever is appropriate for your program.)
I'm currently trying to have a better understanding on how the mechanisms of passing data between controllers work and I'm a little confused especially when passing data back from a second view controller to the main view controller.
This is what I have that works but don't fully understand. I have two view controllers, in the first one I have a button that when clicked it basically goes to the second view controller and a label which shows a message sent from the second view controller. In the second view controller I have a button and a textField, the button basically sends whatever is in the textfield to the label in main view controller.
Here is the code...
// FirstVC.h
#import <UIKit/UIKit.h>
#import "SecondVC.h"
#interface FirstVC : UIViewController <passNames>
#property (nonatomic, strong) NSString* firstNameString;
#property (weak, nonatomic) IBOutlet UILabel *firstNameLabel;
#end
//FirstVC.m
#import "FirstVC.h"
#implementation FirstVC
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier]isEqualToString:#"secondController"])
{
UINavigationController *navController = segue.destinationViewController;
SecondVC *vc2 = (SecondVC*)navController.topViewController;
[vc2 setDelegate:self];
}
}
-(void)viewWillAppear:(BOOL)animated
{
self.firstNameLabel.text = _firstNameString;
}
-(void)setFirstName:(NSString *)firstName
{
_firstNameString = firstName;
}
#end
//SecondVC.h
#import <UIKit/UIKit.h>
#protocol passNames <NSObject>
-(void)setFirstName:(NSString*)firstName;
#end
#interface SecondVC : UIViewController
#property (retain)id <passNames> delegate;
- (IBAction)send:(UIBarButtonItem *)sender;
#property (nonatomic, strong) NSString *firstNameString;
#property (weak, nonatomic) IBOutlet UITextField *firstNameText;
#end
//SecondVC.m
#import "SecondVC.h"
#import "FirstVC.h"
#interface SecondVC ()
#end
#implementation SecondVC
- (IBAction)send:(UIBarButtonItem *)sender
{
_firstNameString = _firstNameText.text;
[[self delegate]setFirstName:_firstNameString];
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Can someone explain how the prepareForSegue method works in the above code? The reason for this question is because I added an NSLog and it looks like this method is only called in the transition from main view controller to the second controller. Why is this method needed if it is not called when transitioning from second view controller to main view controller which in my case is what I'm doing? It makes sense to use it when passing data from main view controller to a second controller not on the case shown above.
Can some explain the whole mechanism when passing data back to the main view controller?
FYI, I do understand about protocols and delegation.
Thanks a lot.
In your case, you are setting your delegate method of the second view controller to self in mainViewController in you prepareForSegue. This means that apart from navigating to the SecondViewController, you are implementing the callback mechanism in your main view controller, so that your delegate method gets called when the value is passed from the second view controller and this delegate method collects the value as a parameter to handle it in the main View Controller. You might have set the delegate of VC2 as self inn your prepareForSegue because you are creating the instance of VC2 in this method to navigate to the second controller.
Your goal is to hand back the data, like this:
[[self delegate] setFirstName:_firstNameString];
But you can't do that unless you know who to send setFirstName: to, and the compiler won't let you do it unless you guarantee that whoever you are sending setFirstName: to can accept that message.
That is what prepareForSegue prepares. FirstVC has declared that it adopts the passNames protocol, which means that it implements setFirstName:. And now you are saying:
[vc2 setDelegate:self];
...where self is the FirstVC instance. This solves both problems at once. The SecondVC instance (vc2) now has a delegate (the FirstVC instance), it is the right object to send the info back to, and because its delegate is declared as adopting passNames, we know that SecondVC can actually send setFirstName: to that delegate.
Now to the heart of your actual question: The reason for doing this in prepareForSegue is merely that this is the only moment when the FirstVC instance and the SecondVC instance "meet" one another! There is no other moment when the FirstVC instance has a reference to the SecondVC instance so as to be able to call setDelegate on it in the first place. If you weren't using segues and storyboards, the FirstVC would simply create the SecondVC instance directly - and would set itself as its delegate, just as you do:
SecondVC *vc2 = [SecondVC new];
UINavigationController *nav = [
[UINavigationController alloc] initWithRootViewController: vc2];
[vc2 setDelegate:self];
[self presentViewController: nav animated: YES completion: nil];
This is one reason I don't like storyboards: they muddy the story. It's all so simple and obvious when you don't use them and just do everything directly like this.