get sender of segue in destination view controller - ios

I have a VC named Dashboard (D) which can open a VC named Login (L) and a VC named Register (R). Login can open VC Register too.
I try to use storyboard as often as possible, so I have created with it three Segues, D to L, D to R, L to R
So, in case of D -> L -> R and in case of D -> R, when I close R, I have to close L if it necessary and inform D which he can begin to load the user infos (launch function in nutshell).
So, I would like get the sender of Segue in destination vc, knowing that I put it in sender entrie of performSegueWithIdentifier like that :
[self performSegueWithIdentifier:#"SegueToFbRegister" sender:self];

I'd do this by having R send a notification when the registration/login is done, and having D listen to it then pop everything and load your data.
If however you insist on getting a reference to the sender, you can add this property on your destination VC and set it in the source VC's prepareForSegue:sender:

This sounds like a great place to use Delegates. In your RegisterViewController.h define a protocol like this
#protocol RegisterViewDelegate <NSObject>
- (void)tellRegisterDelegateSomething:(NSObject*)something;
#end
Then on your class keep a pointer to your delegate
#interface RegisterViewController : UIViewController
#property (weak, nonatomic) id <RegisterViewDelegate> delegate;
#end
Now tell the presenting view controllers that they implement the new protocol you just created. This is done in the .h files of the other viewcontrollers that present this view.
In LoginViewController.h
#interface LoginViewController : UIViewController <RegisterViewDelegate>
#end
In DashboardViewController.h
#interface DashboardViewController : UIViewController <RegisterViewDelegate>
#end
In the .m files of the above classes, implement the protocol's method
- (void)tellRegisterDelegateSomething:(NSObject*)something
{
}
Now you need to assign the delegate when you perform your segue from either presenting view controller like this.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"SegueToFbRegister"])
{
RegisterViewController* destination = [segue destinationViewController];
destination.delegate = self;
}
}
Now you can call the presenting view controller (delegate) and have it do something with any information you need to send back like this (this would be called in your RegisterViewController.m).
if ([self.delegate respondsToSelector:#selector(tellRegisterDelegateSomething:)])
{
// Tell the delegate something.
[self.delegate tellRegisterDelegateSomething:something];
}
The instance where you need to pass back through two controller you follow the same basic pattern.
#protocol LoginViewDelegate <NSObject>
- (void)tellLoginDelegateSomething:(NSObject*)something;
#end
Then on your class keep a pointer to your delegate
#interface LoginViewController : UIViewController
#property (weak, nonatomic) id <LoginViewDelegate> delegate;
#end
Now tell the Dashboard view controller that it implements the protocol. This is done in the .h files of the Dashboard viewcontrollers that present this view.
In DashboardViewController.h
#interface DashboardViewController : UIViewController <RegisterViewDelegate, LoginViewDelegate>
#end
In the .m files of the DashboardViewController implement the protocol's method
Follow the above pattern of setting the delegate on the viewcontroller when you perform the segue. Now when the delegate method is called in the LoginViewController, you call the delegate in the DashboardViewController as well.
in LoginViewController.m
- (void)tellRegisterDelegateSomething:(NSObject*)something
{
if ([self.delegate respondsToSelector:#selector(tellLoginDelegateSomething:)])
{
// Tell the delegate something.
[self.delegate tellLoginDelegateSomething:something];
}
}
Now you are all connected so you can pass data back through both controllers (or just one) and do something with it. You will know which scenario you are in because different delegate methods will be called in the DashboardViewController based on which viewcontroller was visible.
Hope this helps.

Create a delegate for R and make D and L to implement the delegate methods.Use prepareForSegue:sender to assign the delegate of R.When you finish task in R use your delegate to perform the rquired action.

Another way would be to use an unwind segue.
Place the following code in you Dashboard (D) view controller.
#IBAction func loadUserInfoAfterRegistration(segue: UIStoryboardSegue) {
}
In Interface Builder, do the following steps for the Register (R) view controller:
Select the button that will be pressed on the completion of registration.
Ctrl + drag to the exit symbol on top of the view.
Select loadUserInfoAfterRegistrationWithSegue: from the list displayed.
Using this approach, the Register (R) view controller will always navigate to the Dashboard (D) view controller, regardless of what is between them. The view controllers between them will not have to be touched. The loading of the user data in the Dashboard (D) view controller can also be customized in the method declared above.

You can get it easily by using the type of the parent controller such as
let temp = self.navigationController?.viewControllers
if (temp != nil){
if let _parent = temp![temp!.count-2] as? UIControllerClass {
//do what you want here with the _parent
}
}
You have to subtract 2 because last one is the current view that you want to get its parent.

Related

Problems Linking Up a Switch in Xcode?

I am having trouble linking up a switch. I understand how to connect the switch as an outlet and have it have actions change the boolean state of a value, but I need the switch to perform an action in a different view controller.
Here's the situation: I have a main table view controller, called View Controller A. I have a second view controller, lets call it view controller B, that controls a menu sidebar (on a regular view controller, not a table view) triggered by a bar item. I want to be able to open up the menu, hit a switch in the sidebar, and have something change in the main table view that is controlled by view controller A.
Is there any way that I can accomplish this? I seem to have no way of accessing or changing the IBOutlets in View Controller A from B. Is there a way that I can have the action in B linked with the switch change the boolean state of a value, and have an action waiting in controller A that will respond to a change in boolean? I am not sure how to solve this problem. Help is appreciated!
You should use delegation pattern. You'll have an action waiting in controller A, but instead of responding to value changed in B the action will be triggered by B when appropriate
ViewControllerB.h
// Create delegate protocol and property
#protocol ViewControllerBDelegate <NSObject>
- (void)switchPressed:(BOOL)switchStatus;
#end
#interface ViewControllerB : NSObject
#property (nonatomic,weak) id<ViewControllerBDelegate> delegate;
#end
ViewControllerB.m
// When switch is tapped, call delegate method if it is implemented by delegate object
- (IBAction)flip: (id) sender {
UISwitch *onoff = (UISwitch *) sender;
if ([self.delegate respondsToSelector:#selector(switchPressed:)]) {
[self.delegate switchPressed:onoff.on];
}
}
ViewControllerA.h
// Conform to ViewControllerB protocol
#import ViewControllerB.h
#interface ViewControllerA : NSObject,ViewControllerBDelegate
ViewControllerA.m
// Set self (VC A) as VC B's delegate
- (void)ViewDidLoadOrSomeOtherFunction {
ViewControllerB *vcB = [[ViewControllerB alloc] init];
vcB setDelegate = self;
}
// Implement delegate method
- (void)switchPressed:(BOOL)switchStatus {
if (switchStatus) {
// Make changes on VC A
}
}

How to go back to previous view controller?

I have an UIViewController A with push segue to UIViewController B, in some case i need to repeat this for example:
ViewController A -> ViewController B -> ViewController A -> ViewController B
Also i need to pass data from ViewController B -> ViewController A
I don't want to create 4 ViewControllers, so how can I recycle code in this case?
You simple push and pop the same view controllers.
If you want B to communicate with A then setup a protocol at A that communitactes with the B. (or who ever implements the protocol)
B code
#protocol MyProtocol <NSObject>
-(void)callFromB:(id)data;
#end
#property (weak) id <MyProtocol> delegate;
implementation:
if ([self.delegate respondsToSelector:#selector(callFromB:)]){
[self.delegate callFromB:data];
}
A code
B.delegate = self;
-(void)callFromB:(id)data {
...
}
You should not recycle them. By that I mean you should not recycle the concrete instances of those view controllers.
Certainly there might her some technical implementation allowing that but it is (contrary to table view cells) not necessary.
You are trying to optimise prematurely. Just create new view controller instances as you need. Any iPhone supporting iOS6 and later will handle that without any problem.
Passing the data can be done by a convenient method for example
-(void)configureWithData:(WhateverDataType *)paramData
In prepare for segue, you would obtain a reference to the target view controller and pass the data like this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
CustomViewController *nextViewController = [segue destinationViewController];
[nextViewController configureWithData:someData];
}

How can I pass data between two View Controllers in a Navigation Controller, with two Push Segue?

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

Understanding the mechanism when passing data back from a second view controller to main view controller

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.

Does unwinding a storyboard (exit) segue replace the need for a child delegate in a parent scene?

Does unwinding a storyboard segue in ios6 replace the need to make a source scene implement a delegate to pass data back from the child scene to the parent scene in ios5?
The way I usually do it is:
Parent Controller Header:
Call the Delegate of the child scene
#interface ParentViewController : UIViewController <ChildViewControllerDelegate>
//ok not much to show here, mainly the delegate
//properties, methods etc
#end
Parent Controller Main (body):
Prep the segue, set the delegate, create a return method from child scene
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"toChildScene"])
{
UINavigationController *childViewController = segue.destinationViewController;
childViewController.delegate = self;
}
}
#pragma mark - Delegate Segue Methods
-(void) childViewControllerDidSave: (ChildViewController *) controller Notes:(NSString *)sNotes
{
someTextLabel.Text = sNotes
[self dismissModalViewControllerAnimated:YES];
}
Child Controller Header:
create the delegate, reference the parent scenes methods
#class ChildViewController;
#protocol ChildViewControllerDelegate <NSObject>
-(void) childViewControllerDidSave: (ChildViewController *) controller Notes:(NSString *)sNotes
#end
#interface ChildViewController : UIViewController
#property (weak, nonatomic) id <ChildViewControllerDelegate> delegate;
//properties, methods, etc
#end
Child Controller Main (body):
call the parent scenes method
- (IBAction)someAction:(id)sender
{
[self.delegate childViewControllerDidSave:self sNotes:someTextField.text];
}
So now the million Dollar question:
Is this process now simpler in iOS 6? Can I cut a lot of the work out using unwinding a segue / exit segue? Any example would be greatly appreciated.
Yes.
Unwind segues are an abstracted form of delegation. In iOS 6, it's simpler to use unwinds rather than delegates to pass data backwards when dismissing view controllers.
In the parent view controller, create an unwind method that returns an IBAction and takes a UIStoryboardSegue as the argument:
- (IBAction)dismissToParentViewController:(UIStoryboardSegue *)segue {
ChildViewController *childVC = segue.sourceViewController;
self.someTextLabel.Text = childVC.someTextField.text;
}
Then, in the child view controller, Control-drag from your dismiss button to the green exit icon to connect the unwind segue:

Resources