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.
Related
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.
I'm going through some old code and trying to detect some hard to find bugs. I came across an unusual usage of a UIViewController where the controller is allocated, stored in a property, and its view is added as a subview instead of presenting the entire controller.
Let me start off by saying I know this is kind of hacky and abnormal. That said, what are the dangers in the following implementation? Are there any unexpected side effects that this could cause? What if MyOtherViewController unloads its view and recreates it due to receiving a memory warning at some point, could that cause any strange behavior?
#interface MyViewController()
#property (nonatomic, strong) MyOtherViewController *otherVC;
#end
#implementation MyViewController
- (void)viewDidLoad
{
self.otherVC = [[MyOtherViewController alloc] init];
[self.view addSubview:self.otherVC.view];
}
#end
What you are doing is creating custom view controller containers. This is not a bad thing, but you aren't doing it the way you're supposed to. There is a section in UIViewController's class reference that explains exactly how to accomplish what you're trying to do.
Take a look at Displaying a View Controllers Contents Programatically
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/UsingViewControllersinYourApplication/UsingViewControllersinYourApplication.html#//apple_ref/doc/uid/TP40007457-CH6-SW8
Note this: Important: Never install the view controller’s view into a view hierarchy directly.
I just tracked down a nasty BAD EXEC crash in a project I moved to (see nasty bug below).
I can say that using a UIViewController is very bad because:
You have to make sure the controller is not deallocated. The view won't because it is in view hierarchy with a superview, but the controller has no object to retain it. If it was added to the window as a rootController, to a tab controller, a navigation controller or presented by another controller (normal usage) it would be ok.
It won't receive orientation changes and messages that you would expect to get called besides viewDidLoad.
Nasty bugs. For instance in iOS 5 if this controller is not deallocated before you dismiss a modal controller you'll have a BAD EXEC crash that will drive you crazy. It seems the animation methods from the SDK expect your view controller to be present during the dismiss modal animation.
I have a view that I want to reuse in different situations. It is a user view that, when touched, will have the viewcontroller push a user detail viewcontroller.
So basically I have a view that can any number of superviews until the viewcontroller. I want that view to be able to notify whatever viewcontroller that is currently being displayed to push the user detail view.
Is there a way besides using NSNotificationCenter to do this? Is NSNotificationCenter my best option? I've tried to put in a protocol/delegate, but that isn't working out for me.
Thanks!
------------------------Response to a comment----------------
I would like to have it so it is dynamic. That is partially my problem. I will use this view throughout my code and when I make updates/changes, I don't want to have to change the actual user view to make things work
An example would be adding this user view on the following hierarchy: viewcontroller->tableview->tableviewcell->userview. But then I'd also like to add it like this: viewcontroller->userview.
navigationController.topViewController may be helpful in this case. Or if your app is using a single navigation stack, you could handle this notification in the appDelegate
#interface AppDelegate
#property (nonatomic, strong) UINavigationController *nav;
...
[nav pushViewController:userVC animated:YES];
I think it does make sense to use an NSNotification in this case. Per MVC, the UIView handling the touch event should not need to know much about the View Controller hierarchy it lives in. Notifications handle that issue.
I am thinking that I will subclass a UINavigationController and register for my NSNotification there, then i won't have to worry about registering on each UIViewController in my app. I'll leave this answer here for a bit without checking it as the answer to see what kind of side effects this might have.
I've been looking at view controllers for a few days now (searching xcode help, google and stack overflow), and I think I have some understanding of it, but not a lot.
I have some code, that I have trouble understanding.
So here I have the following code I found in the book I'm reading, and I'm not sure If I understand it correctly.
-(void)prepareForSegue(UIStoryboardsegue *)segue sender:(id)sender{
((ViewController2 *)segue.destinationViewController).delegate = self;
}
First, I have no idea why we typecast to our second view controller(viewController2) here.
I think I get the rest though, we take the method's segue parameter (which holds information about the view controllers involved in the segue), and we access the destinationViewController(meaning that we want to access the view controller that we are going to). We then set the delegate property of the destination view controller to self. I believe we set the delegate property to self, because we want to send messages to a delegate in the view controller we're going to.
Heres the last one I don't get:
In the header file:
#property (weak, nonatomic)id delegate;
In the implementation file: (the controllerVisisble property is a boolean, and is changed to YES when the user hits a button to perform a manual segue to the second view controller, which is this one)
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
((ViewController *)self.delegate).controllerrVisisble=NO;
}
Heres what I think this does: the viewWillDisappear method is called when a view is closing/removed.
I'm not quite sure about [super viewWillDisappear:animated], but I'm guessing that it gives an animation when the view disappears?However, I remove that line, and my dismissViewControllerAnimated method still gives an animation when the view controller is dismissed.
Here's the part that really confuses me. We need to access the first view controllers dateChooserVisible property somehow, to set it to NO, so we can access the second view controller again through our button. But, I don't understand why we have to typecast (viewController *), and type in self.delegate. Nor, do I understand why we created a property called delegate in the header file, to use here.
A lot of these questions are more generic than just within the context of view controllers.
[super methodName] calls the superclasses implementation of the method named methodName. In your case, Apple has written some code (that we don't have access to) in UIViewController's viewWillDisappear: method. ALWAYS call super when overriding a method from a superclass.
Note that viewWillDisappear: is just callback triggered whenever the view is set to disappear. My guess is that the super implementation of this method forwards this callback down to child view controllers, especially in the case of standard container classes like UINavigationController and UITabBarController.
The type casting really doesn't seem necessary, you can always call methods without compiler errors/warnings if the receiver is either type id or provides a declaration for the called method in its #interface.
As far as the delegates go, protocols and delegation are a major part of Objective-C, and widely used in Apple's APIs. This link might help you understand how they work; it helped me immensely.
I'm currently refactoring my app to be sure the it's MVC compliant.
I would like to split the controller (MyController which extends UIController) and the view (HomeView which extends UIView) I set the view in myController using
self.view = [[HomeView alloc] init];
When I push an UIButton, a method is called in the view, and in this method I would like to call a method from the controller.
In my view
[zenModeBtn addTarget:self action:#selector(touchZenMode:) forControlEvents:UIControlEventTouchDown];
...
- (void) touchZenMode:(id) sender {
[myController playZenMode];
}
But having a reference to the controller in the view is really a bad practice isn't it ?
EDIT :
So in my UIViewController I've made this :
- (id) init {
HomeView* myHomeView = [[HomeView alloc] init];
[myHomeView.arcadeModeBtn addTarget:self action:#selector(touchArcadeMode) forControlEvents:UIControlEventTouchUpInside];
self.view = myHomeView;
return self;
}
is that correct ?
The view talking to your controller is no problem, as outlined by some answers here. E.g. a text field can notify its controller via the defined delegate methods.
However, your design is still seriously flawed. Your view has absolutely no business handling a button press itself. Your intuition that the view should not know about its controller is correct.
Your controller should know about the button and how to react to it being tapped. That's why a controller has button IBOutlets to tell the button to e.g. change its title or enabled state. And it has button handlers to react to UI events. It is the controller's job to handle this logic. It is the view's job to display the title, gray out or send a tap event back to the controller.
The only code you should put into a view is basically how to draw itself. Everything that cannot be handled by a controller.
The basic idea of the MVC pattern, as used in Cocoa Touch:
As described here: The Model-View-Controller Design Pattern
What you want to achieve, is a form of loose-, even blind maybe, coupling. By using protocols (for delegation mechanism), a View only knows that there is an object that adopts a specific protocol, it can 'talk' to.
Take the UITableView for instance. It does not need to know that there is a certain type of UIViewController that helps it gather data, but only that there is an object that adopts the UITableViewDatasourceDelegate and/or UITableViewDelegate; that object can be of any type.
In your edit, you use the target-action mechanism, which is another way of achieving loose-coupling. You set up the connection at runtime; your View does not know your Controller. Therefor: correct, apart from the comment #Mundi made about your init implementation being incomplete.
The view needs some way to communicate things back to the controller, ask it questions about what to do next, etc. So it's perfectly fine for the view to know something about the controller.
Some of the built-in views, like UITextField, define protocols they use to tell their delegate about what's going on, or ask it to do something. You typically implement the protocol in your controller. That way the view doesn't really know much about the controller, just enough to communicate. That makes your view more generic and reusable.
What you want to avoid is for your view to have direct links to your model. The role of the controller is to mediate between the view and the model. You should be able to completely change how the view is implemented without touching the model, and vice-versa.
You can put that method in a protocol in your view's interface:
#protocol MyViewDelegateProtocol <NSObject>
-(void)myMethod;
#end
you put a new property of NSObject type called delegate in your view.
you make your view controller comply to that protocol and when it inits the view assign the delegate property to self.
you implement myMethod in your view controller implementation.
and now you just call [delegate myMethod] from your view whenever you need it.