I have following code:
LoginViewController *lvc = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
[self presentModalViewController:lvc animated:false];
[lvc release];
That is called from my MainViewController.
Now, when the LoginViewController will be dismissed (of course this only happens when the login is correct) I must call a method in my MainViewController to load the initial data for my app.
I read a lot about delegate and tried it, but don't get it to work.
Could someone help me please?
(if possible, please with a few lines of code)
Any help is welcome!
I read a lot about delegate and tried it, but don't get it to work.
What have you tried really? Your LoginViewController must define a simple delegate protocol, and your MainViewController must conform to it.
All you need to do is add something like this in LoginViewController.h above #interface:
#protocol LoginViewControllerDelegate
- (void)loginViewControllerDidFinish;
#end
Which declares a protocol with one method. Then add this between #interface and #end:
#property (nonatomic, assign) id <LoginViewControllerDelegate> delegate;
Which means your login view controller will have a property called delegate which will point to an instance of any class (that's what id means) that conforms to it's delegate protocol (the thing between < and >). Don't forget to #synthesize delegate; inside .m file.
Now what you need to do is inside MainViewController.h add to #interface line like this:
#interface MainViewController : UIViewController <LoginViewControllerDelegate>
Which tells the compiler your MainViewController class conforms to this LoginViewControllerDelegate delegate protocol. Now implement the - (void)loginViewControllerDidFinish; method inside MainViewController.m and before presenting the login view controller modally set it's delegate to self (login.delegate = self;). When you are done inside your login view controller, before you dismiss it, call the delegate method on your delegate:
[self.delegate loginViewControllerDidFinish];
And that's it. Any more questions?
Try this:
1) when pushing login view, set some flag in MainViewController
2) in method viewWillAppear in MainViewController check that flag from 1). If it is set then load the initial data and unset flag. Otherwise push LoginView.
You've got an UIApplicationDelegate, and it should have an instance variable that points to the MainViewController. Expose this instance variable via a property, say mainViewController (on your UIApplicationDelegate), and then you can access it like this:
[(MyUIApplicationDelegate *)[[UIApplication sharedApplication] delegate] mainViewController]
Related
I've been struggling with a problem lately, and believe me I wouldn't post here if the solution was online somewhere.
Here is my problem :
I have a parentVC with 3 childVC linked to it. I have delegates for the 3 of them to report to the parentVC some actions, and it's working for 2 of them and not the 3rd one even if I declared the same thing for the 3 of use.
Here is my code:
In parentVC.m
// Initializing the clientsListViewController
self.clientsListViewController = [[UIStoryboard storyboardWithName:#"AGZClientsList" bundle:nil] instantiateInitialViewController];
[self addChildViewController:self.clientsListViewController];
[self.clientsListViewController didMoveToParentViewController:self];
self.clientsListViewController.delegate = self;
In my childVC.h
#protocol AGZClientsListViewControllerDelegate <NSObject>
- (void)didClickLeftBarButtonInClientsView:(UIButton*)button;
#end
#interface AGZClientsListViewController : AGZBaseViewController
#property (nonatomic, weak) id <AGZClientsListViewControllerDelegate> delegate;
#end
In my childVC.m
- (void)didClickLeftBarButton:(UIButton*)button
{
if ([self.delegate respondsToSelector:#selector(didClickLeftBarButtonInClientsView:)])
[self.delegate didClickLeftBarButtonInClientsView:button];
}
Please help me!
Is parentVC released before your delegate is invoked? You set the delegate weak, so if the object is free, it will be set to nil.
May be your parentVC is not conforming your childVC's protocol properly. When you the compiler encounters self.delegate, it is trying to find the Controller class who will should conforms to your protocol; basically it is trying to find the delegate class that should implement the methods that you have enlisted inside the #protocol ....#endpart of your childVC class. May be for some reason, it failed.
Let's try a check list-
Have you declared your custom delegate inside the protocol list in the ParentVC. In the parentVC.h class at the interface declaration do
#interface parentVC: UINavigationController
PS: I assumed your parentVc is a navigation controller. It doesn't matter whether it is Navigation controller or just a view controller.
If you have, try moving
self.clientsListViewController.delegate = self;
right after you instantiate the controller, meaning after
self.clientsListViewController = [[UIStoryboard storyboardWithName:#"AGZClientsList" bundle:nil] instantiateInitialViewController];
If still doesn't work then, put a break point after
self.clientsListViewController = [[UIStoryboard storyboardWithName:#"AGZClientsList" bundle:nil] instantiateInitialViewController];
and try printing self.clientsListViewController to see whether you have the instance of your childVc. If it prints nil then there is your problem- your parentVC doesn't have the proper instance of your childVc, so does the delegate.
In that case, try to instantiate the childVC correctly.
I have declared my method 'callWEBservice()' in ViewController1.m and i want to call in ViewController2.m . I have created object of ViewController1.m in ViewController2.m as:
ViewController1* mainVC = [[ViewController1 alloc] init];
Now i am trying to call that method but i am unable to do. Please help on this as I am new to iOS and I have searched some are saying to use delegates.
You need to define the method signature in your .h file -
- (void) callWebService;
and then in your .m file you define the method body:
- (void)callWebService
{
// Whatever you need to do to call the web service
}
Then in ViewController2.m you can #import "ViewController1.h"
Now you can call [mainVC callWebService];
BUT The code you have shown creates a new instance of ViewController1 - If you already have an instance of ViewController1, such as the main view in your app, then this probably isn't what you wanted - you may need to set a property in ViewController2 and store a reference to your ViewController1
e.g. in ViewController2.h
#import "ViewController1.h" // or use #class ViewController1 directive
#property (strong,nonatomic) ViewController1 *mainVC;
Then before in ViewController 1, before you present ViewController2 instance
vc2.mainVC=self;
Your invocation in ViewController2 then becomes
[self.mainVC callWebService];
At the risk of confusing you further, as a design note, it probably isn't best to have the callWebService method in a view controller. It might be more appropriate to create a singleton class for this purpose.
First of all, don't use view controllers for this purpose, create a new class to handle methods of the same kind, then use that one across your view controllers. IF you want the SAME class to be shared across your program, then create a singleton.
How to call method from one class in another (iOS)
However, if you still want to do the view controller to view controller thing, the reason its not working is because you are instantiating a new view controller, not the one you were already using.
You have to pass the reference of the first VC to the second VC. It depends on how you are presenting the second VC. If you are using the Interface Builder, then you need to use:
How to pass prepareForSegue: an object
If you are manually creating and presenting the VC, before presenting it let it know which is the first VC.
You can use delegates like this:
How do I set up a simple delegate to communicate between two view controllers?
STILL consider redesigning your usage of the view controllers.
EDIT:
2 options,
1) Singleton:
Follow this guide http://www.galloway.me.uk/tutorials/singleton-classes/
2) AppDelegate:
Instantiate an object of the class in the .m of the app delegate and assign it to a property in the .h of the App Delegate.
Then, retrieve this object.
This is an example of doing it with the motion manager from ios:
AppDelegate.h:
#property (strong,nonatomic) CMMotionManager *motionManager;
AppDelegate.m
_motionManager = [[CMMotionManager alloc] init];
ViewController1-2-etc
CMMotionManager *motionManager;
motionManager = ((AppDelegate*)[UIApplication sharedApplication].delegate).motionManager;
If you want to use methods from outside your class, you should declare them in your ViewController1.h file, not in the m, otherwise they are not visible (you could still call them using performSelector:withObject:afterDelay: but you should use the first solution)
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.
I have the problem that many already have reported, didSelectViewController doesn't get called, but in my case it sometimes gets called. I have three tabs and three view controllers. Every time user presses second or third tab I need to execute some code. In my SecondViewController and ThirdViewController I have:
UITabBarController *tabBarController = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController;
[tabBarController setDelegate:self];
Now everything works fine with the SecondViewController, the didSelectViewController gets called every time the second tab is pressed. Also in ThirdViewController didSelectViewControllergets called every time the third tab is pressed but only when second bar is meanwhile not pressed. So when I switch back and forth between FirstViewController and ThirdViewController everything is OK. But when I go in a pattern like first->second->third, then didSelectViewController doesn't get called in ThirdViewController. Also when I go like first->third->second->third didSelectViewController gets called in ThirdViewController the first time but not the second time. Any ideas?
It's hard to follow what exactly you are doing, but from what I understand you are responding to tab switches by changing the UITabBarController's delegate back and forth between SecondViewController and ThirdViewController.
If that is true, I would advise against doing this. Instead I would suggest you try the following:
Assign a delegate that never changes. For a start you could use your app delegate, but it would probably be better if you had a dedicated small class for this. I am sure that now you have a non-changing delegate, it will get 100% of all the calls to tabBarController: didSelectViewController:.
The object that is the delegate must have a reference to both the SecondViewController and ThirdViewController instances. If you are designing your UI with Interface Builder, you might do this by adding two IBOutlets to the delegate class and connecting the appropriate instances to the outlets.
Now when the delegate receives tabBarController: didSelectViewController: it can simply forward the notification to either SecondViewController or ThirdViewController, depending on which of the tabs was selected.
A basic code example:
// TabBarControllerDelegate.h file
#interface TabBarControllerDelegate : NSObject <UITabBarControllerDelegate>
{
}
#property(nonatomic, retain) IBOutlet SecondViewController* secondViewController;
#property(nonatomic, retain) IBOutlet ThirdViewController* thirdViewController;
// TabBarControllerDelegate.m file
- (void) tabBarController:(UITabBarController*)tabBarController didSelectViewController:(UIViewController*)viewController
{
if (viewController == self.secondViewController)
[self.secondViewController doSomething];
else if (viewController == self.thirdViewController)
[self.thirdViewController doSomethingElse];
}
EDIT
Some hints on how to integrate the example code from above into your project:
Add an instance of TabBarControllerDelegate to the .xib file that also contains the TabBarController
Connect the delegate outlet of TabBarController' to the TabBarControllerDelegate instance
Connect the secondViewController outlet of TabBarControllerDelegate to the SecondViewController instance
Connect the thirdViewController outlet of TabBarControllerDelegate to the ThirdViewController instance
Add a method - (void) doSomething to SecondViewController
Add a method - (void) doSomethingElse to ThirdViewController
Make sure that you don't have any code left in SecondViewController and ThirdViewController changes the TabBarController delegate!
Once you are all set and everything is working fine, you will probably want to cleanup a bit:
Change the names of the notification methods doSomething and doSomethingElse to something more sensible
If you followed the discussion in the comments, maybe you also want to get rid of the secondViewController and thirdViewController outlets
I too had this problem and got fed up with it. I decided to subclass UITabBarController and override the following methods. The reason I did both was for some reason on application launch setSelectedViewController: wasn't being called.
- (void)setSelectedIndex:(NSUInteger)selectedIndex
{
[super setSelectedIndex:selectedIndex];
// my code
}
- (void)setSelectedViewController:(UIViewController *)selectedViewController
{
[super setSelectedViewController:selectedViewController];
// my code
}
I just dug through this tutorial on storyboards, and I thought of an alternative to using UITabBarControllerDelegate. If you want to stick to UITabBarControllerDelegate then feel free to ignore this answer.
First, create a subclass of UITabBarController, let's call it MyTabBarController. In the storyboard editor you need to change the "Class" property of the tab bar controller so that the storyboard picks up your new class.
Add this code to MyTabBarController.m
- (void) prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SecondVC"])
{
SecondViewController* secondViewController = (SecondViewController*)segue.destinationViewController;
[secondViewController doSomething];
}
else if ([segue.identifier isEqualToString:#"ThirdVC"])
{
ThirdViewController* thirdViewController = (ThirdViewController*)segue.destinationViewController;
[thirdViewController doSomethingElse];
}
}
In the storyboard editor, you can now select the two segues that connect to SecondViewController and ThirdViewController and change the segue identifier to "SecondVC" and "ThirdVC", respectively.
If I am not mistaken, that's all you need to do.
In my main view controller, I have a button that calls a popover. Since the popover has it's own view controller, its buttons call methods in the popover's view controller. But how would I call a method from the main view controller?
I tried this. In the popover view controller I added a property in the .h
#class ViewController;
#interface PopoverContent : UIViewController <UITextFieldDelegate>
...
#property (strong, nonatomic) ViewController *parentView;
In my popover implementation I did this in viewDidLoad:
self.parentView = [[ViewController alloc] initWithNibName:nil bundle:nil];
In ViewController I have a method called generateHash so I tried
[parentView generateHash];
But I get the error:
No visible #interface for 'ViewController' declares selector 'generateHash'
Any idea what I'm doing wrong? Thanks
remove this line, don't want to create a new instance of view controller
self.parentView = [[ViewController alloc] initWithNibName:nil bundle:nil];
change this line
#property (strong, nonatomic) ViewController *parentView;
to this, so your parent view pointer is of the right class type
#property (weak) ParentView *parentView;
now inside parent views .m file
- (void) createPopup
{
PopoverView *popoverV = [[PopoverView alloc] init];
popoverV.parentView = self;
//And some command to show your popup, addSubview, or presentModal, or whatever
}
Then in PopoverView.m file, you can call methods of the parentView like so
[self.parentView SomeMethod];
This is a good place to use a delegate protocol. In the PopoverContent.h, add something like this:
#protocol PopoverContentDelegate : NSObject
- (void) method1;
#end
Naturally, you can have more than one method, and the method(s) can return values and take parameters like any other method. Also, in the same file, add property called delegate. (Technically, it can be called anything, but everyone who looks at your code will know exactly what you're doing if you call it delegate.)
#interface PopoverContent
#property (weak) id<PopoverContentDelegate > delegate;
//other properties and methods
#end
Finally, in your "main" view controller's .m file, import PopoverContent.h file and set the delegate to self. Also implement method1 to do whatever you need it to do.
//Create the view controller
myPopoverContentController.delegate = self;
//Create the popover with the view controller.
Now, in PopoverContent controller, you can call method1 on the delegate wherever you need to.
[delegate method1];
First, to answer your question, you probably have to define the generateHash method in your ViewController.h file.
Second, I'd suggest that your design approach is not optimal. The generateHash method probably needs to be in another file that can be called from both your ViewController and Popover content controller. For example consider another objective-c .h/.m pair "MyHashMethods":
MyHashMethods.h
+ (void)generateHash;
MyHashMethods.m
+ (void) generateHash
{
// hash code
}
This would allow you to just include MyHashMethods.h in whatever view controllers you need and then call
[MyHashMethods generateHash];
when you need it.