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Ă !
Related
I am new to story boards and I am trying to move between table view controllers.
I understand how to set up a segue and how to send data to the new story board but my question is returning data back. If I use the push style segue it gives me an automatic back button. I want a page that will create a "job" and save it if they hit save (a bar button I created on the other side of the title) When I set up the segue to go back to the main page from the save button it made that main table view controller a child (instead of simply going back to it's original state). The work around I was thinking was saving it to a file when they hit save and whenever they load the main table view it loads from that file. Is this a common and correct way to do this or should I be trying to return that object and save it in the main table view controller?
A common approach is to use delegation. Since ViewControllers should be as independent as it is possible, we have to minimize dependencies between them. And actually your idea with the file, if I understand it correctly, does it as well. But using files to organize a communication between ViewControllers is not very convinient.
Usually you declare a #protocol SecondViewControllerDelegate for your second ViewController (where you click a "Save" button). With methods like:
#protocol YourSecondViewControllerDelegate <NSObject>
-(void)yourSecondViewControllerDidCancel:(YourSecondViewController*)controller; //if you have a cancel button
-(void)yourSecondViewControllerDidFinish:(YourSecondViewController*)controller yourDataToReturn:(SomeData*)data andSomeMoreData:(AnotherDataType*)data2;
#end
Then you add a property like this to your SecondViewController:
#property (nonatomic, assign) id<YourSecondViewControllerDelegate> delegate; //Do not retain it!
Then you adopt your protocol in your MainViewController.
#interface MainViewController()<YourSecondViewControllerDelegate> //you can do it in the private category to keep the class interface clear.
#end
#implementation
//don't forget to implement those methods here :)
#end
And when you trigger your segue from the MainViewController, you can set your MainViewController as a delegate for the SecondViewController:
SecondViewController *destinationController = [[SecondViewController alloc] init]; //Just for example.
destinationController.delegate = self;
//trigger a segue :)
When the user presses the Save button in the SecondViewController, you call the delegate's yourSecondViewControllerDidFinish:
[self.delegate yourSecondViewControllerDidFinish: self yourDataToReturn: someData andSomeMoreDate: someMoreData];
This way MainController's yourSecondViewControllerDidFinish method will be called. And you can pick someData and someMoreData up there.
You can get more details about it in this tutorial:
Apple's your second iOS app tutorial
If you're making a segue to go "back" to a controller, then that segue needs to be an unwind segue. An unwind segue goes back to the same instance of the controller that you came from originally. If you need to send data back to that controller, you can do it in prepareForSegue.
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.
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.
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];
In Objective C (Cocoa) I have an app running with a modal sheet, but I want to allow the app to quit even when the sheet is displayed (contradicting the definition of modal I think, but I like the animated effect of modal sheets).
I'm already using the -setPreventsApplicationTerminationWhenModal method and it works fine, but I'm wondering... is there any way to keep the close button enabled? The little circle usually red-colored close button that comes with all windows in the top left corner (side by side with minimize and maximize)? Right now it's completely disabled when the sheet is running, and it would be awesome if there is a way to enable it.
Thanks!
Use a delegate method to close the Modal View. You declare the delegate on your modal view controller and that delegate method dismisses the ModalViewController
In the Modal ViewController Interface File:
#protocol MyViewControllerDelegate
-(void)dismissModal;
#end
Then declare the delegate as a class property in the Modal ViewController:
#property (nonatomic, retain) id <MyViewControllerDelegate> delegate;
Now, declare your parent ViewController as a proper delegate implementer for the Modal ViewController:
#interface MyParentViewController : UIViewController
Then in the calling (parent) ViewController implement the delegate method in the implementation file:
-(void)dismissModal
{
// Dismiss the Modal ViewController that we instantiated earlier
[self dismissModalViewControllerAnimated:YES];
}
That should do it. The advised way to handle this is through delegate methods (and delegate methods are so handy to use whenever a process in one controller needs to fire a method in another controller. It is well worth the time to get familiar with using delegates to get work done in Obj C