I have a Settings popover. I have a button (More Info) in that popover. When the user clicks on that button, I want to bring up another view and I want to dismiss the popover.
How do I do this from my More Info button that is actually inside the popover?
Current I have a function that handles the button press:
- (IBAction)showFullVersionInfo:(id)sender
{
[self dismissPopoverAnimated:YES];
parent.settingsPopover = nil;
//need to show more Info...
}
The parent holds a reference 'settingsPopover' to the popover and I want to clear it.
You don't want to do this from inside the popover controller. The popover controller should manage only the contents of your popover, not how and when the popover is shown/hidden.
You can do such actions only in the parent controller. Hide the popover only from the controller which has shown it (and owns it).
Your question should actually be: how to tell the parent controller that the user has performed some action inside the popover?
And the answer is: delegate.
Define a delegate, implement it in your parent controller, pass it to your popover and in IBAction call delegate method.
The best way is to release popover in popoverControllerDidDismissPopover: delegate callback. Unfortunately this callback doesn't call after dismissPopoverAnimated:. I solved this by entering another unified callback named for example popoverDidFinishWorking:. Send it from popover to owner when close button was tapped, etc. When owner get popoverDidFinishWorking: it just invokes popoverControllerDidDismissPopover: and hides/release the popover (settingsPopover). You also can show any other view in this callback. Anyway it depends on your app design.
In your 'parent' object in the function where you launch the settingsPopover you should have :
- (void) launchDismissSettingPopover {
if (settingPopover == nil) {
//Create an launch the popover
} else {
[self dismissPopoverAnimated:YES];
// settingPopover will be nil in navigationController didShowViewController
//self.settingsPopover = nil;
}
}
You should have self.navigationController.delegate = self before in your parent class
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (viewController == self && self.settingPopover != nil) {
//need to show more Info based on settingPopover info here
self.settingPopover = nil ;
}
}
Then in your popover class method
- (IBAction)showFullVersionInfo:(id)sender
{
[parent launchDismissSettingPopover];
}
Related
I would like to display an image in response to a local notification event that occurs while the iPhone is locked. When the user swipes on the notification, I would like my app to go to the root view controller, via popToViewController:animated, and then push a view controller that displays the image. This works when I set animated = YES. When animated = NO, the view controller that displays the image doesn't respond when the user taps the back button. Any thoughts on why the image view controller's navigation controls don't work when I popToViewController without animation? Thanks in advance.
Here's the relevant code...
- (void) localNotificationHandler
{
#ifdef kAnimatePop
animated = YES; // This works
#else
animated = NO; // This doesn't work
#endif
_showImage = YES;
// Check if this view controller is not visible
if (self.navigationController.visibleViewController != self) {
// Check if there are view controllers on the stack
if ([self.navigationController.viewControllers count] > 1) {
// Make this view controller visible
[self.navigationController popToViewController:self animated:animated];
}
}
}
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (_showImage) {
_showImage = NO;
// Show image in a view controller
[self performSegueWithIdentifier:#"MainToImageSegue" sender:self];
}
}
You don't set _showImage until after popToViewController has been invoked. If it's animated viewDidAppear won't be called until later, so it unexpectedly works. If it's not animated then viewDidAppear is called immediately, before _showImage has been set. Move _showImage = true; to before the nav controller stack manipulations.
I might do the check this way:
- (void) localNotificationHandler{
...
...
if (self.navigationController.topViewController != self) {
[self.navigationController popToRootViewControllerAnimated:NO];
}
}
I would also recommend pushing your image view controller programmatically with something like
[self.navigationController pushViewController:<imageVC> animated:YES];
If your image view controller's navigation buttons are not working, it is likely because it is sending those navigation messages to nil. aka, the imageViewControllers self.navigationController is equal to nil.
I'm not sure how to fix this with storyboards, but if you present it programatically that problem should go away.
Say I have UIViewController A and B.
User navigates from A to B with a push segue.
Than user presses back button and comes to A.
Now viewWillAppear of A is called. Can I know in the code here that I came from back button (navigationController popTo...) and not by another way? And without writing special code in the B view controller.
hm, maybe you can use self.isMovingToParentViewController in viewWillAppear, see docs, if it is NO then it means the current view controller is already on the navigation stack.
I like to do the following in view controller A:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (_popping) {
_popping = false;
NSLog(#"BECAUSE OF POPPING");
} else {
NSLog(#"APPEARING ANOTHER WAY");
}
//keep stack size updated
_stackSize = self.navigationController.viewControllers.count;
....
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
_popping = self.navigationController.viewControllers.count > _stackSize;
....
}
What you are doing is keeping track of whether your view controller (A) is disappearing because a view controller (B) is being pushed or for another reason. Then (if you did not modify the child view controller order) it should accurately tell you if (A) is appearing because of a pop on the navigation controller.
Add a BOOL property to UIViewController A:
#property (nonatomic) BOOL alreadyAppeared;
Then in your viewWillAppear: method, add:
if (!self.alreadyAppeared) {
self.alreadyAppeared = YES;
// Do here the stuff you wanted to do on first appear
}
Is there a way to tell if a new controller came from a navigation back button or was pushed onto the stack? Id like to reload data only for pushing on the navigation stack, not on a back button press.
As of iOS 5.0 you can do this:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.isBeingPresented || self.isMovingToParentViewController) {
// "self" is being shown for the 1st time, not because of a "back" button.
}
}
If your push also includes instantiating the view controller, put your push-only logic in viewDidLoad. It will not be called on back because it has already been loaded.
You could implement the UINavigationControllerDelegate and override the `navigationController:didShowViewController:animated:' method. You'll then have to check the returned view controller to make a determination as to whether you came back from the expected view controller.
- (void)navigationController:(UINavigationController*)navigationController didShowViewController:(UIViewController*)viewController animated:(BOOL)animated
{
if (yourPushedViewController == viewController)
{
// Do something
}
}
Let me explain. I have multiple UIViewControllers. On my MainPageController, I have 3 UIViews. Let's enumerate it this way: the first UIView is called LoginView, the second is called HomeView and the other one is called RegView. Now in HomeView, there are multiple buttons that will lead to other UIViewControllers. For example, one button will lead to StoreController. Now if I am inside StoreController and I want to go back to MainPageController, I simply call:
[self dismissModalViewControllerAnimated:YES completion:nil]
This will send me back to the HomeView.
That is good. However, inside the StoreController, there are buttons which will supposedly direct me to LoginView or RegView, whichever button was tapped. The problem is when the method [self dismissModalViewControllerAnimated:YES completion:nil], it only take me back to HomeView, no matter which button I pressed.
So how will I display the right UIView once the dismissModalViewControllerAnimated is called?
EDIT:
This is how I show the UIViews:
-(void)viewDidLoad
{
//Initialize the views here...
}
-(void)showViewByTag:(NSInteger)tag
{
if (tag == 1)
{
[self.view addSubview:loginView];
}
else if (tag == 2)
{
[self.view addSubview:homeView];
}
else
{
[self.view addSubview:regView];
}
}
Now I call the method showViewByTag: somewhere in my code to display the views.
What you could try and do is following: before calling [self dismissModalViewControllerAnimated:YES completion:nil] (and thus go back to your home view), change the view currently displayed in your MainPageController:
[(MainPageController*)self.presentingViewController showViewByTag:desiredViewTag];
[self dismissModalViewControllerAnimated:YES...];
If you are worried at the cast and you foresee that self.presentingViewController might be not of MainPageController type on some occasions, then you can check explicitly for its type:
if ([self.presentingViewController isKindOf:[MainPageController class]])
[(MainPageController*)self.presentingViewController showViewByTag:desiredViewTag];
[self dismissModalViewControllerAnimated:YES...];
For this to compile, MainPageController.h must be imported in your modal controller class.
dismissModalViewController will always bring back the viewController which presented it ,and that can be only one,so the ideal way would be to tell the navigationController to initWith your desired viewController..
eg on regButton click in the presented modalview
RegViewController *regViewController = [[RegViewController alloc]initWithNibNam:#"RegViewController" bundle:nil];
[self.navigationController initWithRootViewController:regViewController];
Hi there, Now I'm trying to create a Pop-OverView using an Xcode
storyboard. Firstly, I have
rootViewController, UIViewController, and UITableViewController
I want the UIView to act as a page flip and the UITableView will show popOver under the navigationBar item controller.
For the UITableView, I want to make a Pop-Over under NavigationBar controller. The problem is, when I touch the Navigation item to show the UITableViewController, it shows correctly, but when I try to close the Pop-Over View, it won't close. And then, the navigation item doesn't work well. It shows multiple instances of popOverView when I touch it multiple times.
This doesn't seem to make sense to me. Can anyone help me out or tell me where to find documentation / tutorials on this?
UPDATE:
For the UIPopOverController, it seems to work well now, but it is still bugging me when I touch a Navigation Item multiple times. It will show multiple instances of PopOver. How can I handle it, so it will show only one instance?
I had the same problem and mostly found the solution here. Basically you change the action of the button each time it's pressed to either display or dismiss the popover. Here's the code I ended up with:
#interface FilterTableViewController : UITableViewController {
UIPopoverController *editPopover;
id saveEditSender;
id saveEditTarget;
SEL saveEditAction;
}
-(void)prepareForSegue:(UIStoryboardPopoverSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:#"EditFilterSegue"]){
// Save the edit button's info so we can restore it
saveEditAction = [sender action];
saveEditTarget = [sender target];
saveEditSender = sender;
// Change the edit button's target to us, and its action to dismiss the popover
[sender setAction:#selector(dismissPopover:)];
[sender setTarget:self];
// Save the popover controller and set ourselves as the its delegate so we can
// restore the button action when this popover is dismissed (this happens when the popover
// is dismissed by tapping outside the view, not by tapping the edit button again)
editPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
editPopover.delegate = (id <UIPopoverControllerDelegate>)self;
}
}
-(void)dismissPopover:(id)sender
{
// Restore the buttons actions before we dismiss the popover
[saveEditSender setAction:saveEditAction];
[saveEditSender setTarget:saveEditTarget];
[editPopover dismissPopoverAnimated:YES];
}
-(BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
// A tap occurred outside of the popover.
// Restore the button actions before its dismissed.
[saveEditSender setAction:saveEditAction];
[saveEditSender setTarget:saveEditTarget];
return YES;
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// Before we navigate away from this view (the back button was pressed)
// remove the edit popover (if it exists).
[self dismissPopover:saveEditSender];
}