Search code examples
iphoneiosipaduipopovercontrolleruiactionsheet

UIActionSheet with 20+ options inside UIPopoverController


In a universal app, I have a UIView which is presented inside an iPhone-Screen-Sized popover controller on the iPad, and a Modal View Controller on the iPhone/Touch.

That UIView, if a user taps a particular button, presents a UIActionSheet with 20+ entries. On the iPhone, the action sheet fills the screen, with a scrollable "wheel"-style list of options. However, on the iPad, the behavior is out of whack: The action sheet presents itself as the wheel-style list again, but extremely tall; it expands the popover to the maximum possible height, but the action sheet is still taller than the popover. So what you wind up with is a ridiculously tall popover with a wheel-style list that's partially cut off at the top, with no way to reach the items at the top of the list. See the images below.

Anyway: I'd ideally like precisely the same thing you see in the iPhone to show up inside that popover, without changing the size of the popover. Is that doable?

Images: iPhone version after presenting action sheet:

iPhone version after presenting action sheet

iPad version showing original UIview with button to press:

iPad version showing original UIview with button to press

iPad version after pressing button, showing actionsheet:

iPad version after pressing button, showing actionsheet


Solution

  • EDIT: I've put together a more complete solution here https://github.com/brandonschlenker/BSActionSheet

    This is a bit of a pain and honestly, I'm not sure if it's worth the effort. If Apple ever changes UIActionSheet all of this will likely break. Anyway, here's what could be a solution...

    You're going to need to subclass UIActionSheet and resize all of it's subviews and superviews according.

    In layout subviews, we're going to iterate through the views and begin resizing the elements to fit our needs. I started by just logging out the list of subviews to find which element was which. So far, I've found the table view, the table views frame that rounds the corners, the popover, etc. You'll still need to go through and find the cancel button and move that around as well.

    - (void)layoutSubviews
    {
        CGRect tableViewFrame = CGRectZero;
        for (id item in [self subviews])
        {
            if ([item isKindOfClass:[UIView class]])
            {
                UIView *v = (UIView *)item;
                v.frame = CGRectMake(v.frame.origin.x, v.frame.origin.y, self.frame.size.width, 320);
    
                for (UIView *subItem in [v subviews])
                {
                    //table view of options
                   if ([subItem isKindOfClass:[UITableView class]])
                   {
                       subItem.frame = CGRectMake(subItem.frame.origin.x, subItem.frame.origin.y, subItem.frame.size.width, someNewHeightHere);
                       tableViewFrame = subItem.frame;
                   }
    
                    //table view image frame. this rounds the bottom corners
                    if ([subItem isKindOfClass:[UIView class]] && subItem.tag == 3334)
                    {
                        subItem.frame = CGRectMake(subItem.frame.origin.x, tableViewFrame.origin.y, subItem.frame.size.width, someNewHeightHere);
                    }
    
                    //this is the cancel buttons label. never could get this to do anything
                    if ([subItem isKindOfClass:NSClassFromString(@"UIButtonLabel")])
                    {
                            if (subItem.frame.size.width == 66)
                            {
    
                            }
                    }
                }
            }
        }
    
        UIViewController *v = [self viewController];
        v.contentSizeForViewInPopover = CGSizeMake(self.frame.size.width, someNewHeightHere);    
    }
    

    I also have a convenience method to grab the views parent view controller.

      - (UIViewController *)viewController 
        {
            UIResponder *responder = self;
            while (![responder isKindOfClass:[UIViewController class]]) {
                responder = [responder nextResponder];
                if (nil == responder) {
                    break;
                }
            }
            return (UIViewController *)responder;
        }