How to push to one view controller object from multiple view controllers - ios

So I have a view controller FavoritesViewController and I have an instance of that view controller:
FavoritesViewController *FVC=[[FavoritesViewController alloc]init];
If I have two other view controllers, HomeViewController and SettingsViewController how do I have it so that I can push to that one particular instance "FVC" from both view controllers. I guess the real question is how/where can I initialize that instance "FVC" so that it is recognized by both view controllers and don't initialize it in HomeViewController or SettingsViewController.
Thanks

When you look at the MVC pattern, the object that you should really care about sharing is the model, not the controller. Your questions suggests to me that maybe the ViewController is also performing the responsibility of being the model.
If this is true, you might want to create another class (called Favorites, perhaps) and follow the suggestions in some of the previous answers to make it a singleton, if necessary. Doing it this way, rather than making the VC a singleton also has the benefit of working the same way whether you are using storyboards, xib, or code.
Maybe you have a good reason to need to share the VC itself, but I thought it would be worthwhile to question that premise.

If you want a single instance of your favorites view controller that is shared throughout your app, make it a singleton. Do a Google search on the singleton design pattern in iOS. The idea is that you would add a class method sharedFavoritesController that would always return the same instance, and use that.
The class method would look something like this:
+(FavoritesViewController *) sharedFavoritesVC;
{
static FavoritesViewController *_sharedFavoritesVC;
if (! _sharedFavoritesVC)
_sharedFavoritesVC = [[FavoritesViewController alloc] init;
return _sharedFavoritesVC;
}
Then #import the header for your FavoritesViewController class, and any time you need to invoke it, use:
[FavoritesViewController sharedFavoritesVC] to get a pointer to it.

Way 1 :
Declare
extern FavoritesViewController *FVC;
Way 2 :
If this is your rootViewController then in other viewcontrollers
FavoritesViewController *fvc=(FavoritesViewController*)appDelegateObj.rootViewController;
Way 3 :
use singleton
+(FavoritesViewController *) sharedInstance;
{
static FavoritesViewController *SVC;
if (! SVC)
SVC = [[FavoritesViewController alloc] init;
return SVC;
}

You could wrap a singleton pattern around that view controller.
Or, less elegant but more common, instantiate it in you AppDelegate and fetch it from there.
Be aware that the same instance of a view controller can only be once in the stack of view controllers. Plus - in principle - there is nothing wrong with having multiple instances of the same view controller class.
Edit in reply to your comment:
Not that I recommend that but this is how you would fetch a property sharedFavoritesViewController (which may refer to an instance of your FavoritesViewController) when your app delegate class is named MyAppDelegate:
FavoritesViewController localVar = [(MyAppDelegate)[[UIApplication sharedApplication] delegate] sharedFavoritesViewController];

Related

How to change the variable of all tabs of the tabbarcontroller from first tab?

I have #property NSString *memberid; in tabbarcontrller.
I will use it in all of it's viewcontorllers.
I want to update the memberid by one of it's viewcontroller. so that other view controller can use the new value.
How can I update the NSString?
Do not listen to the others here, they're just feeding you bad practice. No offense, but hardcoding stuff is just not how it works. It's not how any of this works.
A simple and easy solution could be to add a class, some kind of "Manager" or "Service". You could call it MemberService. This class' job is to make all the work related to your member. I don't know your app but for what you're asking, this assumption is enough. Feel free to tweak this of course.
This class could be a singleton, depending on how that Memberworks. If you have more than one you could have a list of members inside that service class, or simply a property holding the values you need.
Here, I'll go for the simplest possible, assuming one member, so you can simply have a (constant?) field holding the member ID.
Now that service just needs to be available in your controllers, where you'll ask "Hey service, what's the ID number?" and he'll reply no matter where you ask from. With that architecture, you can ask 5 times, from 20 different tabs, it'll always work. No need to mix navigation controllers and whatnot.
You UI should not be responsible for managing your logic. Controllers deal with visuals, animations, etc. All the rest should be moved in logic classes (viewmodels, services, whatever).
Now, having the same property (even a singleton) available in multiple different controllers can be a little annoying, you still have to write down the property each time. A cleaner solution than the above is subclassing your UIViewControllers.
Simple as that : create a ... BaseViewController (you can find a sexier name than that) that inherits : UIViewController. Add a property, your singleton service, MemberService, and make sure to instantiate/prepare it in the viewDidLoad of BaseViewController ; maybe you can set that member ID and other stuff there, that's your choice.
Now, all your viewcontrollers used in tabs, change their superclass (on top of the file). Instead of : UIViewController, use your BaseViewController. Tadaa, they all always have that MemberService available, and you'll 100% sure no matter how many tabs, that you have a service available, and that it is loaded and ready to kick ass (because it's been prepared in the viewdidLoad).
I am doing something like this in one of my project,
NSArray *tabArr = [self.tabBarController viewControllers];
UINavigationController *navController1 = [tabArr objectAtIndex:1];
DraftViewController *dvc = [navController1.viewControllers firstObject];
dvc.screenTitle = self.screenTitle;
UINavigationController *navController3 = [tabArr objectAtIndex:3];
SentViewController *svc = [navController3.viewControllers firstObject];
svc.screenTitle = self.screenTitle;
UINavigationController *navController2 = [tabArr objectAtIndex:2];
OutboxViewController *ovc = [navController2.viewControllers firstObject];
ovc.screenTitle = self.screenTitle;
I am doing that in my first tab (first viewcontroller of tabbarcontroller)
I have embed navigationcontroller to every viewcontroller (tabs). So, i get first nav controller and then get VC(tab).
If you don't have nav controller embed to your tab VC then [tabArr objectAtIndex:index] returns view controller directly.
// In AppDelegate decalre one variable for tabcontroller , assign your tabcontroller to this variable, you can tabcontroller in use entire app. example code is here
Appdelegate *appdelegate = [[UIApplication sharedApplication] delegate];
appdelegate.tabController.memberid = #"1";

Accessing ViewController from it's parent class

I have a lot of viewControllers that have a common parent class
#interface X_ViewController : ParentViewController
{
}
in ParentViewController I want to run some code that does the actual things on the ViewController itself
Specifically UIActionSheet.
Inside ParentViewController, self.view is the wrong thing to do.
How can I still access the "child" (which is actually my own object's) view?
Well, I believe this is not possible.
You can access childViewControllerin different cases, but not this one.
Your approach, IMO, is wrong.
You should make some public methods in your parentVC and let its child calls them. Not the other way around.
You can cast self to X_ViewController.
if ([self isKindOfClass:[X_ViewController class]]) {
[((X_ViewController *)self) doSomething];
}
But I would discourage this. You use subclasses so that you get inheritance, not the other way round (pension?).
import your child VC in your parent.(very unethical i might say)
create reference of your child VC in your parent and then call the method u want to execute

Two UIviewControllers inside UIScrollview - how to call methods between them

I have problem with calling methods from one UIViewController by another UIViewController.
Currently I have UIScroll view with two UIViewControllers.
I want to change something in second one and see results in first one.
I try to do this in this way:
Inside function of second UIViewController:
-(void)doSomething:(){
FirsOneViewController *firstVC = [FirsOneViewController alloc] init];
[firstVC changeUnits:0];
}
Function is called but I don't se any changes in first controller.
BR,
Paul
From your code I see you create a new instance of FirstViewController and so there is no reason why the current instance inside the scrollview would receive this message.
You need to send the changeUnits: message to the current FirstViewController, so you need a reference to it. To do this you may want to think about creating a protocol, so that you parent container (the scrollview) is notified by the SecondViewController and then notifies the FirstViewController. A simpler(and lazier) solution is make the SecondViewController have a strong reference to the FirstViewController (though this solution may bite you in the future).
As said in other answers you are creating a new instance of FirsOneViewController instead of referencing to the one you already have.
Here are three ways of doing what you are asking:
Delegation:
The FirstViewController should be the delegate of the SecondViewController (as the secondViewController is calling methods on the FirstViewController). You should tell the SecondViewController that the FirstViewController is its delegate in what ever class initialises the two viewControllers.
From what you have said so far this seems like your best option.
NSNotification:
This could be good option if you think more than one object will want to listen to the change in the SecondViewController. Just post an NSNotification in the SecondViewController and add an NSNotification listener in the FirstViewController
Singleton:
if there should only ever be one instance of the FirstViewController in existence then make it a singleton. By making a class initialiser method. so that you can create/get the current instance of the object from anywhere in your appellation.
Hope this helped.

How do I pass the managedObjectContext in the AppDelegate to the right view controller if it's a child view controller?

I have a child view controller that requires the managedObjectContext be set on it in order to operate properly, and while I considered using a singleton this question seems to point strongly to that it should be passed around, starting with the AppDelegate.
I don't know how to pass it to a child view controller, however (note that it's set up via Storyboards). I tried a few ways:
In prepareForSegue, before the embed segue is called, I save a reference to the destination view controller in the containing view controller as a property, then try to access this property, but in didFinishLaunching the segue obviously hasn't been set up yet.
Accessing the childViewControllers property of the containing view controller, but again, at didFinishLaunching this is not populated.
So despite the Core Data template and examples seeming to indicate that the managedObjectContext of other view controllers should be set in didFinishLaunching, I see no way to.
How would I properly pass the managedObjectContext onto the view controllers that need it?
Add an NSManagedObjectContext property to the public API of your root controller, then set it to the managed object of your app delegate in didFinishLaunching. You can then use the context in your root view controller to pass it along to subsequent view controllers. In didFinishLaunching:
MyRootViewController *controller = (MyRootViewController *)self.window.rootViewController;
controller.context = self.managedObjectContext;
You now have a managedObjectContext in your root view controller that can be passed in the prepareForSegue method to the child view controller (the child view controller must have a public managedObjectContext property as well that you can access in the prepareForSegue method).
The debate as to whether to have a single, globally accessible object, either as a property on AppDelegate or as a statically accessible Singleton, is largely a religious debate. There is no right or wrong answer and there are pros and cons either way. Do what is simplest and most natural to you.
Typically, managedObjectContext is a de facto singleton: there is only one for the application and that one is used everywhere when needed. Your inclination to use a singleton or always access it from the AppDelegate maybe the way to go. Otherwise, and to answer you question, set it as a property on the view controller you are transitioning to before pushing the view controller or in prepareForSegue as you describe. To start the process, the root view controller could either instantiate it or access it from the AppDelegate in it's init, loadView, or viewDidLoad methods and keep a reference to it.
For me the decision comes down to which approach is more obvious months or years later when I read the code and which approach do I want to maintain.
If the NSManagedObjectContext was setup in AppDelegate, you don't pass it; rather, you create a reference to it:
AppDelegate *appDel = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDel.managedObjectContext;
The type of object you reference it from is irrelevant.

prepareForSegue **always** creates a new destinationViewController?

I Just realized that the following code always creates a new TagsFeedViewController. Is this the default behavior of segues? Is there a way to configure iOS to not create a new destinationViewController every time?
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showSearchResult"]) {
TagsFeedViewController *destViewController = segue.destinationViewController;
destViewController.query = query;
}
}
Segues use whichever view controllers are provided to their – initWithIdentifier:source:destination: methods. It's not the segue that creates the destination view controller, but the storyboard. From the docs:
Normally, view controllers in a storyboard are instantiated and
created automatically in response to actions defined within the
storyboard itself.
So you have some options:
Subclass UIStoryboard. Probably a bad idea. The public interface for UIStoryboard has only three methods; the "actions defined within the storyboard itself" aren't public, and I don't think there's enough information available to let you do the job right.
Make your destination view controller a singleton. Also a bad idea. Aside from the general badness that singletons bring with them, you shouldn't need to keep a view controller that has no views and no child view controllers around. And making your view controller class a singleton just to fool UIStoryboard into using a particular instance of your view controller class seems kinda icky.
Subclass UIStoryboardSegue. If you create your own segues, you can do what you like in – initWithIdentifier:source:destination:, including ignoring the provided destination view controller and using the one you want instead. This still seems like working against the framework, and that's usually a poor plan, but if you absolutely must use a particular instance of your destination view controller this seems like a better way to go.
Go with the flow. Best option. Think about the reason that you're hoping to segue to an existing view controller. Consider whether there might be better ways to accomplish what you want without having to subvert the framework. For example, do you want to use an existing view controller because it already has some particular state? Maybe it'd be better to maintain that state in your model and not in the view controller.
Yes, This is the default behavior for segues. See this post for more information.
You can prevent the creation of the controller by handling the shouldPerformSegueWithIdentifier:sender: message.
-(BOOL) shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
if ([identifier isEqualToString:#"showSearchResult"]) {
return [self.results count] > 0;
}
return YES;
}

Resources