Dismiss popover when iPad app goes in background
ios, ipad, popover, uipopovercontroller
Solution
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
Problem
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?