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
Related
I have just inherited a code base and the project seems to be made up mainly of UIViews.
Here is an example of a header file for a UIView which was written on 29/07/2014, so the code is relatively new:
#import <UIKit/UIKit.h>
#interface SettingsView : UIView {
UIView *aView;
UIViewController *controller;
}
#property(nonatomic,strong)UIView * aView;
#property(nonatomic,strong)UIViewController * controller;
#end
You can see that there is a pointer to a UIViewController. In the implementation file, the mainView is used in a few places. Here is an example:
MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(0, yPos, scroller.frame.size.width, 50)];
myView = 102;
myView = mainView; // Here we are setting another view with this controller.
[scroller myView];
To me this code doesn't feel right. However I would like to know if there is a good reason someone would have a pointer from a UIView to a UIViewController. I have seen my fair share of iOS code over the years and this is the first time I have seen this.
Any insights would be much appreciated.
I also use my views like this. I use a variable called "sender" with which UIView keeps track of the UIVC that called it. Then I could do all sorts of logical operations, know the size of the parent, add the view as a subview without explicitly stating it.
Is it the best way? No, it breaks MVC, it doesn't clearly separate UI with functions but it is also a lot more convenient and easier to code with.
You can decide to add buttons or actions in your UIView which will affect its senderVC.
func openWebViewController(#URL: NSURL) {
var webViewController = SVModalWebViewController(URL: URL)
senderVC.presentViewController(webViewController, animated: true, completion: nil)
}
Here is a small example of my style of use of pointers to UIVC, sorry its swift but it should be understandable.
https://github.com/goktugyil/CozyLoadingActivity
There is no harm to have UIViewController reference inside a UIView, unless its creating a strong retain cycle, where ViewController pointing to View with strong reference and view is pointing back to ViewController with strong reference as well, this is not specific to viewController and view but it could happen with any two objects, and in this case it both objects will not get dealloc and its a memory leak.
Other then this there is no problem, in your case its keeping reference to viewController because it is using viewController's view as subView and which is fine.
But best practice is that keep your business logic away from view and avoid tight coupling, in an MVC environment view should not have any business logic, controller should handle that, I would say above code is not very good way to work and is not following MVC properly and is tightly coupled.
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.
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];
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;
}
I am probably overcomplicating this but is there a way to access a property of a modal's parent?
So, I call "presentModalViewController" and then I can access some properties on the view controller that just called it from the modal.
Thanks,
Ashley
if you are using iOS 5, you can call self.presentingViewController to access the parent view controller
here is apple reference
Your answer was nearly correct - you want presentingViewController rather than parentViewController, however this could lead to tight coupling (dependency between the two classes, meaning they can only work with each other) if you're not careful.
It's best to define a protocol still, but your delegate property is not necessary - it will have the same value as the presentingViewController property.
Ok, so since asking this question I have tried a few techniques which looked as though they would work but didn't.
Such an example is this.
((pController *)self.parentViewController).testString;
However, while it was a regular UIViewController that presented the modal, the parent was actually a UITabBarController, and even using selectedViewController didn't work.
My solution was to add to my modal's .h file
id delegate;
and
#property (nonatomic, assign) id delegate;
After synthesizing it in the implementation file, alloc/init the modalViewController, but just before presenting it I set
modalViewController.delegate = self;
This way I could call self.delegate from within my modal. This still wasn't enough as this doesn't say which view controller it is, so I can't say
self.delegate.testString;
But the casting I learnt earlier allowed me to get a fully working solution of
((pController *)self.delegate).testString;
I hope I haven't just babbled my way through this, and I hope that this can help someone in the future.