Reference the main view controller... need the code - objective-c

I have a view based app (not navigation or tab based...)
My main view controller is called from the app delegate and initiated from a xib.
Then I use presentModalViewController to bring another view on the screen with it's own xib and view controller.
I have no problems passing data to that view controller.
However, when I dismiss the second view controller, I want to send data back to the main view controller for my app, but I just can't figure out how to reference it. Actually, I'd like to call a method in the main view controller if possible.
I've been struggling with this a bit and have found suggestions online but I just can't seem to get it to work. I'm hoping someone can provide the sample code to do this.
P.s. is this "main view controller" still referred to as a "root view controller" or is that term only used when dealing with a view controller stack (i.e. navigation or tab view controller)
EDIT:
I'm sure Bryan's solution would work so I have accepted as answer. However I ended up using NSNotificationCenter to get this to work and I find it a bit simpler to understand as a beginner

You can use the delegation pattern. In your modal view controller's header file, create an interface for a new delegate protocol...
#protocol ModalViewControllerDelegate <NSObject>
- (void)sendData:(Data *)someData;
#end
...and give your ModalViewController a new instance variable that implements this protocol:
#property (nonatomic, assign) id<ModalViewControllerDelegate> delegate;
Your main view controller should implement this protocol...
#interface MainViewController : UIViewController <ModalViewControllerDelegate> {
...and set itself as the delegate before it presents the modal view controller:
ModalViewController *modalViewController = [[[ModalViewController alloc] init] autorelease];
[modalViewController setDelegate:self];
// Present modal view controller
The main view controller should implement the delegate protocol's method:
- (void)sendData:(Data *)someData {
NSLog("I have just received some data: %#", someData);
}
Then inside your modal view controller, you can simply call the following method whenever you want to send data back to the main view controller:
[delegate sendData:someData];

Related

Accessing an instance method of a ViewController From Another view controller

Let's say I have a view controller called vc1, which a synthesized property called property1, and i wants to access it from another view controller (vc2) and change it from vc2.
Now the methods created by the #syntisize to change and get properties are instance methods, so how can I get to them fro another view controller (do view controllers have instances in the app, and if so, what are they?)
Just to be clear I am using storyboards, so I never really instantiate the view controllers...
VC1.m:
-(void) yourMethod {
...
}
VC2.m
YOURViewController * vc2 = [[YOURViewController alloc]init];
[vc yourMethod];
[vc release];
Make sure to import your YOURViewController in your other view .m file
Something like that should work.
Or if you're having problems, try this tutorial here:
Tutorial on How-To Pass Data Between Two View Controllers
Hope this helps :)
While you can do it the way you describe, I think the common technique (assuming VC1 has a segue to VC2) is a bit different, where VC2 will have a property that will be set by prepareForSegue. See Configuring the Destination Controller When a Segue is Triggered in the View Controller Programming Guide.
You will need to link the storyboard views with the viewcontrollers so the view for vc1 would use the class vc1 etc for the rest (I assume you have done this because this is important when coding for different views)
Then all you need to do is where ever you are calling the properties so lets say the viewDidLoad method, declare the view controller like this:
- (void) viewDidLoad {
vc1 *viewController;
// Now you change the variable I'll presume its a UILabel so I'll change its text [viewController.property1 setText:#"I changed a different views UILabel"];
}
Let me know whether this works... Its worked for me before so should work

Dismissing the split view popover controller

I have a UISplitViewController with the master view set up like this:
UITabBarController
Tab1:
UINavigationController -> UIViewController -> UIViewController
Tab2:
UINavigationController -> UIViewController
Each of the UIViewControllers is a table view, and when the user chooses a row in the last one, an image is shown in the detail view, which contains a UIScrollView.
The tab bar Controller is the UISplitViewControllerDelegate and handles putting up the button on a toolbar at the top of the scroll view.
The problem is, I want to add code to dismiss the popover when the user makes their choice. The pointer to the popover has to be saved in the tab bar controller when the button goes up, and then used to dismiss the popover several view controllers down the line when the user makes their final selection. There doesn't seem to be any way for the view controller that needs that pointer to get at it, without doing something gross like storing it in the App Delegate.
I don't see other people asking this question, which leads me to believe that I've once again overlooked something simple. Please enlighten me!
It sounds like your tab bar controller is already a subclass of UITabBarController, which means that you've already got some custom code in there. I would suggest that the tab bar controller is the primary owner of the popover, and it is the table view controller's responsibility to simply notify the tab bar controller that a selection has been made. The tab bar controller can respond to that message by dismissing the popover. You can take advantage of the fact that UIViewController already has a method for accessing the tab bar controller that contains a given controller.
So it would look something like this:
#interface MyTabBarController : UITabBarController
- (void)itemWasSelected;
#end
#implementation MyTabBarController {
UIPopoverController *popover;
}
- (void)itemWasSelected {
[popover dismissPopoverControllerAnimated:YES];
}
#end
//////////////
#implementation TableController
- (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)path {
// Do whatever else you want to do
MyTabBarController *tabController = (MyTabBarController *)self.tabBarController;
[tabController itemWasSelected];
}
With this solution, the table controller doesn't have to know anything about the popover; it just has to know that it's going to be presented inside a MyTabBarController, which seems a reasonable thing for it to know.
You could create a singleton class to track your popover status and then make it available to all classes equally and easily. That way it could easily be updated and accessed from any code without having to go straight to overburdening the app delegate even though thats basically the same idea but a bit cleaner in its own singleton.

View Controller Doesn't respond to Action Methods

I have a simple test app (in OSX) that has a view controller with a button in its view. The button's action method is in the view controller's class, and that IBAction is connected in IB (through File's Owner). When the button is clicked, I get an EXC_BAD_Access error (except occasionally I get -[NSRunLoop buttonClick:] instead). I've read a bunch of posts here on SO having to do with NSViewControllers not being in the responder chain, but also that specifically hooking the action method up in IB should work. The only code I have is this:
In the app delegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
TestController *controller = [[TestController alloc] initWithNibName:#"TestController" bundle:nil];
[self.window.contentView addSubview:controller.view];
}
And, in the TestController class, just this:
-(IBAction)buttonClick:(id)sender {
NSLog(#"%#",sender);
}
I have 2 questions. Why is this happening, and where is the correct mvc place to put IBActions for button methods (shouldn't controller classes handle these)?
First off, I believe in the App Delegate there is already a property called viewController already defined for you. You should use this code self.viewController = [[TestController alloc] initWithNibName:#"TestController" bundle:nil]; instead of TestController *controller = [[TestController alloc] initWithNibName:#"TestController" bundle:nil];. If that doesn't fix it, then I'm not sure what's wrong. Your code should return the attributes of the button you clicked. I created my own sample project and tested out your code (although my app delegate looked different than yours) and it worked fine.
Second, you should elaborate on your second question "Why is this happening, and where is the correct mvc place to put IBActions for button methods (shouldn't controller classes handle these)?". It's not very clear what you mean.
Hope this helps a little.
NSViewController will not respond to IBActions on mac os x on iPhone It is the correct place to put your IBAction as the view should only draw its content (data) not change it. But on Mac os x the NSViewController is for setting up the NSView it's no ment to respond to IBActions you have two choices one is to put your IBAction in your NSView or is create a NSWindowController .
On mac osx you have plenty of screen space and you will alway have views within a window you'll use NSViewController to add them to your window and to setup your view but the window is first in your responder chain then your NSWindowController . eg: you may have one window and two view controller showing the same view which may have 5 test fields in it and view controller one loads that with data but you can not edit the data then view controller two loads the same view with the same data but enable editing for the text fields so. but all action methods will go to the WindowController .
I recommend you to check your viewController's identity inspector.
It may indicate wrong custom class.
viewController created basically usually has name ViewController on the section.

UINavigationController subclass - customizing Pop

Like many others, I would like to perform an action when the 'back' button is used in a UINavigationController. I am aware of some alternative approaches to this, such as adding my own button and trying to spoof the look, using viewWillDisappear and setting tags on all non-back actions that will cause the view do disappear, etc.
But the one that I would like to try is subclassing UINavigationController and injecting something in popViewControllerAnimated:
But I can't get a simple test case to work. Here is what I tried...
I created a new class, CustomNavigationController, which inherits from UINavigationController. In here, I updated popViewControllerAnimated:
.h:
#import <UIKit/UIKit.h>
#interface CustomNavigationController : UINavigationController
#end
.m:
#import "CustomNavigationController.h"
#interface CustomNavigationController ()
#end
#implementation CustomNavigationController
- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
NSLog(#"pop!");
return [super popViewControllerAnimated:animated];
}
#end
In interface builder, I changed the class of the navigation controller:
What am I missing? I am expecting 'pop' to be logged whenever I pop any view within this nav controller.
I've just tested the very same implementation on iPhone 5.1 simulator and it works fine - it seems the problem is somewhere else.
I've created an empty project from a single-view application template and added CustomNavigationController class with the code which you posted in the question body. An UIButton is used to push another view controller to navigation stack (using storyboard's segue), and 'POP' message is being posted in the console as expected.
From the first paragraph of the documentation on UINavigationController.
The UINavigationController class implements a specialized view
controller that manages the navigation of hierarchical content. This
class is not intended for subclassing. Instead, you use instances of
it as-is in situations where you want your application’s user
interface to reflect the hierarchical nature of your content.
If you want this kind of customization you're better off implementing your own navigation style controller.

Problem reloading gridView data after dismissing a modal view controller

I am presenting a modal view controller to show detailed information. I have it set up so that any change to the information in the modal view controller will change the information in its parent view controller.
The information is changed, but I cannot manage to reload the gridView data when the modal view controller is dismissed. Right now I have the action to dismiss the modal inside the modal view controller. Everything works good, I just can't reload the data for the gridView from the modal view controller.
I read somewhere that one of the options is to create a delegate that will be able to dismiss the modal view controller from the parent view controller, I just can't seem to find examples or nice tutorials on how to go by doing this. The truth is that I know how to use the delegates, but not quite sure on how to properly implement one.
Can anyone please point me in the right direction here? Maybe someone has a better option. I am open to any suggestions.
If I understand clearly, you want to reload the grid contained in your parent view controller when you dismiss the modal view controller. If so, here how:
Declare a protocol in your ModalViewController by doing something like
#protocol MyViewControllerDelegate;
#interface MyViewController : UIViewController {
id<MyViewControllerDelegate>delegate;
// Your stuff
}
#property (nonatomic, assign) id<MyViewControllerDelegate>delegate;
#end
#protocol MyViewControllerDelegate <NSObject>
-(void)viewControllerWasDismissedOrAnyOtherNameYoudLike;
#end
And in your .m file, just #synthesize delegate.
Just when you call dismissModalViewController:animated:, also call [delegate viewControllerWasDismissedOrAnyOtherNameYoudLike].
In your view controller with the grid view, import the header file of your modalviewcontroller, conform to the protocol
#interface MyGridViewController : UIViewController <MyViewControllerDelegate>
When you init the Modalview controller, set the delegate to self, and implement the viewControllerWasDismissedOrAnyOtherNameYoudLike method.
VoilĂ !