Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dismiss popover when iPad app goes in background

Hi I am working on a iPad app and got a requirement to dismiss all popovers (if any) when app goes in background.

I did some study online and didn't find a simple way to do it. I'd like to share some my idea here and see if there are a better way to do it.

1, Dismiss popovers in didEnterBakcground in delegate. Seems not practical since we have to add all popovers reference in.

2, Go through all views recursively in current window to find popover view by (class = _UIPopoverView). It is seems a bit hacky and dangerous.

3, Set up UIApplicationDidEnterBackgroundNotificationgroundNotification in each object who own popovers and dismiss them. This seems reasonable, but really troublesome if there are hundreds of popovers in your app.

4, How about add a category method say -(void)dismissWhenAppWillEnterBackground; and register notification.

Or there is easier way to do it?

like image 250
ThinkChris Avatar asked Jul 05 '13 00:07

ThinkChris


4 Answers

Here is a drop-in category on UIPopoverController that does what you're asking.

Basically the category swizzles initWithContentViewController: so that it can track live UIPopoverController instances in a NSHashTable (which doesn't itself hold the contained UIPopoverControllers alive since it keeps weak references to them.) It also monitors for UIApplicationDidEnterBackgroundNotification, and when this arrives it iterates the collection of live UIPopoverControllers and dismisses any that are showing.

It might be nice to extend this to implement the "never allow two popovers to show at once" rule that Apple has.

I'm not a huge fan of method swizzling in production apps but this seems pretty safe.

No special instructions for use. Just include the category in your project and use your UIPopoverControllers normally.

#import <objc/runtime.h>

@interface UIPopoverController (autodismiss)
@end

@implementation UIPopoverController (autodismiss)

static NSHashTable* ts_popoverHashTable;

+ (void) load
{
    SEL originalSelector = @selector(initWithContentViewController:);
    SEL replacementSelector = @selector(ts_initWithContentViewController:);
    Method originalMethod = class_getInstanceMethod( [UIPopoverController class], originalSelector);
    Method replacementMethod = class_getInstanceMethod( [UIPopoverController class], replacementSelector);
    method_exchangeImplementations(originalMethod, replacementMethod);

    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector( applicationDidEnterBackgroundNotification: )
                                                 name: UIApplicationDidEnterBackgroundNotification
                                               object: nil];
}

- (id) ts_initWithContentViewController: (UIViewController*) contentViewController
{
    UIPopoverController* pc = [self ts_initWithContentViewController: contentViewController];

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        ts_popoverHashTable = [NSHashTable weakObjectsHashTable];
    });

    [ts_popoverHashTable addObject: pc];

    return pc;
}

+ (void) applicationDidEnterBackgroundNotification: (NSNotification*) n
{
    for ( UIPopoverController* pc in ts_popoverHashTable )
    {
        if ( pc.isPopoverVisible )
        {
            [pc dismissPopoverAnimated: NO];
        }
    }
}

@end
like image 81
TomSwift Avatar answered Nov 12 '22 14:11

TomSwift


I may have a better answer, which is add a category method -(void)dismissWhenAppWillEnterBackground to UIPopoverController and register UIApplicationWillEnterBackgroundNotificationgroundNotification.

like image 30
ThinkChris Avatar answered Nov 12 '22 12:11

ThinkChris


Write a protocol with a couple of optional methods:

- (void)appWillEnterBackground;
- (void)appWillBecomeActive;

Make your view controllers to implement it and then in your app delegate, access your root view controller, check if it responds to those methods and invoke them when the app is going to background and becoming active. You should be able to obtain the root view controller easily. If you have a hierarchy of view controllers you may need to forward the call.

Add your popover dismissal code in appWillEnterBackground, for instance.

like image 25
djromero Avatar answered Nov 12 '22 12:11

djromero


  1. Create a uiviewcontroller base class for all the view controllers in the application.
  2. Add an array which contains the references to the popover views in the particular viewcontroller
  3. Maintain a reference to the current viewcontroller iin app delegate .
  4. When app enters in background, get the current viewcontroller and travers the popover array and dismiss all the popovers.
like image 38
Govind Neelamana Avatar answered Nov 12 '22 12:11

Govind Neelamana