UIPickerView inside a UIPopoverController memory leak - ios

I'm relatively new to iOS programing so I'll do my best to describe the problem I'm having. If I can provide some additional info to clear things up please tell me.
I'm currently displaying a UIPickerView within a UIPopoverController within my app. The problem I'm having is when I'm profiling with the leaks tool, every time the popover is dismissed a memory leak pops up. Here is the code for initializing the popover and the picker view.
// Show the popover
CGRect cellRect = [tableView rectForRowAtIndexPath:indexPath];
UIViewController *vc = [[UIViewController alloc] init];
[vc setContentSizeForViewInPopover:CGSizeMake(320, 216)];
UIPickerView *pv = [[UIPickerView alloc] init];
[pv setShowsSelectionIndicator:YES];
[pv setDataSource:self];
[pv setDelegate:self];
[[vc view] addSubview:pv];
inputPopoverController = [[UIPopoverController alloc] initWithContentViewController:vc];
[inputPopoverController setDelegate:self];
[pv selectRow:selection inComponent:0 animated:NO];
vc = nil;
pv = nil;
[inputPopoverController presentPopoverFromRect:cellRect inView:[self view] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
inputPopoverController is declared as
UIPopoverController *inputPopoverController;
And here's where I release the popover controller and I've confirmed that the function does get called every time the popover is dismissed.
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
inputPopoverController = nil;}
Instruments shows the leak at the following two locations
[pv selectRow:selection inComponent:0 animated:NO];
and
[inputPopoverController presentPopoverFromRect:cellRect inView:[self view] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
Thanks in advance.

After a couple days of breaking my head, it turns out I needed to implement one of the UIPickerView delegate methods. Here's where I found my answer here

Related

How to display a UIPopover from another class?

I have a iPad app (XCode 5, iOS 6, ARC and Storyboards), which has a class to display a UIView. From a separate class, I want to display a UIPopover under a certain condition.
I'm having a problem with the last line of code:
// create a popover for login or registration
UIViewController* popoverContent = [[UIViewController alloc]init];
UIView* popoverView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 280, 180)];
popoverView.backgroundColor = [UIColor lightGrayColor];
popoverContent.view = popoverView;
//resize the popover view shown in the current view to the view's size
popoverContent.contentSizeForViewInPopover = CGSizeMake(320, 280); // was 180
//create a popover controller
popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverContent];
// if previous popoverController is still visible... dismiss it
if ([popoverController isPopoverVisible]) {
[popoverController dismissPopoverAnimated:YES];
}
// [popoverController presentPopoverFromRect:popoverView inView:self
// permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
SettingsViewController *svc = [[SettingsViewController alloc]init];
[popoverController presentPopoverFromRect: popoverView inView:svc.view.frame
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
It's telling me:
Sending 'UIView *__strong' to parameter of incompatible type 'CGRect' (aka 'struct CGRect')
How do I fix this? (I've tried different ways of presenting the popover, but none of them work).
Assuming the popoverView is the section of the svc view you want the popover to present from, your code should read:
[popoverController presentPopoverFromRect:popoverView.frame inView:svc.view
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
This will correct that error.
Edit: The reason it's probably not displaying for you is because you seem to be misunderstanding the presentPopoverFromRect method. The frame you indicate in the presentPopoverFromRect method needs to correspond the point of the view you'd like the popover to emerge from. For example, to have the popover present from the top left corner of the view, do something like this:
[popoverController presentPopoverFromRect:CGRectMake(0, 0, 1, 1) inView:svc.view
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

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

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.

Releasing a popover which takes great amount of memory

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.

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