How to recognise the segue identifier on destinationVC from multiple segue? - cocoa-touch

I have this view which on it there are three UIButtons that each of which has segue identifier pushing to one VC.
Here's my code for preparing the segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Button 1"]) {
[[segue destinationViewController] setBudgetOrderingViewController:self];
} else if ([segue.identifier isEqualToString:#"Button 2"]) {
[[segue destinationViewController] setBudgetOrderingViewController:self];
} else if ([segue.identifier isEqualToString:#"Button 3"]) {
[[segue destinationViewController] setBudgetOrderingViewController:self];
}
}
Is there a way to know what segue identifier which load the destinationVC on the destinationVC?
Thanks.

You can give the destination view controller a property that identifies the segue, and set that property in the source view controller's prepareForSegue:sender: method. Example:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DestinationViewController *dvc = segue.destinationViewController;
dvc.segueIdentifier = segue.identifier;
if ([segue.identifier isEqualToString:#"Button 1"]) {
[dvc setBudgetOrderingViewController:self];
} else if ([segue.identifier isEqualToString:#"Button 2"]) {
[dvc setBudgetOrderingViewController:self];
} else if ([segue.identifier isEqualToString:#"Button 3"]) {
[dvc setBudgetOrderingViewController:self];
}
}
However, this is actually not a very good design. Now you have two view controllers that need to know all of the segue identifiers. You might forget to update one of them if you change an identifier or add a new one.
A better design is to make the source view controller tell the destination view controller what to do.
Let's use a concrete example. Suppose your app lets the user send a card to a friend when the friend has a baby. So your main screen has three buttons: “It's a boy!”, “It's a girl!”, and “It's a puppy!” When the user clicks any of these buttons, you want to segue to a screen where the user can type in a message. You want the message entry screen to be customized with a theme based on which button was pressed: pink hearts for girls, blue trucks for boys, and a doghouse for puppies.
Give your destination view controller a message for each of these possibilities:
#interface MessageComposerViewController : UIViewController
#property (nonatomic, weak) id<MessageComposerViewControllerDelegate> delegate;
- (void)useGirlTheme;
- (void)useBoyTheme;
- (void)usePuppyTheme;
#end
Then, in your main screen view controller's prepareForSegue:sender:, you test the identifier and send the appropriate message to the destination view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
MessageComposerViewController *dvc = segue.destinationViewController;
dvc.delegate = self;
if ([segue.identifier isEqualToString:#"Girl"]) {
[dvc useGirlTheme];
}
else if ([segue.identifier isEqualToString:#"Boy"]) {
[dvc useBoyTheme];
}
else if ([segue.identifier isEqualToString:#"Puppy"]) {
[dvc usePuppyTheme];
}
}

//Declare a string Property in Destination View Controller
#property (strong, nonatomic) NSString *Segue_Listner;
//In Source ViewController perform segue method
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier]isEqualToString:#"destinationVC"]) {
DestinationVC *dvc = [segue destinationViewController];
dvc.Segue_Listner = #"somevalue";
}
Passing whole view controller is ok, But we have to be careful if the view controller object size is too big.
Also make sure to declare destinationVC property as weak(ONLY IF YOU ARE PASSING VIEWCONTROLLER AND IT IS STILL IN THE MEMORY),i.e presenting modally.
I recommend its safe to declare a string property and made it set by previous/SourceVC.

Related

call popover and send back variable to previous uiview

I have an UITableViewController created with storyboard.
Than I have an anchor that call a popOver created with storyboard as well. The popover is another UItableViewController, when I click on a row I should call back the first controller and pass an object.
I have tried this in the popover object:
(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"Fortune"]){
NSIndexPath *indexPath = (NSIndexPath *)sender;
ASFortuneTeller * aController = [segue destinationViewController];
[aController setWYPT:[allWYPTs objectAtIndex:indexPath.row]];
}
}
Basically I need to pass a NSMutableDictionary bag to the first UITableViewController.
But I have noticed that in this way I create a new ASFortuneTeller object , that is not what I want... I want just to call back the first controller and pass an object.
How can I do it?
A quick solution (when it involves always the same two classes) could be:
In the .h file of the first view controller, define a method (or just a property):
-(void)selectedWYPT:(NSMutableDictionary*)wypt;
Within the .h file of your second view controller make a property
#property FirstUIViewController *firstView;
In the first view controller, you will open the second view controller via a segue, so there you can use:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segueIdentifier"]) {
SecondUIViewController *destination = segue.destinationViewController;
destination.firstView = self;
}
}
When the row is selected in the second view you can use
if (self.firstView)
[self.firstView setWYPT:[allWYPTs objectAtIndex:indexPath.row]];
to pass the data back to the first view.
As said, this would be a quick solution when always the same two classes are involved.
An other way would be to use protocols. When the first view controller won't always be FirstUIVierController, you may use something like this:
SecondUIViewController.h
#class SecondUIViewController;
#protocol SecondUIViewControllerDelegate <NSObject>
-(void)secondUIViewController:(SecondUIViewController*)controller didSelectWYPT:(NSMutableDictionary*)wypt;
#end
#interface SecondUIViewController : UIViewController
#property id<SecondUIViewControllerDelegate> delegate;
#end
SecondUIViewController.m
where the row is selected:
if (self.delegate && [self.delegate respondsToSelector:#selector(secondUIViewController:didSelectWYPT:)])
[self.delegate secondUIViewController:self didSelectWYPT:[allWYPTs objectAtIndex:indexPath.row]];
AnyOtherUIViewController.h
#import "SecondUIViewController.h"
#interface AnyOtherUIViewController : UIViewController <SecondUIViewControllerDelegate>
...
...
AnyOtherViewController.m
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segueIdentifier"]) {
SecondUIViewController *destination = segue.destinationViewController;
destination.delegate = self;
}
}
-(void)secondUIViewController:(SecondUIViewController*)controller didSelectWYPT:(NSMutableDictionary*)wypt {
//do something with the data
}

how to pass value from firstviewcontroller to thirdview controller in xcode?

i have declared a string in firstviewcontroller str1 and i want to get that string in thirdview controller and use it inside if loop.
firstviewcontroller
SecondViewController *secondview=[[SecondViewController alloc]init];
secondview.str1=[name objectAtIndex:indexPath.row];
[self.navigationController pushViewController:secondview animated:YES];
thirdviewcontroller
if([str1 isEqualToString:#"X"])
//&&[str12 isEqualToString:#"hospital"])
but it doesnt seem to work.pls help to sort out
Try like this:
SecondViewController *secondview=[[SecondViewController alloc]]nitWithNibName:#"SecondViewController" bundle:nil];
secondView.str1 = #"testString";
[self.navigationController pushViewController:secondView animated:YES];
In the prepareForSegue method, you can set a #property on each of your view controllers. You'll need to either use storyboard IDs or set your view controllers up as strong #properties.
VC1:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (_secondView && _secondView.yourPropertyName)
_secondView.yourPropertyName = #"something";
}
VC2:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (_thirdView && _thirdView.yourPropertyName)
_thirdView.yourPropertyName = self.yourPropertyName;
}

addObject to NSMutableArray doesn't work with storyboards

I have a table in my root view controller which has the "add" button mapped to another view controller which has text field to input the name of the list and a save button. That save button is mapped back to the main controller. When the data is passed back to the main controller, it doesn't add the item to the table.
This is what my table view controller's viewDidLoad look like:
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.allowsMultipleSelectionDuringEditing = YES;
if (!self.listArray) self.listArray = [[NSMutableArray alloc] init];
[self.listArray addObject:#"New Item"];
[self.tableView reloadData];
[self updateButtonState];
}
This adds the "New Item" to the list and shows up in the table. But when I pass the listname from the segue, I can see the log entry "Adding..." but doesn't add the item to the table. Here's the method for add a new item from a segue.
-(void) addList:(NSString *)listName
{
NSLog(#"Adding %#", listName);
[self.listArray addObject:listName];
[self.tableView reloadData];
}
Prepare for segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
LSNewListViewController *sourceView = [segue sourceViewController];
LSMainViewController *destView = [segue destinationViewController];
[destView addList:[[sourceView listName] text]];
}
Rather than using a storyboard segue to push another view controller, try setting a protocol in your "add" view controller. Then set your "main" view controller as the delegate and handle the adding there. From there you can connect the "add" button to call the delegate method and pop the view controller.
LSNewListViewController.h
#protocol LSNewListViewControllerDelegate;
#interface LSNewListViewController : UIViewController
#property (nonatomic, weak) id <LSNewListViewControllerDelegate> delegate;
#end
#protocol LSNewListViewControllerDelegate <NSObject>
#optional
- (void)didAddToList:(NSString*)item;
#end
LSNewListViewController.m
#implementation LSNewListViewController
// Hooked up to the Add button in the storyboard (touchUpInside)
- (IBAction)addToListAction:(id)sender {
if ([self.delegate respondsToSelector:#selector(didAddToList:)]) {
[self.delegate didAddToList:[sourceView listName] text]];
}
}
#end
LSMainViewController.h
#import "LSNewListViewController.h"
#interface LSMainViewController : UIViewController <LSNewListViewControllerDelegate>
#end
LSMainViewController.m
#implementation LSMainViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
LSNewListViewController *destView = [segue destinationViewController];
destView.delegate = self;
}
// Delegate method
- (void)didAddToList:(NSString*)item {
NSLog(#"Adding %#", listName);
[self.listArray addObject:listName];
[self.tableView reloadData];
[self.navigationController popViewControllerAnimated:YES];
}
#end

set navigationController's rootViewController as delegate

My problem may be one of technique instead of a misunderstanding of how controllers and delegates are set up. That is, maybe I should be doing all a different way...
In any event, I have a storyboard setup with a mainViewController. In it there's a UIButton which, when clicked, segues to a popover. The popover's content view controller is a UINavigationController who's rootViewController is, say, MyViewController.
I'm trying to make the mainViewController a delegate of MyViewController and am doing so in prepareForSegue:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:(#"popSleepSegue")] || [segue.identifier isEqualToString:(#"popAlarmSegue")])
{
UIStoryboardPopoverSegue *popSegue = (UIStoryboardPopoverSegue *)segue;
popSegue.popoverController.delegate = self;
popSegue.popoverController.passthroughViews = [NSArray arrayWithObject:self.view];
if ([segue.identifier isEqualToString:#"popAlarmSegue"])
{
if ([[segue destinationViewController] isKindOfClass:[UINavigationController class]])
{
UINavigationController *uNC = (UINavigationController *)[segue destinationViewController];
MyViewController *aVC = (MyViewController *)uNC.topViewController;
aVC.popController = popSegue.popoverController;
aVC.delegate = self;
}
}
}
}
The [self.delegate class] is coming up as null in an NSLog when MyViewController loads. And, naturally, the delegate callback isn't received in the mainViewController.
Essentially, I'm trying to mimic the behavior of Apple's Calendar app on the iPad.
I'm trying to use delegation to pass data upstream as per the idiom. The trick is that I'm trying to set the delegate through a UINavigationController which is the content view of a popover. Sounds too complex. Maybe there's another idiom?
In the meantime, I'm going to give NSNotificationCenter a whirl.
You have to potential 'if' statements which can not be true:
if ([segue.identifier isEqualToString:#"popAlarmSegue"])
{
if ([[segue destinationViewController] isKindOfClass:[UINavigationController class]])
{
From the look of your code, you should get the controller drilling inside the content controller in the PopOver,not the destination viewController from the segue. As it seems the second 'if' is not true.
You would need to add:
if ([[popSegue.popoverController contentViewController] isKindOfClass:[UINavigationController class]])
{
UINavigationController *uNC = (UINavigationController *)popSegue.popoverController;
MyViewController *aVC = (MyViewController *)uNC.topViewController;
aVC.popController = popSegue.popoverController;
aVC.delegate = self;
}

Transferring Data Back to Original View

So I am able to transfer the data from the first view to the second view like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Check Mark Segue"])
{
NSLog(#"Transfering Data");
AutoRenewDrop *controller = segue.destinationViewController;
controller.transferData = self.renewDate.text;
}
}
However, I try to transfer a new value back to renewDate.text when the user hits done and the transferData is working correctly but the renewDate.text does not change. Here is the code that I am using to transfer the data back:
-(IBAction)done:(UIStoryboardSegue *)segue {
[self.navigationController popViewControllerAnimated:YES];
AddR *add = [[AddR alloc] init];
add.renewDate.text = transferData;
}
Can someone tell me how to fix this?
You need to add a property that contain a reference of the first view into the second view :
#interface AutoRenewDrop
#property(weak, nonatomic) AddR *callerView;
#end
And then in the done method of the second view you can just update the variale in the caller view :
-(IBAction)done:(UIStoryboardSegue *)segue {
[self.navigationController popViewControllerAnimated:YES];
callerView.renewDate.text = transferData;
}
Of course when you instantiate the second view you will have to set the reference, in this way :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Check Mark Segue"])
{
NSLog(#"Transfering Data");
AutoRenewDrop *controller = segue.destinationViewController;
controller.transferData = self.renewDate.text;
controller.callerView = self; //Here, you are passing the reference to this View
}
}
You should use a delegate.
You can read a pretty good tutorial about protocols and delegates here.
Lets say you have 2 ViewControllers - VC1 and VC2, and lets say that VC1 displays VC2.
In VC2.h
#protocol VC2Delegate <NSObject>
- (void)updateData:(id)theData;
#end
#interface
#property (nonatomic, weak) id<VC2Delegate> delegate;
#end
In VC2.m
#syntesize delegate;
- (void)done
{
[delegate updateData:myData];
}
In VC1.h
#interface VC1 : UIViewController<VC2Delegate>
In VC1.m
- (void)goToVC2
{
VC2 *vc2 = [[VC2 alloc] init];
vc2.delegate = self;
// present vc2
}
- (void)updateData:(id)data
{
// update what you need
}