set navigationController's rootViewController as delegate - objective-c

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;
}

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;
}

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

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.

Pushing a controller from a subview

In essence what is the correct way of pushing a new view controller from a sub view of a navigation controller.
The issue being subviews of the navigation view don't inherit the self.navigationController (its nil)
The reason is I need separate controllers for the navigation bar view & the main view but both need to push new controllers.
I am willing to change this model if someone can tell me the correct way of doing this.
Also:
AppDelegate *del = (AppDelegate *)[UIApplication sharedApplication].delegate
[del.navigationController pushViewController:vc animated:YES];
Does not work as the delegates controller is nil.
Create a following category on UIView.
#interface UIView (GetUIViewController)
- (UIViewController *)viewController;
#end
#implementation UIView (GetUIViewController)
- (UIViewController *)viewController;
{
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return nextResponder;
} else {
return nil;
}
}
#end
Now get the SuperView from the subView.
mySuperView = [mySubView superview];
Then call the method from category created.
mySuperViewController = [mySuperView viewController];
Now, using this viewController, you can access the navigationController.
I have not tried the above code and approach, but I hope it should work.
You may try two ways
1.Use the superViewController's navigationController to push your viewController
2.Embed your current viewController in a NavigationController so that the navigationController won't be nil