call popover and send back variable to previous uiview - objective-c

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
}

Related

Multiple webviews in one UIViewController

Lets say I have a UIViewController with two buttons, both going (push) to another UIViewController that has two UIWebViews (showing two different PDF files), how can I make sure that only the one I choose via the button is showed?
You need to pass some information to the UIViewController which has the UIWebViews, saying which button was pressed. Then, based on that information, decide which of the UIWebViews to display.
As you are using storyboards, I suggest you look into prepareForSegue. It will allow you to set a property on the destination view controller with something like the following. You should add this to the UIViewController which contains the buttons.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"buttonOne"]) {
ExampleViewController *destViewController = segue.destinationViewController;
destViewController.buttonClicked = #"One";
} else if ([segue.identifier isEqualToString:#"buttonTwo"]) {
ExampleViewController *destViewController = segue.destinationViewController;
destViewController.buttonClicked = #"Two";
}
}
You can then use the buttonClicked property in the destination view controller to decide which you should display. If you have two separate UIWebViews, you could choose to hide one using webViewOne.hidden = YES; and show the other using webViewTwo.hidden = NO;.
However, it would probably be neater to only have a single UIWebView. You could then use prepareForSeque to pass in the URL of the PDF you would like it to display, rather than just sending the name of the button clicked.
Assuming you webView is in a view controller called SecondViewController and your buttons are in the view controller called FirstViewController
1) Create an object in your SecondViewController.h
#interface SecondViewController : UIViewController
#property (nonatomic, strong) NSString *whichButtonClicked;
#end
2) Import SecondViewController in your FirstViewController
#import "SecondViewController.h"
3) In you button IBAction method in FirstViewController.m . use this code
- (IBAction) firstButtonClicked
{
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondView"];
secondViewController. whichButtonClicked = #"first"
[self.navigationController pushViewController:secondViewController animated:YES];
}
- (IBAction) secondButtonClicked
{
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondView"];
secondViewController. whichButtonClicked = #"second"
[self.navigationController pushViewController:secondViewController animated:YES];
}
PS Don't forget. In you Storyboard. Set Storyboard ID for SecondViewController as secondView
4) In your SecondViewController.m use this code to check which button
if ([self.whichButtonClicked isEqualToString:#"first"])
{
///display first web view here
}
else
{
//display second web view here
}
Hope this helps

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

How to recognise the segue identifier on destinationVC from multiple segue?

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.

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
}

Calling method to view controller delegate, won't dismiss modal view

I have the following simple view controller class set up
#protocol ThermoFluidsSelectorViewControllerDelegate;
#interface ThermoFluidsSelectorViewController : UIViewController <UITextFieldDelegate>
#property (weak, nonatomic) id <ThermoFluidsSelectorViewControllerDelegate> delegate;
// user hits done button
- (IBAction)done:(id)sender;
#end
#protocol ThermoFluidsSelectorViewControllerDelegate <NSObject>
-(void) didFinishSelection:(ThermoFluidsSelectorViewController *)controller fluidID: (NSString *)fluidID;
#end
the 'didFinishSeletion: fluidID:' method is defined in the master view controller and should dismiss the selector view controller when called. When the done button is pressed the following method is called:
- (IBAction)done:(id)sender
{
[[self delegate] didFinishSelection:self fluidID:nil];
}
the 'done:' method gets called (checked with an alert) but 'didFinishSelection...' is not getting called so the view will not revert back to the main screen. Any ideas?
It sounds like you have not assigned your delegate in your master view controller.
You should have something like this in your master view controller which sets up the delegate:
ThermoFluidsSelectorViewController *view = [[ThermoFluidsSelectorViewController alloc] init];
view.delegate = self;
here you can see I create the view, then set the delegate of the view back to myself.
If you are not creating the Thermo... view controller programatically, but have used a storyboard, then you can set the delegate in the prepareForSegue: method of your master view controller:
// Do some customisation of our new view when a table item has been selected
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure we're referring to the correct segue
if ([[segue identifier] isEqualToString:#"MySegueID"]) {
// Get reference to the destination view controller
ThermoFluidsSelectorViewController *cont = [segue destinationViewController];
// set the delegate
cont.delegate = self;
Hope this helps.