In my app there are two tabbars. In tab-0 parent class is "FirstViewController" and in tab-1 parent class is "SecondViewController". In "SecondViewController" i have declared protocol and custom delegate method. i want to pass the information in "FirstViewController"(FVC). So FVC has to assigned as a delegate.
Now my doubt is, right now i am in "SVC". How can i assign "FVC" as a delegate of "SVC"?
In "SVC"
[[self delegate] sendCoordinates:self];
Definition of method is in "FVC". To execute this method, first i need to assign "FVC" as a delegate.
I hope I am clear in explaining my problem.
Thanks in advance.
You need to set the delegate. Let me demonstrate:
`SVC.h`
#interface SVC
{
id _delegate;
}
- (void)setDelegate:(id)delegate; //sets the delegate
- (id)delegate; //gets the delegate
#end
#protocol SVCDelegate
- (void)sendCoordinates:(SVC *)svc;
#end
Then, in SVC.m, you call the delegate in exactly the same way as you showed in your question, so [[self delegate] sendCoordinates:self];
Now, in FVC, you'll need to #import SVC.h and initialise the object.
SVC*svcObject = [[SVC alloc] init];
[svcObject setDelegate:self]; //self now refers to FVC - I think you're missing this one
//work with the object and when you're done, get rid of it in dealloc:
[svcObject setDelegate:nil];
[svcObject release];
In the same class, implement - (void)sendCoordinates:(SVC *)svc and it will be called after you set the delegate.
I think you're missing the setDelegate: stage, which is why it doesn't work.
Hope it helps!
Note: In SVC, remember to retain the delegate, or it will become nil and no delegate methods will never be called. Don't forget to release that delegate once you're done.
Related
I was trying to push a viewcontroller B into navigation controller from A and then assigning some properties of B in A.
In this case, the assigning of properties was done and then viewDidLoad of viewcontroller A was executed.
Here, assigning properties in A should be done only after viewDidLoad of A has done.
For example,
[b.navController pushViewController:a animated:YES];
a.status = #"loaded";
Here, status was assigned first and then viewDidLoad of A was executed.
This happens only in iOS 7 whereas in iOS6 it works fine.
Can anyone please let me know where the problem is?
UPDATE: For me in some cases in iOS7, Push view is not working. How cna I debug and fix it?
Just access the viewcontroller.view (set any thing immediately after the alloc) property after the alloc init;
Which will loadview/viewdidload.
See Apple Documentation
In my experience, a UIViewController view is loaded lazily, no matter which iOS version you're working on. If you want to trigger a view load, and therefore its UIViewController viewDidLoad, you should access the view after the UIViewController is allocated. For example:
UIViewController *aViewController = [[CustomViewController alloc] init];
[aViewController view];
Make sure you don't code it as
aViewController.view
since that would generate a compiler warning.
So, in your case it would have to be something like this:
...
CustomViewController *a = [[CustomViewController alloc] init];
[b.navController pushViewController:a animated:YES];
[a view];
a.status = #"loaded";
Let me know if you have further problems with it.
You can know when a View Controller has been pushed onto the stack by implementing the UINavigationControllerDelegate and setting yourself as the delegate self.navigationController.delegate = self; then you will get this callback after every push
navigationController:didShowViewController:animated:
So you can check if the shown viewController is the one your interested in, then set your a.status.
I would suggest you call a delegate method once the view is loaded.
Set the delegate to be controller B.
and after viewDidLoad finishes (in controller A) call the delegate method. You can even pass parameters as you wish to the delegate.
Here's some example code:
Controller B:
a.delegate = self;
[b.navigationController pushViewController:a animated:YES];
Implement the delegate method:
- (void)controllerIsLoaded:(ControllerA *)controllerA status:(NSString *)status
{
a.status = status;
}
Controller A .h file:
#class ControllerA;
#protocol ControllerADelegate <NSObject>
- (void)controllerIsLoaded:(ControllerA *)controllerA status:(NSString *)status;
#end
#interface ControllerA : UIViewController
#property (nonatomic, weak) id <ControllerADelegate> delegate;
Controller A .m file:
- (void)viewDidLoad:(BOOL)animated
{
[super viewDidLoad:animated];
/* your viewDidLoad code here... */
if ([_delegate respondsToSelector:#selector(controllerIsLoaded:status)])
[_delegate controllerIsLoaded:self status:#"Loaded"];
}
Turn off animation for ios7, in my case its work
[b.navController pushViewController:a animated:NO];
a.status = #"loaded";
No documentation provides enough information to know exactly when viewDidLoad would be called.
UIViewController's documentation just says this
This method is called after the view controller has loaded its view hierarchy into memory
I would suggest that you create a custom initializer like this
- (id)initWithStatus:(NSString *)status {
}
But, if you are trying to use this variable to check if the viewController's view has 'loaded' or not, it may not be possible to do that because the pushViewController or presentViewController methods are not guaranteed to be synchronous.
Even in iOS 6, there was no explicit guarantee that the view would be loaded as soon as that method returned.
Please write the code in viewWillAppear method instead of viewDidLoad in next class i.e. where you are pushing the object to
-(void)viewWillAppear:(BOOL)animated
{
}
I'm understand of your question like this:
B *b = [[B alloc] init];
b.status = #"loaded";
[self.navigationController pushViewController:b animated:Yes];
If you want to pass a value from one controller to another means, you have to assign a value before using pushViewController method.
First thing i do is create a ViewController and push it to the Navigation Controller.
viewController = [[MyViewController alloc] init];
[navController pushViewController:viewController animated: NO];
[viewController release];
Retain count is 2 now (pushViewController uses 2 retain apparently but not my responsibility) so far so fine.
Inside MyViewController i'm createing a instance of a class and sets the ViewController as delegate to the instance.
timer = [[MyBackgroundTimer alloc] initWithInterval:20];
[timer setDelegate:self];
Now the viewControllers retain count has increased by 1 becouse of setDelegate:
But when i'm releasing the viewController later it will never call dealloc becouse i have one more retain count.
How should you correctly drop the retain count when you set your self as delegate?
Don't retain your delegate. If you're using a property, define your delegate as assign, not retain. Somebody else needs to retain your delegate, not you.
Your class MyBackgroundTimer should have the delegate property as assign and not retain.
#property (nonatomic, assing) id delegate;
And this class should retain the delegate just when it needs to use it, and release when it is done.
#implementation MyBackgroundTimer
#synthesize delegate;
-(void) startTimer {
[self.delegate retain];
//... do some actions
}
-(void) timerStopped {
//... call delegate methods
[self.delegate release]
}
#end
It is important to remember that you can have your delegate as a retain property. But to do so the right way, you have to ensure that you release it before dealloc is called (like the timerStopped method in the example above)
I say that because if you try to release the delegate at the dealloc method, the class that instantiates MyBackgroundTimer is the same class as the delegate, and it also releases MyBackgroundTimer at the dealloc (which is pretty much the common case), the dealloc method of both classes will never be called, as each class will have the ownership of the other, resulting in a memory leak (that will not be shown at instruments).
I've been trying to use a UIButton action to call a method in a different class (AppViewController). I first tried creating an instance of the view controller in the UIButton's calling class (caller.m) and then calling the method, but that kept resulting in EXC_BAD_ACCESS.
I'm realizing I need to point to the same instance of the view controller and am now trying to make sure the view controller instance is properly declared in caller.m.
I have a declaration of AppViewController *viewController in the AppDelegate, so my thought is to refer to that same instance from caller.m.
#import "caller.h"
#import "AppDelegate.h"
#implementation caller
- (id)initWithFrame:(CGRect)frame {
...
[btnSplash addTarget:viewController action:#selector(loadSplashView) forControlEvents:UIControlEventTouchUpInside];
....
}
However, viewController still shows up as undeclared. I tried a few other things, but know I'm probably missing something basic.
::::UPDATE::::
Okay, so it turns out I needed to create the following so the target "viewController" was actually declared and pointing to the correct instance:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
AppViewController* viewController = appDelegate.viewController;
The method in the view controller class is now being properly called.
For a more clearly explained and more general version of this question, go here:
Objective-c basics: Object declared in MyAppDelegate not accessible in another class
There are multiple ways for objects to initiate actions, communicate with other objects and/or observe changes they are interested in including:
UIControl target/action bindings
Protocols
Key/Value Observing (KVO)
Notifications
I don't think notifications are what you want in this case. Notifications are most appropriate when the object posting the notification does not care what object(s) are observing the notification and there can be one or more observers. In the case of a button press you would typically only want a specific object to handle the action.
I would recommend using a protocol. You'll see lots of protocols in use in the iOS frameworks, basically any class that has a delegate property usually defines a protocol that delegate objects need to conform to. The protocol is a contract between the two objects such that the object defining the protocol knows that it can communicate with the object conforming to the protocol with out any other assumptions as to its class or purpose.
Here's an example implementation. Apologies if any typos/omissions.
In caller.h (I assumed caller is a UIViewController):
#class Caller
#protocol CallerDelegate
- (void)userDidSplashFromCaller:(Caller *)caller;
#end
#interface Caller : UIViewController
id <CallerDelegate> delegate;
#end
#property (nonatomic, assign) id <CallerDelegate> delegate;
#end
In caller.m:
#implementation Caller
#synthesize delegate;
- (void)viewDidLoad {
// whatever you need
// you can also define this in IB
[btnSplash addTarget:self forAction:#selector(userTouchedSplashButton)];
}
- (void)dealloc {
self.delegate = nil;
[super dealloc];
}
- (void)userTouchedSplashButton {
if (delegate && [delegate respondsToSelector:#selector(userDidSplashFromCaller:)]) {
[delegate userDidSplashFromCaller:self];
}
}
in otherViewController.m:
// this assumes caller is pushed onto a navigationController
- (void)presentCaller {
Caller *caller = [[Caller alloc] init];
caller.delegate = self;
[self.navigationController pushViewController:caller animated:YES];
[caller release];
}
// protocol message from Caller instance
- (void)userDidSplashFromCaller:(Caller *)caller {
NSLog(#"otherVC:userDidSplashFromCaller:%#", caller);
}
[EDIT: CLARIFICATIONS]
I realized after looking at your question and code again that I made some assumptions that may not be true in your code. You most likely should still use a protocol but the exact way to integrate my example depends on your app. I don't know what class Caller is in your app but whatever it is, it is dealing with UIButtons so it is most likely a view controller or a view.
Your comment about not having the correct instance of your appViewController makes me wonder if you understand the difference between classes and instances of a class. If my answer doesn't help you, please post some more code showing how you create and present your view controller as well as how you are configuring the button and I can try to clarify my answer.
You should post a NSNotification when clicking the button that will be caught and executed in the AppViewController.
So this should be:
In the sender class:
[btnSplash addTarget:self
action:#selector(loadSplashView)
forControlEvents:UIControlEventTouchUpInside];
-(void)loadSplashView:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"notif_name" object:some_sender_object];
}
In the target class:
Register to get the notification at view's load:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(some_function:) name:#"notif_name" object:nil];
Define the action to take in this class:
-(void) some_function:(NSNotification *)notif {
//do something
// to access the object do: [notif object]
}
Communication between various objects of your app is a design level decision. Although iOS provides neat ways of doing this at code-time (properties) - it's through hard-coupling.
True inter-object communication does not bind objects at compile time - this is something that can only be assured by following design patterns.
Observers & Delegates are two most commonly used patterns, and it's worth for you to learn when to use which one - see Observer vs Delegate.
I'm trying to send an array from one viewController to another using protocols and delegates. I declared the delegate on viewcontroller B and used the following code in viewcontroller A to send the message from A to B. The protocol's method is didReceiveMessage. Unfortunately, the message never arrives.
Attached is the code from viewController A
- (IBAction) graphPressed:(UIButton *)sender {
GraphingViewController *gvc=[[GraphingViewController alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:gvc animated:YES];
[gvc release];
[delegate didReceiveMessage:brain.internalExpression];
}
and the code from viewcontrollerB
- (IBAction) ViewdidLoad {
CalculatorViewController *cvc =[[CalculatorViewController alloc] init];
cvc.delegate=self;
[cvc release];
}
- (void) didReceiveMessage:(NSMutableArray *)expression {
NSLog(#"message received from CalculatorAppDelegate");
}
Any suggestions would be greatly appreciated.
I'm not sure what are you doing in second sample? You created an object, assign its delegate property and then released it. Why?
If that is code from your application it could probably be from releasing cvc at the end of your ViewDidLoad method.
init would give it a retain count of 1, and the release would take it back to 0.
Also the code seems sort of backwards, if you wanted to set view A as the delegate for view B, you would do so in view A.
Unless there is a more complex reason to use a delegate that I'm not seeing from the code, I would just keep a pointer around to the child if you really need to send it messages.
Like others have posted, you are just getting rid of the Calculator VC after creating it. I would recommend adding an #property to the second VC and using it to store a pointer to the Calculator. It should be a retain property. Then set the delegate of that property to self.
Also, you make sure to use an assign property for the delegate value, and try to use the property syntax (self.delegate) whenever possible.
There could absolutely be more going on here, so if this doesn't actually solve the problem try and post up more of your code (header files, what connections are made in IB, etc.)
Edit: For the record, the method is -(void)viewDidLoad, not -(void)ViewDidLoad, so that could be contributing to the problem.
As you said you are trying to pass an array from one view controller to another.Well i use in this way.Here is the code
- (IBAction) graphPressed:(UIButton *)sender {
GraphingViewController *gvc=[[GraphingViewController alloc] initWithNibName:nil bundle:nil];
gvc.myArray=infoArray;
[self presentModalViewController:gvc animated:YES];
[gvc release];
}
Where myArray is array in your GraphingViewController,You just need to make property of this array with retain attribute,simply as
#property(nonatomic,retain)NSMutableArray *myArray;
And you need to Synthesize it as well.
I'm trying to figure out how I can call a function from another one of my classes. I'm using a RootViewController to setup one of my views as lets say AnotherViewController
So in my AnotherViewController im going to add in on the .h file
#class RootViewController
And in the .m file im going to import the View
#import "RootViewController.h"
I have a function called:
-(void)toggleView {
//do something }
And then in my AnotherViewController I have a button assigned out as:
-(void)buttonAction {
//}
In the buttonAction I would like to be able to call the function toggleView in my RootViewController.
Can someone clarify on how I do this.
I've tried adding this is my buttonAction:
RootViewController * returnRootObject = [[RootViewController alloc] init];
[returnRootObject toggleView];
But I dont think that's right.
You'll want to create a delegate variable in your AnotherViewController, and when you initialize it from RootViewController, set the instance of RootViewController as AnotherViewController's delegate.
To do this, add an instance variable to AnotherViewController: "id delegate;". Then, add two methods to AnotherViewController:
- (id)delegate {
return delegate;
}
- (void)setDelegate:(id)newDelegate {
delegate = newDelegate;
}
Finally, in RootViewController, wherever AnotherViewController is initialized, do
[anotherViewControllerInstance setDelegate:self];
Then, when you want to execute toggleView, do
[delegate toggleView];
Alternatively, you could make your RootViewController a singleton, but the delegate method is certainly better practice. I also want to note that the method I just told you about was Objective-C 1.0-based. Objective-C 2.0 has some new property things, however when I was learning Obj-C this confused me a lot. I would get 1.0 down pat before looking at properties (this way you'll understand what they do first, they basically just automatically make getters and setters).
I tried out the NSNotificationCentre - Works like a charm - Thanks for your reply. I couldn't get it running but the NS has got it bang on.
[[NSNotificationCenter defaultCenter] postNotificationName:#"switchView" object: nil];