Dismissing UIPopoverController with UINavigationController - ios

I'm currently building an iPad app in which I need to implement a pop over view.
I have set up a view controller like I always do:
Create UIViewController wit xib file
set the xib up and do the necessary programming in it's .h & .m files
now in the view controller I'm loading it from (from a UIBarButtonItem), I have this code:
- (void) action
{
ItemContent *newItem = [[ItemContent alloc] initWithNibName:#"ItemContent" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:newItem];
_popover = [[UIPopoverController alloc] initWithContentViewController:nav];
[_popover setPopoverContentSize:CGSizeMake(557, 700) animated:YES];
_popover.delegate = self;
[_popover presentPopoverFromBarButtonItem:self.navigationItem.rightBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
This properly displays my view- and UINavigationController in a UIPopOverController. So far so good!
In the newItem view controller, I have made a bar button in my navigation bar that says "Done". When that button is pushed, I want the UIPopOverController to disappear. How do I do this:
Set a method for when te button is pushed. In this method I want to call a function on the view controller that loaded the Popover to dismiss it again.. but how do I do this?
Put things shortly
How do I make my UIPopOverController call a method on the view controller that loaded the UIPopOverController?
I have been searching SO for a while but none of the solutions and answers solve my problem. If I missed something please inform me ;)
Thank you so much in advance!

You can do this by delegate...
In NewItem.h declare a protocol
#protocol NewItemDelegate
-(void)onTapDoneButton;
#end
Now create a delegate property like this
#property (nonatomic, assign) id<NewItemDelegate>delegate;
In NewItem.m in doneButtonPuhsed method call this
[self.delegate onTapDoneButton];
Change this method a bit
- (void) action
{
ItemContent *newItem = [[ItemContent alloc] initWithNibName:#"ItemContent" bundle:nil];
newItem.delegate =self;
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:newItem];
_popover = [[UIPopoverController alloc] initWithContentViewController:nav];
[_popover setPopoverContentSize:CGSizeMake(557, 700) animated:YES];
_popover.delegate = self;
[_popover presentPopoverFromBarButtonItem:self.navigationItem.rightBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
Now implement NewItemDelegate method below this action method.
-(void)onTapDoneButton{
//dismiss popover here
}

I had this problem too and solved it using notifications.
In your parent controller, in viewDidLoad method, you have to add an observer:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(closePopover)
name:#"closePopoverName"
object:nil];
Then, in your viewDidUnload method you remove the observer like this:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Of course, you have to create the function which actually dismisses the popover:
- (void) closePopover
{
[_popover dismissPopoverAnimated:YES];
}
Then, in your ItemContent controller, you just post a notification when you want to close the popover:
[[NSNotificationCenter defaultCenter] postNotificationName:#"closePopoverName" object:self userInfo:nil];

You could define a protocol on ItemContent and use it on your CallerViewController. If you want to close your Popover, just call your delegate method, which you will implement on your CallerViewController

Related

Issue while pushing ABNewPersonViewController

I am using AddressBookUI Framework for Adding contact, when I tried to pushing this view controller then cancel and done button not working properly, I don't want to present it
Here is my code
ABNewPersonViewController *abnpvc = [[ABNewPersonViewController alloc] init];
[abnpvc setNewPersonViewDelegate: self];
[self.navigationController pushViewController:abnpvc animated:YES];
I am also tried add as subview rather then pushing it but when I am adding as subview then it was not added
As per comment i have tried like
UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:abnpvc];
[self presentViewController:navigation animated:YES completion:nil];
Can anyone help me out why properly not working ?
You can implement that too considering also the other answers and the deprecations to ABNewPersonViewController in iOS 9.
As per your remarks:
cancel and done button not working properly
They are working if you have included the ABNewPersonViewControllerDelegate on interface like this:
#interface ViewController () <ABNewPersonViewControllerDelegate>
Pushing the viewController on navigation stack like this:
ABNewPersonViewController *controller = [[ABNewPersonViewController alloc] init];
controller.newPersonViewDelegate = self;
[self.navigationController pushViewController:controller animated:YES];
And by conforming to the protocol by implementing this method:
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(nullable ABRecordRef)person {
// Trick to go back to your view by popping it from the navigation stack when done or cancel button is pressed
[self.navigationController popViewControllerAnimated:YES];
}
The tricky line is to pop the newPersonController from the navigation stack when either Done or Cancel button are pressed.
Enjoy it
Why can't you just do it as the docs say?
It is recommended that you present a new-person view controller modally.
Use
ABNewPersonViewController *abnpvc = [[ABNewPersonViewController alloc] init];
[abnpvc setNewPersonViewDelegate: self];
[self presentViewController:abnpvc animated:YES completion:nil];
That should work fine.
Edit
On second thought, did you set your delegate correctly and do the implementations get called? I suspect they are not implemented or the delegate is not set correctly.
Apple guideline(IMPORTANT) :: New-person view controllers must be used with a navigation controller in order to function properly. It is recommended that you present a new-person view controller modally.
Add Delegate
#interface ViewController () <ABNewPersonViewControllerDelegate>
Pushing the viewController
ABNewPersonViewController *abnpvc = [[ABNewPersonViewController alloc] init];
[abnpvc setNewPersonViewDelegate: self];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:abnpvc];
[self presentModalViewController:navController animated:YES];
And Now Add Delegate Method
#pragma mark ABNewPersonViewControllerDelegate methods
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person
{
[self dismissViewControllerAnimated:YES completion:NULL];
}
That will work fine.

Transfer data between view controllers, using storyboard and TabBarController

I have a Login ViewController and a Tab Bar Controller with two “tabs”. Now I want to transfer data from Login VC to one of the TabBar VCs. If the Login VC receives certain information, it should call a method in one of the TabBar VCs. I created all the view controllers with the storyboard. I don’t use programmatically segue of the view controllers.
Thus I tried to use a protocol and delegate. But when I’m coding something like this:
LoginViewController *loginVC = [[LoginViewController alloc] init];
loginVC.myTestDelegate = self;
It creates a new instance of the view controller and didn’t take the existing where it gets the information to call the method.
Is there any way to fix that problem? Or do I have to use programmatically segues?
If yes how can I change the tabs programmatically?
YourViewController* pdvc = [[YourViewController alloc] init];
pdvc.selectedImage = selectedPhoto;
pdvc.selectedObj = theObject;
pdvc.selectedImg = theImage;
[self presentViewController:pdvc animated:YES completion:nil];
or
YourViewController* nextView = [self.storyboard
instantiateViewControllerWithIdentifier:
#"YOUR_NEXT_STORYBOARD_ID"];
[nextView setYOUR_VALUE:value];
[self presentViewController:nextView
animated:YES
completion:nil];
If you want to navigate to the same viewController to which you want to pass data when your loginViewController receives data, then you can use following method which is triggered when the transition begins from the current view.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"yourViewIdentifier"]) {
RecipeDetailViewController *destViewController = segue.destinationViewController;
destViewController.recievingData = yourDataToPass;
}
}
Second method is to add observers using NSNotificationCenter class:
Add following method to your TabBar VCs in viewDidLoad: method
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(viewDidReceivedData:)
name:"ReceivedData" object:nil];
And implement following method:
- (void)viewDidReceivedData:(id)receivedData{
}
In your loginViewController,once you receive necessary information to pass to the viewcontroller call following:
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReceivedData" object:yourData];
Refer: http://www.hpique.com/2013/12/nsnotificationcenter-part-1/

Switching view controllers from UIToolbar subclass

Please excuse my newness in IOS, but I am trying to switch view controllers when someone clicks on a button in my toolbar. For my toolbar I have overridden the UIToolbar and drawn my own custom toolbar. I have four buttons each with their own action something like this:
NSMutableArray *toolbarItems = [#[] mutableCopy];
[toolbarItems addObject:[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"notifications"] style:UIBarButtonItemStyleBordered target:self action:#selector(viewNotifications)]];
I want to be able to do something like:
-(void)viewNotifications
{
NSLog(#"CustomUIToolbar::viewNotifications");
//layoutFlow....
// Show the notifications view controller
NotificationsViewController *rootViewController = [[NotificationsViewController alloc] initWithCollectionViewLayout:layoutFlow];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
[self presentViewController:navController animated:YES completion:NULL];
}
The problem with this is clearly that the UIToolbar doesn't directly have access to switching view controllers. Is there a way to access the presentViewController method or something like it from within the custom UIToolbar?
Create a protocol and a delegate and let the UIViewController which creates the UIToolBar implement that delegate.
When the user presses a UIBarButtonItem you send the delegate the message (the main UIViewController) and handle the UINavigationController code there.
#protocol ToolBarProtocol <NSObject>
-(void)didPressButton1;
#end
#property (nonatomic) id <ToolBarProtocol> delegate
And when creating your UIToolBar:
YourToolBar *toolbar = [YourToolBar alloc] init];
toolbar.delegate = self;
Inside your method change to tell the delegate what should happen:
-(void)viewNotifications
{
NSLog(#"CustomUIToolbar::viewNotifications");
//layoutFlow....
if ([self.delegate respondsToSelector:#selector(didPressButton1)])
{
[self.delegate didPressButton1];
}
}

popoverControllerDidDismissPopover method is not called

In my application I have a presentViewController, and inside it I have a button that opens a popover. In this popover I have a barButtonItem to save de data of this popover. I would like that when the user taps outside of the popover, the data could be saved too.
I've tried to use the popoverControllerDidDismissPopover method in the presentViewController view. I have the delegate but when I tap outside of the popover this method is not called.
What can I do?
Thanks!!
Add this line of code while adding popOver:
popover.delegate = self;
Also register popOverDelegate in .h file where u present your popOver COntroller
#interface yourViewController : UIViewController<UIPopoverControllerDelegate>
Please, pay attention to docs!
"Called on the delegate when the user has taken action to dismiss the popover.
This is not called when the popover is dismissed programmatically."
It was my case because my popover was closing on button tap with this method:
dismiss(animated: true, completion: nil)
You probably already solved it, but I just faced the same problem. I'm holding a instance of UIPopoverController in my Viewcontroller and had it this way:
self.popover.delegate = self;
self.popover = [[UIPopoverController alloc] initWithContentViewController:wgtvc];
of course this doesn't work because I'm initializing the UIPopoverController AFTER setting the delegate, which overrides the delegate setting. So the correct way is to FIRST initialize the UIPopovercontroller and THEN setting the delegate
self.popover = [[UIPopoverController alloc] initWithContentViewController:wgtvc];
self.popover.delegate = self;
Maybe you are reinitializing your UIPopoverController somewhere - just set the delegate again after reinitializing.
I had the same problem and I solved it by handling it different for iOS8.
Presentation code
UIViewController *searchViewController = [[UIViewController alloc] init];
[[searchViewController view] addSubview:_searchOptions];
[searchViewController setModalPresentationStyle:UIModalPresentationPopover];
[searchViewController setPreferredContentSize:CGSizeMake(500, 400)];
[_searchOptions setHidden:NO];
[_searchOptions setFrame:[[searchViewController view] bounds]];
[_searchOptions setAutoresizingMask:UIViewAutoresizingFlexibleWidthAndHeight];
if (CRIdiomToolsIsIOS8OrHigher())
{
UIPopoverPresentationController *popOverPresentationController = [searchViewController popoverPresentationController];
[popOverPresentationController setDelegate:self];
[popOverPresentationController setSourceView:[_searchOptionsButton disclosureView]];
[popOverPresentationController setSourceRect:[[_searchOptionsButton disclosureView] bounds]];
[self presentViewController:searchViewController animated:YES completion:nil];
}
else
{
UIPopoverController *popOverControler = [[UIPopoverController alloc] initWithContentViewController:searchViewController];
[popOverControler setDelegate:self];
[popOverControler setPopoverContentSize:CGSizeMake(500, 400)];
[popOverControler presentPopoverFromRect:[[_searchOptionsButton disclosureView] bounds] inView:[_searchOptionsButton disclosureView] permittedArrowDirections:UIPopoverArrowDirectionUp|UIPopoverArrowDirectionLeft animated:YES];
}
Delegate calls
#pragma mark Delegate Methods: UIPopoverControllerDelegate
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
[self showSearchOptions:NO animated:YES];
}
#pragma mark Delegate Methods: UIPopoverPresentationControllerDelegate
- (void)popoverPresentationControllerDidDismissPopover:(UIPopoverPresentationController *)popoverPresentationController
{
[self showSearchOptions:NO animated:YES];
}
I had the same issue. You will need to retain the popover object, that way the delegate method gets called. Its strange but it works.
#property (nonatomic, retain) UIPopoverController *popupObject;
UIPopoverController *popup = [[UIPopoverController alloc] initWithContentViewController:viewController];
popup.delegate = self;
[popup presentPopoverFromRect:presentationRect inView:self permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
self.popupObject = popup; //Retained
-(void) popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
//Do whatever operation you need to perform
self.popupObject = nil;
}
I know this questions is old but hopefully it helps someone out there...
The issue is in the initialization of your popover controller. If you have established the popover segue in the storyboard you need to have a reference to this popover in order for the delegate to be called when it is dismissed.
In your prepare for segue method:
Instead of:
self.popoverController = [[UIPopoverController alloc]initWithContentViewController:segue.destinationViewController];
self.popoverController.delegate = self;
You need:
self.popoverController = [(UIStoryboardPopoverSegue *)segue popoverController];
self.popoverController.delegate = self;
Then make sure to correctly handle if the when the popover should appear in
(BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender

UIPopoverController deallocated issue

I used a Popover to display image in it. When the user touch a button, the popover appears with a slideshow inside.
I initialize the Popover like this : `
- (IBAction)showPopover:(UIButton *)sender {
myPopover *content = [[myPopover alloc] init];
detailViewPopover = [[UIPopoverController alloc] initWithContentViewController:content];
detailViewPopover.popoverContentSize = CGSizeMake(600., 400.);
detailViewPopover.delegate = self;
[detailViewPopover presentPopoverFromRect:sender.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[content release];
}
`
detailViewPopover is a UIPopoverController, I declare it my .h.
I dismiss the Popover like this : `
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
// If a popover is dismissed, set the last button tapped to nil.
[popoverController release];
}`
When I run my app, it works until I got "-[UIPopoverController release]: message sent to deallocated instance 0x1b29b0" and my apps crashes...
I understand I release too much time my UIPopoverController, but I don't know where. Is my implementation good ?
Thanks for your help
Let me know if you need more information, I will edit the post
You shouldn't release your popoverController here.
You need to call release on detailViewPopover in your current view controllers dealloc method
- (void) dealloc
{
[detailViewPopover release];
[super dealloc];
}

Resources