Releasing a popover which takes great amount of memory - ios

I am using a popover view to present a large amount of flags of which the your can select.
There is something wrong with my code since soon after I open this popover memory is not released (the viewcontroller "flagsViewController" is ok and clean, it does init and release each and every item inside of it.
What am I doing wrong? How can I free memory as soon as the popover is closed?
-(void)presentFlags
{
[self.popoverController dismissPopoverAnimated:YES];
FlagsViewController *controller = [[FlagsViewController alloc]
initWithNibName:#"FlagsViewController"
bundle:[NSBundle mainBundle]] ;
UINavigationController *container = [[UINavigationController alloc] initWithRootViewController:controller];
UISegmentedControl *ctrl = [[UISegmentedControl alloc] initWithItems:segmentedItems];
ctrl.frame = CGRectMake(0, 6, 500, 30);
[ctrl addTarget:self action:#selector(changeSeg:) forControlEvents:UIControlEventValueChanged];
ctrl.segmentedControlStyle = UISegmentedControlStyleBar;
//ctrl.momentary = YES;
ctrl.tintColor = [UIColor darkGrayColor];
UIImage *theImage = [UIImage imageNamed:#"highlight_country.png"];
[ctrl setImage:theImage forSegmentAtIndex:0];
[container.navigationBar addSubview:ctrl];
[ctrl release];
//
//create a popover controller
self.popoverController = [[[UIPopoverController alloc]
initWithContentViewController:container] autorelease];
[container release];
[popoverController setPopoverContentSize:CGSizeMake(500, 600)];
//present the popover view non-modal with a
//refrence to the button pressed within the current view
[popoverController presentPopoverFromRect:CGRectMake(popoverArrowPossition, 0.0, 0.0, 52.0) inView:super.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
[controller release];
}

Working with the UIPopoverController has been pretty difficult but I solved this problem by doing the following setting the Delegate of the Popover Controller to self (popoverController.delegate = self) and adding the UIPopoverControllerDelegate Protocol to your Class Header
Next, I implemented the - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController delegate method and here I released the popoverController and set it to nil.
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
[self.popoverController release];
self.popoverController = nil;
}
Please note: This delegate method won't be called if you dismiss the popover via code (i.e. using dismissPopoverAnimated), it'll only be called if this User dismisses it by tapping outside the popover etc.

Related

How do I make a popover appear over a button inside a collection view protype cell

I created a UpcomingCell.h and .m for the cell. In the button's tap method i tried this
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
// cameraSystemMenu = [mainStoryboard instantiateViewControllerWithIdentifier:#"CameraSystemMenuViewController"];
UIView *anchor = sender;
UIViewController *viewControllerForPopover =
[storyboard instantiateViewControllerWithIdentifier:#"yourIdentifier"];
self.popover = [[UIPopoverController alloc]
initWithContentViewController:viewControllerForPopover];
[self.popover presentPopoverFromRect:anchor.frame
inView:anchor.superview
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
and I also tried this
self.popoverContent = [[PopOverViewController alloc]init];
UIView *popoverView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 200, 300)];
[popoverView setBackgroundColor:[UIColor purpleColor]];
self.popoverContent.view = popoverView;
self.popoverContent.contentSizeForViewInPopover = CGSizeMake(200, 300);
UIPopoverController *contactPopover =[[UIPopoverController alloc] initWithContentViewController:self.popoverContent];
[contactPopover presentPopoverFromRect:self.remindButton.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES ];
[contactPopover setDelegate:self];
and countless other popover examples on stackoverflow and I cant get it to work.
Both examples crashes. The first one crashes on
the rect passed in to this method must have non-zero width and height. This will be an exception in a future release
The second one crashes because an UIPopoverController needs to be a property or an instance variable, otherwise it will be deallocated on the end of the method, because the View that's showing it will not retain it

Disable UIBarButtonItem when popover is dismissed

I am new in iOS development and I am trying to show a popover when tap a barButtomItem. So far I have this:
-(IBAction)shareButtonPressed :(id)sender{
UIViewController *popoverViewController = [[UIViewController alloc] initWithNibName:#"ShareOptionsViewController" bundle:nil];
popoverViewController.contentSizeForViewInPopover = CGSizeMake(319, 422);
self.popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverViewController];
[self.popoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}
This code works and when I tap the correct button, the popover is showed. The problem is when the popOver is showed, if I tap again the button, the app crashes. I know is because it is trying to alloc a variable that is already created. I think the solution should be disable the button when the popover loads but I don't know how to activate again once the popover is dismissed.
Am I right? How can I enable the button when the popOver is dismissed?
First you should disabled the button in your shareButtonPressed method.
-(IBAction)shareButtonPressed :(id)sender{
UIViewController *popoverViewController = [[UIViewController alloc] initWithNibName:#"ShareOptionsViewController" bundle:nil];
popoverViewController.contentSizeForViewInPopover = CGSizeMake(319, 422);
self.popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverViewController];
[self.myButton setEnabled:NO];
[self.popoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}
Then you make your view controller comply with the UIPopoverControllerDelegate protocol. After that you implement the popoverControllerDidDismissPopover method.
-(void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
if (!self.myButton.enabled) // Just to make sure the button is disabled.
[self.myButton setEnabled:YES];
}
Just check if instance is already created.
-(IBAction)shareButtonPressed:(id)sender
{
if (self.popoverController == nil) {
self.popoverController = alloc/init...;
[_popoverController present...];
} else {
if (self.popoverController.popoverVisible)
[self.popoverController dismissPopoverAnimated:YES];
self.popoverController = nil;
}
}
There is a possibility, it will be garbage collected before animation completes, yielding invalid user experience. Then, if possible, I would pre-create the instance in viewDidLoad or contructor or so and then just present and dismiss as needed, checking visible property.
Look at this "event --> responsible" list:
1) Button pressed --> MainViewController - IBAction
2) Popover shown --> MainViewController - IBAction
3) Button disabled --> MainViewController - IBAction
4) Popover dissmis --> popoverDelegate - popoverControllerDidDismissPopover
5) Button enabled --> popoverDelegate - popoverControllerDidDismissPopover
So, if the delegate of your popover is the MainViewController, you would have access to the button, if it is a different class you'll probably have to set up a protocol in order to enable the button back.
Tell me if you need any further explanations...
Just modify your code like this...
-(IBAction)shareButtonPressed :(id)sender{
UIViewController *popoverViewController = [[UIViewController alloc] initWithNibName:#"ShareOptionsViewController" bundle:nil];
popoverViewController.contentSizeForViewInPopover = CGSizeMake(319, 422);
if(self.popoverController)
{
[self.popovercontroller dismissPopoverAnimated:YES];
self.popovercontroller = nil;
}
self.popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverViewController];
[self.popoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}
you can detect if the popover is shown and dismissed:
- (IBAction)shareButtonPressed :(id)sender{
if (self.popoverController) {
[self.popoverController dismissPopoverAnimated:YES];
self.popoverController = nil;
} else {
UIViewController *popoverViewController = [[UIViewController alloc] initWithNibName:#"ShareOptionsViewController" bundle:nil];
popoverViewController.contentSizeForViewInPopover = CGSizeMake(319, 422);
self.popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverViewController];
[self.popoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}
}
or if you just want to disable the button:
- (IBAction)shareButtonPressed :(id)sender{
...
UIButton *button = (UIButton *)sender;
button.enabled = NO;
}
but in this case you will have to detect when the user close the popOver to enable the button again. You can do this adopting the UIPopoverController delegate (see documentation) in your class

How to display a popover programmatically from a uibutton that is also created programmatically (Not using interface builder)

I have a button I have created programmatically within a view controller. Once the button is pressed I want it to to use a method to create the popover programmatically.
The button which is created in the ViewDidLoad in my view controller.m
UIView *moreFundInfoView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 540, 620)];
[self.view addSubview:moreFundInfoView];
[moreFundInfoView setBackgroundColor:[UIColor RMBColor:#"b"]];
btnContact = [UIButton buttonWithType:(UIButtonTypeRoundedRect)];
[btnContact setFrame:CGRectMake(390, 575, contactButton.width, contactButton.height)];
btnContact.hidden = NO;
[btnContact setTitle:#"Contact" forState:(UIControlStateNormal)];
[moreFundInfoView addSubview:btnContact];
[btnContact addTarget:self action:#selector(showContactDetails:) forControlEvents:UIControlEventTouchUpInside];
Then I have the method I use when the button is pressed.
-(void) showContactDetails: (id) sender
{
UIViewController *popoverContent = [[UIViewController alloc]init];
UIView *popoverView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 200, 300)];
[popoverView setBackgroundColor:[UIColor RMBColor:#"b"]];
popoverContent.view = popoverView;
popoverContent.contentSizeForViewInPopover = CGSizeMake(200, 300);
UIPopoverController *contactPopover =[[UIPopoverController alloc] initWithContentViewController:popoverContent];
[contactPopover presentPopoverFromRect:btnContact.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES ];
[contactPopover setDelegate:self];
}
What am I missing here? Cause it runs fine, but as soon as I click the button the app crashes. I think it is a delegate issue, but I am not sure. Any advice would be appreciated.
I think this code will help you. You are certainly missing delegate methods
ViewController *viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:navigationController];
popover.delegate = self;
popover.popoverContentSize = CGSizeMake(644, 425); //your custom size.
[popover presentPopoverFromRect:button.frame inView:self.view permittedArrowDirections: UIPopoverArrowDirectionLeft | UIPopoverArrowDirectionUp animated:YES];
Just make sure you are not forgetting UIPopover Delegate methods or else application will definitely crash. It is mandatory.
UIViewController *controller = [[UIViewController alloc] init];
[view removeFromSuperview]; //view is a view which is displayed in a popover
controller.view = view;
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:controller];
popover.delegate = self;
[popover presentPopoverFromRect:button.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
All I had to do was change the property from "retain" to "strong" in the .h file and it works, stopped the app from crashing.
Yes, changing property for "retain" to "strong" makes you to hold your picker view object.
I think the problem with your code was, UIPopoverController object gets deallocated automatically when method finishes.
making strong property gets you to strongly point an object.

How to dismiss a popover with a navigation controller?

I have an app which is divided in four views, when I tap a view the app shows a popover and that popover has a navigation controller. I can navigate between view and the data is shows correctly. But when I am in the last view and I tap a row, the popover don't dismiss (I want in didSelectRowAtIndexPath).
How could do it?
I tried in this way, I created a method that dismiss the popover and I call this method en didSelectRowAtIndexPath, but did not works.
This is the method in the main ViewController
-(IBAction)mostrarTabla:(id)sender
{
// Popover that shows the table
UIPopoverController *popover;
// RootViewController is the first view
RootViewController *rootViewController = [[RootViewController alloc] init];
// With a nav bar
UINavigationController *navBar = [[UINavigationController alloc] initWithRootViewController:rootViewController];
[rootViewController release];
// Popover customitation
navBar.contentSizeForViewInPopover = CGSizeMake(20.0f, 20.0f);
popover = [[UIPopoverController alloc] initWithContentViewController:navBar];
[navBar release];
popover.delegate = self;
popover.popoverContentSize = CGSizeMake(320.0f, 832.0f);
// PopOver is shows in the view 1
[popover presentPopoverFromRect:CGRectMake(100.0f, 10.0f, 20.0f, 20.0f) inView:_view1 permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
I create a method in this View Controller
-(void)hidePopover
{
[self.popOver dismissPopoverAnimated:YES];
}
And in the last view I used the method but did not works
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ViewController *grafica = [[ViewController alloc] init];
self.indicadorId = [[self.arrayId objectAtIndex:indexPath.row] integerValue];
DataIndicador *datos =[[DataIndicador alloc] init];
datos.idIndicador = self.indicadorId;
[datos release];
[grafica hidePopover];
}
In the last view I want that the popover returns to its view and shows the data (a chart)
Thanks
The grafica instant of the ViewController is not the same as the whatever instant of VieController that was originally presented the popover.

Navigation Controller in popover

I am a total newbie in the ipad app development.
I am interested in creating an application where i have a popover showing a tableview with list of items. Then i select an item and the view drills to another tableview consisting of another list of items and a navigate back button as well.
the level to which i would be drilling down is dynamic.
Please guide me with appropriate resources to help me solve the problem.
I did it before and it works well!
Just assign this function to your button (perhaps an UIBarButtonItem):
UIPopoverController *popover;
bool isPopoverOpen = false;
-(void)openPopover{
if(!isPopoverOpen){
FirstViewController *firstViewCtrl = [[PartsViewCtrl alloc] init];
UINavigationController *navbar = [[UINavigationController alloc] initWithRootViewController:firstViewCtrl];
[firstViewCtrl release];
navbar.contentSizeForViewInPopover = CGSizeMake(TABLE_WIDTH, TABLE_HEIGHT);
popover = [[UIPopoverController alloc] initWithContentViewController:navbar];
[navbar release];
popover.delegate = self;
popover.popoverContentSize = CGSizeMake(TABLE_WIDTH, TABLE_HEIGHT);
[popoverOnPartsView presentPopoverFromBarButtonItem:barButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
isPopoverOpen = true;
}else{
[popover dismissPopoverAnimated:YES];
[popover release];
isPopoverOpen = false;
}
}
And implement this function to FirstViewController which has an UITableView:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
SecondViewController *secondViewController = [[SecondViewController alloc] init];
[self.navigationController pushViewController:secondViewController animated:YES];
[secondViewController release];
}
Now you can add an UITableView to SecondViewController, Too. And use this scenario for other tables!
I hope it works for you!
When you create the popover, you just need to allocate a UINavigationController and use this to manage the view hierarchy within the popover itself. A quick web search revealed this tutorial which covers the things you need to know.
I also meant to add that you should get up to speed with Objective-C and iOS development in general. Don't try and blindly use things you've found on the net without understanding what you're actually doing :)
Do the following steps
1)In the action of button (by clicking on that button pop over should appear) write the code
[here PopOverContentViewController is a viewController where i have table view and several list of items which should be displayed when the pop over arrives]
- (IBAction)callPopOver:(id)sender
{
UIButton *button = (UIButton*)sender;
PopOverContentViewController1 *popOverContent = [[PopOverContentViewController1 alloc]initWithNibName:#"PopOverContentViewController1" bundle:nil];
UINavigationController *navbar = [[UINavigationController alloc] initWithRootViewController:popOverContent];
navbar.contentSizeForViewInPopover = CGSizeMake(266, 200);
popover = [[UIPopoverController alloc] initWithContentViewController:navbar];
popover.delegate = self;
[popover presentPopoverFromRect:CGRectMake(button.frame.size.width / 2, button.frame.size.height / 1, 1, 1) inView:button permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
[popover setPopoverContentSize:CGSizeMake(266, 200) animated:YES];
[popUpContent release];
}
2)now to change the table view on clicking on any of the rows type this code in PopOverViewController.m
[here PopOverViewController2 is the ViewController where we have the next table view to be displayed]
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
PopOverViewController2 *secondViewController = [[PopOverViewController2 alloc] init];
[self.navigationController pushViewController:secondViewController animated:YES];
[secondViewController release];
}
3)to avoid the change in size of popover while navigation write the following code in viewDidLoad of both view controllers (ie PopOverContentViewController1 and PopOverContentViewController2)
- (void)viewDidLoad
{
[super viewDidLoad];
[self setContentSizeForViewInPopover:CGSizeMake(266, 200)];
}

Resources