I have a method inside my viewController (MainViewController.m) that accepts a parameter (Let's say it changes the background color of the viewController based on the number):
-(void) methodThatDoesSomething:(int)indexNumber {}
Inside this viewController I have a UITableView, but the delegate of this table is another class (TableDelegateClass.m). So inside this class I have didSelectRowAtIndexPath.
How do I call the "methodThatDoesSomething" from didSelectRowAtIndexPath? If I do it like this:
MainViewController* mainView = [[MainViewController alloc] init];
[mainView methodThatDoesSomething:indexPath.row];
It doesn't work. As another instance of the class is created and it doesn't after the viewController that I'm currently using.
I can easily do it if the delegate of the table is the MainViewController (just with this code inside didSelectRowAtIndexPath) :
[self methodThatDoesSomething:indexPath.row]
but I want to organize better the code and have different classes, one for the table and another for the viewController containing the table.
One simple solution could be to define your own delegate protocol for -(void) methodThatDoesSomething:(int)indexNumber {} so you can set TableDelegateClass delegate to your viewController and then call [delegate methodThatDoesSomething:indexPath.row]; (after checking respondsToSelector).
You can also pass the viewController instance when you create TableDelegateClass but this is a higher coupling level than delegate.
One way to solve your problem is to create a property of type MainViewController in the delegate class and assign the controller instance to it.
But maybe this problem that you are having is an indicator that the tableView delegate should indeed be the MainViewController and not another class?
Related
I have a button in mainviewcontroller, when that button is tapped I want to call a method on another view controller. Here below is an image showing my scenario, Green button click to call preview controller VC1 received method without navigation anything. I just need to call that method only!
Declare a method in .h file of VC1 and implement in .m file of VC1
Call that method from MainView Controller ,
Create a NSNotificationObserver in MainView Controller and implement it in .m file of MainView Controller ,
Now when method of VC1 is called and before it returns, fire a PostNotification from VC1 and pass the parameter which you want in MainView Controller.
#RobAu has given correct answer as you can use NSNotificationObserver and call from anywhere.
Other alternate which i have used is using protocol and is very simple.
Just declare a protocol in the child controller. while launching the view put the parent controller as delegate. now call the delegate method from where ever you want from child controller
Here is example which i used (In my case all the child controller was of same kind)
//ChildClass.h file of child class
#protocol updateIndex <NSObject>
-(void)updateMediaId:(NSString*)currentMediaId;
#end
#interface ChildClass : UIViewController
#property NSString *imageID;
#property id updateIndexDelegate;
#end
//ChildClass.m file for child class where you want to call the delegate method
(I called in viewDidAppear method)
-(void)viewDidAppear:(BOOL)animated{
[self.updateIndexDelegate updateMediaId:_imageID];
}
And in ParentClass.m file of parent class use the delegate to self like this
ChildClass *childObject = [[ChildClass alloc] init];
childObject.delegate = self;
and define the delegate method like this
-(void)updateMediaId:(NSString*)currentMediaId {
NSLog(#"%#",currentMediaId);
}
Enjoy coding
These are my delegate methods in Child view controller.
#protocol assignNames<NSObject>
-(void)setFirstName:(NSString*)firstName;
-(void)setLastName:(NSString*)lastName;
#end`
In my parent view controller, I receive the first name in a text field.
Here is a part of my code .
VIEWCONTROLLER.M
\\ FirstName is the name of my textField string
viewcontroller2 *viewc = [[viewcontroller2 alloc]init];
viewc2 = [segue destinationViewController];
viewc2.FNT = FirstName.text;
[viewc2 setDelegate:self ]; \\ Here is my question
So, when I give this method, what it actually means?? as nothing seems to happen if i include it in my code .
The #protocol block in your childviewController is a list of methods that a delegate of the childviewController should implement. When you call [viewc2 setDelegate:self ] from viewController.m, your viewController becomes a delegate of your childViewController.
So what does all these steps do? Well, firstly, because the parent VC subscribes to the assignNames delegate, it will have those two methods implemented. Secondly, the Child VC can call the protocol's methods in the parent VC like so:
[self.delegate setFirstName:#"Derp"];
// we are in the child VC and the delegate would be parent VC
Anyways, there was a time when all this baffled me, so I know how it is. I suggest you go through some tutorials to get a hang of the stuff, like this one here.
I would like to use a method in a viewcontroller that is the "container" of an other viewcontroller but not directly the parent. I display a popover containing a custom xib file. For that I have three viewcontrollers:
1) popoverVC managing the settings of the popover (like alpha value, any arrow, size, and the method I want to use from "outside" dismisspopover)
2) customVC with a xib file to change the content of my popover (some text and some buttons).
3) mainVC from where the popover is launched and where I receive some actions when interacting with my buttons in customVC. mainVC is the delegate of customVC.
My problem is I don't know of to call dismisspopover method in popoverVC from an action in customVC. At the very beginning I initialize my customVC like this from mainVC:
mainVC.m
customVC *cvc = [[customVC alloc] init];
[cvc setDelegate:self];
PopoverController *popover = [[PopoverController alloc] initWithViewController:cvc];
[popover presentPopoverFromView:textField];
I tried to instantiate a new instance of popoverVC inside my action method in customVC to be able to call the method [popoverdismiss]. But this should not be working since I will be targeting a different instance from the one I started with... And I would like to be able to target popoverVC like [self.presentingVC] but I am not sure the method initWithVC sets a hierarchy like this.
Does anyone has an idea?
The way I usually go about having to circumvent the default view controller hierarchy is to create a property within your child view controller (I think customVC) called parent or something to that effect that's of type mainVC. Then either create a new initializer for your customVC that incorporates a field to assign to parent or just assign to it after you first initialize your customVC. Then whenever your customVC needs to ask the mainVC to perform a function, you can make a method call from parent.
Alternatively, you could just as easily make parent correspond to your popoverVC object, I'm not totally sure which object you need to access, but it should be as simple as creating your own property for it.
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 want to send a UITableViewCell at indexPath.row from one controller to another. I can remove the row using removeObjectAtIndex, but unable to send the removed row into another controller.
I'm trying to store the removed row in an NSMutableArray in one controller, but don't know how to populate it in another controller.
Below is the code -
ViewController *view= [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
view.anotherviewArray= [self.arrayFromAFNetworking objectAtIndex:indexPath.row];
If anyone can give me an idea, it would be helpful.
I believe it's bad practice to retain UI elements and pass them around your app. You should instead have some kind of a model containing your data, and pass this model from one view controller to the other. I'd recommend checking out tableview frameworks such as the free Sensible TableView framework, as they do an excellent job of providing such a model for you automatically.
I personally think that it's wrong approach to pass UI object as parameter to another controller.
As I would do it is create some object that encapsulates data model from this cell and pass this object to another view controller.
#interface DataObject : NSObject
#property id field1;
#end
UI part of cell can be easily copied in Interface Builder, so I don't see problem in that. Probably it would be great to have cell class that could fill necessary field from the object with data. This class you can use in both view controller that have to show the same cell
#interface CustomTableViewCell : UITableViewCell
- (void)customizeCellWithDataObject:(DataObject *)dataObject;
#end
I hope it makes sense to you
Assuming that you DO want to set the other data source with only this row, you need to pass it as an array.
view.anotherviewArray= [NSArray arrayWithObject:[self.arrayFromAFNetworking objectAtIndex:indexPath.row]];
But it's hard to tell from the little code that you have provided. I assume that since you are instantiating the viewController you are also transitioning to it below the provided code. If you are trying to set the array for a viewController already presented, you need to access that one, not create another, perhaps by having saved a reference to it an ivar within the current viewController or another accessible class.
I would also not name a ViewController view, it is confusing to anyone reading the code later on.
Editing for my comment below about traversing the hierarchy. Here is some code that I used in one iPad project to return the final presented viewController. This method is in the appDelegate. It is somewhat specific to my project, where there is only one navigationController. But you can adapt it to yours. You would test for a viewController that is of the class of your target view controller.
- (UIViewController *)topViewController {
UIViewController *rootViewController = self.window.rootViewController;
UIViewController *topViewController = rootViewController.presentedViewController;
while (topViewController && topViewController.presentedViewController) {
topViewController = topViewController.presentedViewController;
if ([topViewController isKindOfClass:[UINavigationController class]]) {
UIViewController *presentedViewController = [(UINavigationController *) topViewController topViewController];
if (presentedViewController) {
topViewController = presentedViewController;
}
}
}
return topViewController;
}
The other approach is to set a property to it when it is created and presented. We don't have enough code to get a good idea of your app as a whole. Where are you creating the ViewController instance that you are displaying? By that I mean where you are calling a segue to it, or pushing it onto a navigationController or call presentViewController. Wherever that is, you need to set a property or ivar to it. Let's say that you use a property in your appDelegate as a very generic case.
In your MyAppDelegate.h file you have
#property(nonatomic,strong) ViewController *viewController;
Wherever you first create it you set that property
MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
I now think you are trying to add this to a mutableArray in the other ViewController. Then replacing your code from the tableViewCell above you would use
MyAppDelegate appDelegate = (MyAppDelegate)[[UIApplication sharedApplication] delegate];
[appDelegate.viewController.mutableDataArray addObject:self.arrayFromAFNetworking objectAtIndex:indexPath.row];
[appDelegate.viewController.tableView reloadData];
I will say that it is not great practice to use the appDelegate for the property. But it is a generic answer that would work. It's best to put it in a class which is common to the viewControllers that you are passing data between. Perhaps a single parent which holds these two viewControllers?