I am trying to set the delegate of a view controller from my app delegate.
But it does not work.
AppDelegate.m:
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:nil];
SFLoginViewController * LoginVC = (SFLoginViewController *)[sb
instantiateViewControllerWithIdentifier:#"Login"];
LoginVC.delegate = self;
SFLoginViewController.m
- (IBAction)Login:(id)sender {
NSLog(#"%#",self.delegate); //returns nil (!!)
//It should call the delegate method here
[[self delegate] LoginSucceeded];
}
any help?
Why not set your delegate in the ViewController like this:
self.delegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
Then you'll be able to handle delegate events in your AppDelegate.
Looking at the instantiateViewControllerWithIdentifier documentation...
Discussion You use this method to create view controller objects that
you want to manipulate and present programmatically in your
application. Before you can use this method to retrieve a view
controller, you must explicitly tag it with an appropriate identifier
string in Interface Builder.
This method creates a new instance of the specified view controller
each time you call it.
I don't think the code you have in your appDelegate is returning the ViewController that is presented via the storyboard
By doing this
(SFLoginViewController *)[sb instantiateViewControllerWithIdentifier:#"Login"];
you are creating a new instance of SFLoginViewController.
I assume that you already have an instance of this viewcontroller created from the storyboard.
The instance from the storyboad is the one who call its method login:(id)sender and not the one you assigned the delegate.
Try #hw731 answer or you need to add the delegate to the instance created from the storyboard (not to the one you'are creating in your appdelegate).
Related
I'm trying to pass some data to my view controller class like this:
MyViewController *vc = [[MyViewController alloc] init];
vc.myProperty = dataToBePassed;
[self.navigationController pushViewController:vc animated:YES];
I need to make some view configuring in viewDidLoad, but it seems that viewDidload called earlier than property assignment.
Then in MyViewController implementation:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.myProperty); // Here i get myProperty = nil
}
- (void)viewWillAppear
{
[super viewWillAppear];
NSLog(#"%#", self.myProperty); // Here i get myProperty = dataToBePassed but it's to late
}
How can i get passed data in viewDidLoad method without implementing singleton or delegate patterns?
Try doing this
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:
#"MainStoryboard" bundle:[NSBundle mainBundle]];
MyViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"storyboardIdentifier"];
vc.myProperty = dataToBePassed;
[self.navigationController pushViewController:vc animated:YES];
You have to set a storyboard identifier first in the storyboard for the view controller.
While the code sample you provide looks technically correct, I'm with #john-elemans in that you need to show more code.
There is something that is referencing the view which causes it to load and therefore causes viewDidLoad to fire prematurely.
In any case, if something (such as your property) is absolutely essential to the correct building of your view structure, I'd put in its own designated initializer, e.g.,
- (id)initWithPhotoDiameter:(CGFloat)diameter
{
self = [super init...]; // some VC initializer that you should call
if (self) {
_photoDiameter = diameter;
}
return self;
}
Notice the use of the backing instance variable _photoDiameter instead of self.photoDiameter. This is about the only place in a class where you should use the backing ivar, since self is still in the process of being initialized.
Technically there are two approaches that are quite common for lifecycle handling of view controllers related to an application.
Using XIBs
When using XIBs one of the most common if not the most common process to create and setup your view controllers is done programmatically. Following this process, when you initialise the view controller you have the option of either overriding your init method in order for your view controller to have the information prior to loading the view and easing up the process of adjusting drawn content. You can also create a method within your view controller to be called in which you pass the data to be used by the view controller.
Using Storyboard
If you are using storyboards I recommend that you trust segues setup through it. I have found that they make life easier and it will allow you to use certain methods to handle the transition. One of those is prepareForSegue:sender: Within that method I have found that it is easier to setup a view controller after it's initialized accessing the destination controller. You also might consider having all data there before viewDidLoad hence following the segue approach.
I have multiple UIViewController objects within one main UIViewController. I need to call FVC method when I click the main view controller button. Here three view controllers having three separate class files.
From your first controller on didSelectRowAtIndexpath method,
UIStoryboard * board = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ViewController * cntrl = [board instantiateViewControllerWithIdentifier:#"ViewController"];
[self presentViewController:cntrl animated:YES completion:^{
}];
add above code. Here ViewController is nothing but a your Second view controller. Using reference of cntrl pass data to Second controller.
When you want to navigate to any other controller without having navigation controller reference , you use present view controller.
If you are trying to call a method present in other ViewController without presenting it, I guess you are doing it wrong, because that method belongs that ViewController class and ideally should be called when that ViewController's lifecycle is in progress.
For your scenario, I suggest that you should create a utility class, move that method which accepts two strings and then processes something in that utility class and then call that method from your ViewController1 something like :
[UtilityClassName yourMethodWithFirstString : str1 andSecondString : str2];
Hope that clears.
UIViewController *viewVC = [[UIViewController alloc] init];
[self presentViewController:viewVC animated:YES completion:nil];
//Loading a view controller from the storyboard
UIViewController *viewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"IDENTIFIER_OF_YOUR_VIEWCONTROLLER"];
[self presentViewController:viewVC animated:YES completion:nil];
In First View Controller
//Do this inside your btnCall method
SecondViewController * cntrl = [self.storyboard instantiateViewControllerWithIdentifier:#"secondViewControllerIdentifier"];
[cntrl methodName:firstParameter:secondParameter];
In SecondViewController
In .h file
-(void)methodName:firstParameter:secondParameter;
In .m file
-(void)methodName:firstParameter:secondParameter{
//Do your task here
}
Most of the information I found involving implementing protocols and delegates involves a step where you do this;
DestinationViewController *destinationVC = [[destinationViewController alloc] init];
destinationVC.delegate = self;
But after hours of frustration because I couldn't get it to work I finally stumbled across another way to allocate the destinationVC in prepareForSegue
DestinationViewController *destinationVC = segue.destinationViewController;
destinationVC.delegate = self;
Which actually works. What was I doing wrong? It seemed using the first method my delegate was never set to self.
When instantiated from a storyboard, the initWithCoder: methid is called, not the init method.
DestinationViewController *destinationVC = [[destinationViewController alloc] init];
destinationVC.delegate = self;
is how you do when your controller is not from a storyboard: you init it from the code. After that you have to manually handle the transition from your source VC to your destination VC.
DestinationViewController *destinationVC = segue.destinationViewController;
destinationVC.delegate = self;
is how you do when your controller is defined in a storyboard and is the destination of a segue.
When you perform a segue, the prepareForSegue: method of the source view controller is called, in which you should configure your destination like you want: setting properties, delegates, passing data,...
There is two way you can pushController while using UIStoryboard.
Option 1 : taking reference of actually UIViewController from storyboard.
UIViewController *displayTable = [self.storyboard instantiateViewControllerWithIdentifier:#"nextViewcontroller"];
[self.navigationController pushViewController:displayTable animated:YES];
Option 2 : Using Segue
[self performSegueWithIdentifier:#"MySegue" sender:sender];
In your first case you are allocating object and assign delegate. that does't means while performing pushViewController operation same reference is passing. so in that case two different reference is created. so you delegate is point out some other reference that doesn't exist.
may this help you.
Here is the basic tutorial about segues, updated for Xcode 6 and above.
https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/chapters/StoryboardSegue.html
When you use storyboards, all necessary context provided by UIStoryboardSegue class. it holds destination view controller for you. So, you must access destination controller throw destinationViewController property.
if you want to manually add controller to your navigation stack:
{
// binds your viewController from storyboard with local instance
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"YOUR_STORYBOARD_IDENTIFIER"];
// set your delegate
vc.delegate = self;
// push controller into navigation stack
[self.navigationController pushViewController:vc animated:YES];
}
I need to get in AppDelegate one parameter of some ViewController.
It not root for AppDelegate.
What is faster way to do it? Delegation?
Make it a property on your VC and then your AppDelegate can access it as needed.
First something is terribly wrong with your design otherwise there shouldn't be any need for you to do something like this.
Second, you haven't provided any relevant information about your VC hierarchy and there is no general solution for this.
However , here are few workarounds / patches:
1) If you are using storyboard you can use :
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"mystoryboard"
bundle:nil];
UIViewController* vc = [sb instantiateViewControllerWithIdentifier:#"ExampleViewController"];
2) You can make make view controller singleton and access it directly from AppDelegate
3) Hacky Method: In AppDelegate have a #property (nonatomic, retain) UIVIewController *hackyViewController;
In hackyViewController.m do this
-(void)viewDidLoad{
// call super
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
YourAppDelegate.hackyViewController =self;
}
Ideally , you should navigate through viewcontroller hierarchy using parentViewController and childViewcontroller property of UIVIewcontroller to get the instance. You can also make a recursive function which navigates through all childViewcontroller and check instance using iSKindOf to identify the viewcontroller you are looking for but this method does not work with all iOS configurations.
I am trying to switch views on button press event, but when i press the button to switch the view from firstview to second. It shows the secondView for a sec or so and then a blank page appears. Im new to iOS programming so not getting what might be the issue.
Here is my code :
- (IBAction)okPressed:(id)sender {
AppDelegate *mainDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
shipView = [mainDelegate.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self.navigationController pushViewController:shipView animated:YES];
The UIStoryBoard is instantiated in AppDelegate class. When the okPressed Method is invoked the SecondViewController is not loaded.
The standard alloc / init will not do anything. You need to either use initWithNibName:bundle: using the filename of your XIB, or UIStoryboard's instantiateViewControllerWithIdentifier: using the identifier of the controller that you defined in the storyboard file.