26

I have a UIViewController that contains a UITableView. This UIViewController is being displayed in a UIPopoverController.

Now, the things is that the number of items in the tableView is not constant, and I want the size of the popover (that is - the popoverContentSize), to adjust according to the number of items in the tableView

Naively, I was thinking that if I'll set the contentSizeForViewInPopover in viewDidLoad after I'm loading the tableView with all the items - It'll do it.

It didn't.

So the make it short, my question is: How can I change the popoverContentSize directly from the contentViewController - after it's been presented?

Appendix: enter image description here

Avi Shukron
  • 6,088
  • 8
  • 50
  • 84

8 Answers8

47

I might be very late to answer but for new user from iOS 7 please use the following line in your UIViewController i,e contentViewController of your UIPopOverViewConotroller

-(void) viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    self.preferredContentSize=myTableView.contentSize;
}

Hope this will help for iOS 7 user.

Pawan Rai
  • 3,434
  • 4
  • 32
  • 42
Avinash
  • 4,304
  • 1
  • 23
  • 18
  • 9
    this may work better in ViewWillAppear, as it happens before the view appears. – nh32rg Mar 14 '14 at 18:11
  • 1
    if you are using navigationController inside popover and problem is occurring even after setting preferredContentSize. Try [self.parentPopOverController setPopoverContentSize:CGSizeMake(250, 400) animated:YES]; before pushing the UIViewController – Hassy May 24 '14 at 08:15
  • 1
    Accepted your answer because it more up-to-date. Thanks! – Avi Shukron Oct 08 '14 at 20:08
  • Check out @Robert's answer below - nice and clean – amergin Nov 17 '14 at 08:51
  • 1
    You might also need to [myTableView sizeToFit]; If the table has a default size. And this too should go in the viewWillAppear rather than viewDidAppear method. – kvn Jan 15 '15 at 17:08
  • 1
    On iOS 8 this has to go into `viewDidAppear` because self.tableView isn't properly laid out yet when `viewWillAppear` is being called, i.e. it's height is zero. – Mojo66 Jul 27 '15 at 20:29
9

Well, In the end i did something that I'm not sure if it's the right thing to do, but it is working.

I added a reference in my contentViewController to the popoverController:

@property (nonatomic , assign) UIPopoverController *popoverControllerContainer;

Then, I added the resizing code to viewWillAppear and viewDidAppear:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView reloadData];
}

-(void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    self.contentSizeForViewInPopover = self.tableView.contentSize;
}

-(void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self.popoverControllerContainer setPopoverContentSize:self.contentSizeForViewInPopover animated:YES];
}

So, keeping a reference to the popover is kind of hack-ish, so I'm open to hear better ideas.

Avi Shukron
  • 6,088
  • 8
  • 50
  • 84
  • I use the same mechanism. I am adding method `createPopover` to the UIViewControllers which are used as popover contents. The methods creates a popover from `self` and then saves the popover instance for further access. – Sulthan Dec 15 '11 at 09:34
  • Hello @Avraham Your answer was the one i used and it is working great. I have a question about your solution: why did you set the contentsize of the view in the will appear method and the popover size in the did appear? Why can't it be done in just one of the two methods, because i tried and it didn't work? – Julian Osorio Oct 18 '13 at 16:26
7

A UIViewController class has the property

self.contentSizeForViewInPopover

which will resize the pop over w/out needing to adding a reference to it.

And to expand on a solution, i used the method rectForSection: to get the size of the section (mine only has 1 section, so easy enough to get) and then added the height of the navigation bar (it seems to be 20). so i'm able to create the popover the size of the finished table view:

CGRect sectionRect = [view.tableView rectForSection:0];

if (sectionRect.size.height + 20 < POPOVER_SIZE.height)
    view.contentSizeForViewInPopover = CGSizeMake(POPOVER_SIZE.width, sectionRect.size.height + 20);
else
    view.contentSizeForViewInPopover = POPOVER_SIZE;

might prove more difficult with multiple sections, i didn't try it. should just be able to sum up the section heights, but there might be some spacing issues that i don't know about.

Padin215
  • 7,444
  • 13
  • 65
  • 103
7

For iOS 7 or above.

- (CGSize)preferredContentSize {
    return CGSizeMake(320, 550);
}

If you are a child of a container, re-direct the content size to yourself. E.g. In a UINavigationController subclass:

- (CGSize)preferredContentSize {
    return self.topViewController.preferredContentSize;
}
Robert
  • 37,670
  • 37
  • 171
  • 213
  • Just a note for others that try Robert's approach. It's very clean and works for this question. It may not work for you however, if you're presenting a UITableView inside a UINavigationController. Avinash's seems to work in that case. – DenVog Nov 25 '14 at 00:35
  • @DenVog - Good spot. I would still prefer to subclass `UINavigationController` to get its preferred status bar from it's topViewController. Generally I find the approach of declaring what you require better than setting a value as it prevents bugs where 2 different classes can trample over the same property with different values which can lead to different results depending on the order you do things. This is similar to why `preferredStatusBarStyle` was created in iOS 7. – Robert Nov 25 '14 at 10:35
  • This was the only solution that worked for me. Setting self.preferredContentSize had no effect. – pls May 15 '15 at 09:30
2

This should do the trick

override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()

  self.preferredContentSize = CGSizeMake(0, self.tableView.contentSize.height)}
user431791
  • 341
  • 2
  • 17
1

My app has a menu with sub-menus originating from a popover and embedded in a navigation controller, i.e. something like UINavigationController -> TopMenuViewController -> SubMenuViewControllerxN.

None of the above solutions worked for me.

My solution: In my root menu view controller class, from which all other menu view controllers inherit, add the following code:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationController.preferredContentSize  = CGSizeMake(450.0f, 0.0f);// increase standard width (320.0)
}


- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.navigationController.preferredContentSize = self.tableView.contentSize;
}
Mojo66
  • 1,109
  • 12
  • 21
0

I was having this same problem and none of the answers I found worked. I cobbled together that I think works well.

I have an array in my initWithCoder that holds my values. I simply run this code:

- (id)initWithCoder:(NSCoder *)aDecoder
{
//Popover Choices

_importantChoices = [NSMutableArray array];
[_importantChoices addObject:@"Bakery"];
[_importantChoices addObject:@"Beverage Dry Mix"];
[_importantChoices addObject:@"Beverage RTD"];
[_importantChoices addObject:@"Crisps"];
[_importantChoices addObject:@"Meat"];
[_importantChoices addObject:@"Supplements"];


//Calculate size of Popover
self.clearsSelectionOnViewWillAppear = NO;
NSInteger rowsCount = [_importantChoices count];
NSInteger singleRowHeight = [self.tableView.delegate tableView:self.tableView
                                       heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
NSInteger totalRowsHeight = rowsCount * singleRowHeight;
CGFloat largestLabelWidth = 0;
for (NSString *tmp in _importantChoices) {
    CGSize labelSize = [tmp sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:20.0f]}];
    if (labelSize.width > largestLabelWidth) {
        largestLabelWidth = labelSize.width;
    }
}
CGFloat popoverWidth = largestLabelWidth + 100;     //Add a little padding to the width
self.preferredContentSize = CGSizeMake(popoverWidth, totalRowsHeight);
return self;
}
Bryan Schmiedeler
  • 2,977
  • 6
  • 35
  • 74
0

iOS8/9 solution - just override preferredContentSize and force the table view to layout prior to returning it's contentSize.

- (CGSize)preferredContentSize {
    [self.tableView layoutIfNeeded];
    return self.tableView.contentSize;
}
capikaw
  • 12,232
  • 2
  • 43
  • 46
  • this grows the view but never shrinks it if embedded in a `UINavigationController` hierarchy. – Mojo66 Mar 10 '16 at 18:53