iOS Objective-C Get Reference on Currently Displayed UIAlertController - ios

I wanted to get a reference on the currently displayed UIAlertController.
(Some guys thought my question sucks and close voted it. However they were wrong, as there IS an answer to the question and I will post it, so others interested in this can find it!)

I've made a singleton class with one weak reference, then I've extended UIAlertController and made a new method to present it, where I set this weak reference to the newly displayed alert. Now this will hold the reference of the alert as long as it has any other reference, thus while it is displayed.
UIAlertController+Extension.h
#interface UIAlertController(Extension)
- (UIAlertController*)showIn:(UIViewController*)viewController;
+ (UIAlertController*)lastOnScreenAlert;
#end
UIAlertController+Extension.m
#interface LastAlert : NSObject
#property (nonatomic, weak) UIAlertController* reference;
#end
static LastAlert* lastAlert;
#implementation LastAlert
+ (void)initialize
{
[super initialize];
lastAlert = [LastAlert new];
}
#end
#implementation UIAlertController(Extension)
- (UIAlertController*)showIn:(UIViewController*)viewController
{
[viewController presentViewController:self animated:YES completion:nil];
[LastAlert class];
lastAlert.reference = self;
return self;
}
+ (UIAlertController*)lastOnScreenAlert
{
return lastAlert.reference;
}
#end

There is a common solution to get the current alert controller that supports multiple windows and can work with third party libraries which present their own alerts:
static UIAlertController* currentAlertController() {
for (UIWindow* window in UIApplication.sharedApplication.windows) {
UIViewController* presented = window.rootViewController.presentedViewController;
while (presented != nil) {
if ([presented isKindOfClass:UIAlertController.class]) {
return (UIAlertController *)presented;
}
presented = presented.presentedViewController;
}
}
return nil;
}

Related

SFSafariViewControllerDelegate methods not called when assigning delegate to something other than view controller

I have a simple view controller that presents a SFSafariViewController when a link button is tapped.
#import "ViewController.h"
#import "SafariDelegate.h"
#interface ViewController ()
#property(nonatomic, strong) NSString *url;
#property(nonatomic, weak) SafariDelegate *safariDelegate;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.url = #"http://www.google.com";
}
- (IBAction)linkTapped:(id)sender {
SFSafariViewController *vc = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:self.url]];
SafariDelegate *safariDelegate = [[SafariDelegate alloc] init];
vc.delegate = safariDelegate;
[self presentViewController:vc animated:YES completion:nil];
}
#end
SafariDelegate conforms to SFSafariViewControllerDelegate and just logs when the delegate methods fire.
SafariDelegate.h
#import <Foundation/Foundation.h>
#import <SafariServices/SafariServices.h>
#interface SafariDelegate : NSObject <SFSafariViewControllerDelegate>
#end
SafariDelegate.m
#import "SafariDelegate.h"
#implementation SafariDelegate
- (NSArray<UIActivity *> *)safariViewController:(SFSafariViewController *)controller activityItemsForURL:(NSURL *)URL title:(nullable NSString *)title
{
NSLog(#"safariViewController activityItemsForURL");
return #[];
}
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller
{
NSLog(#"safariViewControllerDidFinish");
}
- (void)safariViewController:(SFSafariViewController *)controller didCompleteInitialLoad:(BOOL)didLoadSuccessfully
{
NSLog(#"safariViewController didCompleteInitialLoad");
}
#end
When I tap the button, the safariviewcontroller loads the page up properly but the delegate methods are never called (including when I hit 'Done'). I stuck a break point at the line where the 'vc' is presented and confirmed that the delegate is properly set at that point. What am I missing?
SafariViewController doesn't retain its delegate, so it gets deallocated immediately after your linkTapped: method returns since nothing else has a strong reference to it.
You need to keep a strong reference to it somewhere. You could probably just change the property you have in your current view controller to strong and store it there. You could also keep it in an associated object on the safari view controller.
This same problem was occur for me too.
I have created "SafariViewController inside of function". So, SafariViewController memory was cleared. So, I can't able to get trigger from SafariViewControllerDelegate.
Finally I found a solution this problem.
When ever You create a SFSafariViewController in a class. You must declare it as a global variable.
class Foo: NSObject {
var safariVC: SFSafariViewController!
}
then you can assign your SafariViewController.
func config(_ loadUrl: URL) {
if #available(iOS 11.0, *) {
let config = SFSafariViewController.Configuration()
config.barCollapsingEnabled = true
config.entersReaderIfAvailable = true
safariVC = SFSafariViewController(url: loadUrl, configuration: config)
} else {
// Fallback on earlier versions
safariVC = SFSafariViewController(url: loadUrl)
}
}
then set a delegate
self.safariVC.delegate = self
finally present safariViewController.
UIApplication.shared.keyWindow?.rootViewController?.present(safariVC, animated: true, completion: nil)
Now surely safariViewController delegate will be trigger.
// MARK: - SFSafariViewControllerDelegate
extension Foo: SFSafariViewControllerDelegate {
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
print("SafariVC is dismissed")
}
}

How do I prevent UIPopoverController passthroughViews from being reset after orientation change when presented from a UIBarButtonItem?

I have a UIPopoverController which is being presented from a UIBarButtonItem. I want touches outside of the popover to dismiss the popover. When presenting a popover from a bar button, the other bar buttons are automatically included in the popovers passthrough views. In order to prevent that I set the passthrough views to nil (or #[ ]) after presenting the popover, like so:
- (IBAction) consoleBarButtonHit:(id)sender {
UIViewController *consoleNavigationController=[self.storyboard instantiateViewControllerWithIdentifier:#"consoleNavigationController"];
_consolePopoverController=[[UIPopoverController alloc] initWithContentViewController:consoleNavigationController];
_consolePopoverController.delegate=self;
[_consolePopoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
// this must be done _after_ presenting the popover in order to work
_consolePopoverController.passthroughViews=nil;
}
That's all fine and good, but the problem that I'm having is that after rotating the device while the popover is visible the bar buttons are being automatically re-added as passthrough views and don't cause the popover to be dismissed.
If I could somehow get the bar buttons view (or rect) then I could present the popover using
-presentPopoverFromRect:inView:permittedArrowDirections:animated:
which would likely fix this problem, but I don't know of any non-hackish way of finding that rect/view from a UIBarButtonItem.
I really don't want the selectors called when the other bar buttons are hit to dismiss the popover programmatically, that's not their responsibility and will likely cause problems for me later.
Any ideas?
So I came up with a solution, that's a little odd, but keeps things modular, works well. I've created a class called PropertyEnforcer which registers itself as a KVO observer of an object's property and re-sets that property any time it changes.
PropertyEnforcer.h:
#import <Foundation/Foundation.h>
#interface PropertyEnforcer : NSObject
+ (void) enforceProperty:(NSString*)keyPath ofObject:(id)target toValue:(id)value;
#end
PropertyEnforcer.m:
#import "PropertyEnforcer.h"
#import <objc/runtime.h>
#interface PropertyEnforcer ()
#property (retain) NSString *keyPath;
#property (retain) id value;
#property (assign) id target;
#end
#implementation PropertyEnforcer
- (void) dealloc {
[_target removeObserver:self forKeyPath:_keyPath context:NULL];
[super dealloc];
}
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if( (([_target valueForKey:_keyPath] == nil) && (_value==nil)) || [[_target valueForKey:_keyPath] isEqual:_value]) {
return;
} else {
[_target setValue:_value forKeyPath:_keyPath];
}
}
+ (void) enforceProperty:(NSString*)keyPath ofObject:(id)target toValue:(id)value {
PropertyEnforcer *enforcer=[[PropertyEnforcer alloc] init];
enforcer.value=value;
enforcer.keyPath=keyPath;
enforcer.target=target;
[target addObserver:enforcer forKeyPath:keyPath options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:NULL];
objc_setAssociatedObject(target,
_cmd, // using this technique we can only attach one PropertyEnforcer per target
enforcer,
OBJC_ASSOCIATION_RETAIN);
[enforcer release];
}
#end
Now I can change the change the original code to:
- (IBAction) consoleBarButtonHit:(id)sender {
UIViewController *consoleNavigationController=[self.storyboard instantiateViewControllerWithIdentifier:#"consoleNavigationController"];
_consolePopoverController=[[UIPopoverController alloc] initWithContentViewController:consoleNavigationController];
_consolePopoverController.delegate=self;
[_consolePopoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
// make sure those passthroughViews are always nil !
[PropertyEnforcer enforceProperty:#"passthroughViews" ofObject:_consolePopoverController toValue:nil];
}
The PropertyEnforcer registers itself as an associated object so we don't ever have to keep track of it. It will automatically unregister itself as a KVO observer and be destroyed whenever the UIPopoverController is destroyed.
This is the best, least hackish, solution that I could come up with.
The solution I went for was to leave passthroughViews alone and instead disable/re-enable individual buttons (UIBarButtonItem instances) in a toolbar or a navigation bar when UIPopoverPresentationController is presented and dismissed, based on its transition.
(iOS 8: UIPopoverPresentationController instead of UIPopoverController.)
UIPopoverPresentationController+managedBarButtonItems.h
#interface UIPopoverPresentationController (managedBarButtonItems)
#property (nonatomic, retain) NSArray* managedBarButtonItems;
#end
UIPopoverPresentationController+managedBarButtonItems.m
#import "UIPopoverPresentationController+managedBarButtonItems.h"
#import <objc/runtime.h>
//
// scope: private, in-terms-of
//
#interface UIBarButtonItem (wasEnabled)
#property (nonatomic) BOOL wasEnabled;
#end
#implementation UIBarButtonItem (wasEnabled)
- (BOOL)wasEnabled
{
return [objc_getAssociatedObject(self, #selector(wasEnabled)) boolValue];
}
- (void)setWasEnabled:(BOOL)wasIt
{
objc_setAssociatedObject(self, #selector(wasEnabled), [NSNumber numberWithBool:wasIt], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// FYI: "Associated objects are released [automatically] after the dealloc method of the original object has finished."
#end
//
// scope: consumable
//
#implementation UIPopoverPresentationController (managedBarButtonItems)
- (NSArray*)managedBarButtonItems
{
return objc_getAssociatedObject(self, #selector(managedBarButtonItems));
}
- (void)setManagedBarButtonItems:(NSArray*)items
{
objc_setAssociatedObject(self, #selector(managedBarButtonItems), items, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// FYI: "Associated objects are released [automatically] after the dealloc method of the original object has finished."
- (void)presentationTransitionDidEnd:(BOOL)completed
{
[super presentationTransitionDidEnd:completed];
if (self.barButtonItem && self.managedBarButtonItems)
{
for (UIBarButtonItem* button in self.managedBarButtonItems)
{
if (button.action != /* actuator */ self.barButtonItem.action)
{
button.wasEnabled = button.enabled, button.enabled = NO;
}
}
}
}
- (void)dismissalTransitionDidEnd:(BOOL)completed
{
[super dismissalTransitionDidEnd:completed];
if (self.barButtonItem && self.managedBarButtonItems)
{
for (UIBarButtonItem* button in self.managedBarButtonItems)
{
if (button.action != /* actuator */ self.barButtonItem.action)
{
button.enabled = button.wasEnabled;
}
}
}
}
#end
Usage:
UIAlertController* actionSheet = [UIAlertController
alertControllerWithTitle:#"Actions" message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
UIPopoverPresentationController* presenter = actionSheet.popoverPresentationController;
// chosen anchor UIBarButtonItem
presenter.barButtonItem = anchorButton;
// disabled UIViewController buttons
presenter.managedBarButtonItems = self.toolbarItems;
Also possible:
// disabled UINavigationController buttons
presenter.managedBarButtonItems =
[[NSArray arrayWithArray:self.navigationItem.leftBarButtonItems]
arrayByAddingObject:self.navigationItem.rightBarButtonItem];

delegate = nil, transfer information between VCs issue

I just started to learn obj-c and I have question about delegates. I know that on SOF is a lot of similar threads, but I was looking for and really didn't get my issue (maybe cause I'm beginner). Here's my problem: I want to use my own delegate and transfer an information from SlaveClass to MainClass. In SlaveClass in buttonDidClick: action, I declare delegate which is equal to NIL. Even I don't know where I should start to looking for mistake. Thanks in advance for any type of advice. Here's my code which refer to delegate:
SlaveClass.h
#protocol slaveDelegate <NSObject>
-(void)transferNameDidClick:(NSString *)text;
#end
// --------------------------------------------------------------------------------
#interface SlaveClass : UIViewController
#property (nonatomic, strong) id <slaveDelegate> myOwnDelegate;
#end
SlaveClass.m (here appears NIL)
-(void)buttonDidClick:(id)sender
{
if ([_myOwnDelegate respondsToSelector:#selector(transferNameDidClick:)])
{
[_myOwnDelegate transferNameDidClick:(_textField.text)];
}
}
MainClass.h
#interface MainClass : UIViewController <slaveDelegate>
#end
MainClass.m
-(void)transferNameDidClick:(NSString *)text
{
SlaveClass *delegate = [[SlaveClass alloc] init];
[delegate setMyOwnDelegate:self];
[_label setText:text];
NSLog(#"text: %#",text);
}
You are setting your delegate in wrong place. You have to set the delegate before going to slaveClass
mainClass.m
Present slave view controller like this
SlaveClass *slaveClass = [[SlaveClass alloc] init]
[slaveClass setMyOwnDelegate:self];
[self presentViewController:slaveClas animated:YES completion:nil];
-(void)transferNameDidClick:(NSString *)text
{
// This is the method getting called by the slaveClass. So it should know the delegate to call this.
[_label setText:text];
NSLog(#"text: %#",text);
}
Set you delegate out side the Custom delegate method. you are mistakenly setting inside the custom delegate method thats y delegate show nil. use like this.
Main Class .M
-(IBAction)nextView
{
nextView = [[ViewController2 alloc]init];
nextView.myOwnDelegate = self;
[self presentViewController:nextView animated:YES completion:nil];
}
-(void)transferNameDidClick:(NSString *)text;
{
NSLog(#"Value from Slave Delegate %#",text);
}
SlaveClass.h
#protocol slaveDelegate <NSObject>
-(void)transferNameDidClick:(NSString *)text;
#end
// --------------------------------------------------------------------------------
#interface SlaveClass : UIViewController
#property (nonatomic, strong) id <slaveDelegate> myOwnDelegate;
#end
SlaveClass.m
-(void)buttonDidClick:(id)sender
{
if ([_myOwnDelegate respondsToSelector:#selector(transferNameDidClick:)])
{
[_myOwnDelegate transferNameDidClick:(_textField.text)];
}
}

How to make a single shared instance of iAd banner throughout many view controllers?

I have tried using a singleton class in my app delegate but I haven't been able to get that to work. I've also checked out the iAdSuite examples (particularly the containerBanner example because it seemed to be the most relative) but I can't figure it out. If there's a better way to accomplish this without using a singleton class and you can point me in the right direction I'd really appreciate it. Some of my singleton class code is below. Thank you!
#interface App Delegate
#property (assign) iAdController *iadc;
+ (AppDelegate*) sharedApplication;
- (iAdController*)sharedAd;
#end
#implementation AppDelegate
#synthesize iadc;
+ (AppDelegate*) sharedApplication
{
return [[UIApplication sharedApplication] delegate];
}
-(iAdController*)sharedAd
{
if(iadc==nil){
iadc=[iAdController new];
}
return iadc;
}
#interface ViewController
iAdController*iadc=[[AppDelegate sharedApplication] sharedAd];
//here i get an error saying, "initializer element is not a compile-time constant.
Everything is imported correctly. If there's anything else I should post let me know.
try changing your singleton creation to this:
+ (LocationManagerSingleton*)sharedInstance {
static LocationManagerSingleton *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init
{
self = [super init];
if (self != nil)
{
// PERFORM any custom initialization here
}
return self;
}
Obviously change the name of the class.
Whenever you want to use your singleton in any of your viewcontrollers just call it like this:
locationManager = [LocationManagerSingleton sharedInstance];
Dont forget to add
+ (LocationManagerSingleton*) sharedInstance;
on the header.
EDIT
well it seems i misunderstood your code (forget my answer, you simply want to be able to access your iAdController from everywhere. so just place
Add inside the .m of the ViewController
#interface ViewController()
{
iAdController *iadc;
}
And inside the
-(void)viewDidLoad
{
iadc=[[AppDelegate sharedApplication] sharedAd];
}
but import the app delegate.h on whichever viewcontroller you want to use it in.
#import "AppDelegate.h"
also there shouldnt be a space in the AppDelegate on the #interface

How can I find the UIPopoverController from the UIViewController being displayed in a popover?

Using an instance of a UIViewController, is there any way I can find the UIPopoverController being used to present it? I would also want to find the UIViewController that displayed the UIPopoverController in the first place.
I would normally use a delegate or other sort of notification to send a signal from the displayed view controller to the displaying one, but in this case I'm trying to create a reusable custom segue that dismisses the popover and then moves on to another view in the main view.
You would think that this would be simple (the UIViewController even has a private _popoverController property!), but it is not.
The general answer is that you have to save a reference to the UIPopoverController in the UIViewController that it is presenting, at the time the UIViewController is created.
If you are creating the UIPopoverController programmatically, then that's the time to store the reference in your UIViewController subclass.
If you are using Storyboards and Segues, you can get the UIPopoverController out of the segue in the prepareForSegue method:
UIPopoverController* popover = [(UIStoryboardPopoverSegue*)segue popoverController];
Of course, be sure that your segue really is a UIStoryboardPopoverSegue!
My recommendation is to leverage a combination of your own custom property and the private APIs in UIKit. To avoid app store rejection, any private APIs should compile out for release builds, and should be used only to check against your implementation.
First let's build the custom property into a category on UIViewController. This allows some perks in the implementation, and it doesn't require you to go back and derive every class from some custom view controller subclass.
// UIViewController+isPresentedInPopover.h
#import <UIKit/UIKit.h>
#interface UIViewController (isPresentedInPopover)
#property (assign, nonatomic, getter = isPresentedInPopover) BOOL presentedInPopover;
#end
Now for the implementation - we'll be using the Objective C runtime's associated object API to provide the storage for this property. Note that a selector is a nice choice for the unique key used to store the object, as it's automatically uniqued by the compiler and highly unlikely to be used by any other client for this purpose.
// UIViewController+isPresentedInPopover.m
#import "UIViewController+isPresentedInPopover.h"
#import <objc/runtime.h>
#implementation UIViewController (isPresentedInPopover)
- (void)setPresentedInPopover:(BOOL)presentedInPopover
{
objc_setAssociatedObject(self,
#selector(isPresentedInPopover),
[NSNumber numberWithBool:presentedInPopover],
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isPresentedInPopover
{
NSNumber *wrappedBool = objc_getAssociatedObject(self, #selector(isPresentedInPopover));
BOOL userValue = [wrappedBool boolValue];
return userValue ?: [[self parentViewController] isPresentedInPopover];
}
#end
So there's a convenient side effect of using this as a category - you can call up to the parentViewController and see if that is contained in a popover as well. This way you can set the property on, say, a UINavigationController and all of its child view controllers will respond correctly to isPresentedInPopover. To accomplish this with subclasses, you'd be either trying to set this on every new child view controller, or subclassing navigation controllers, or other horrific things.
More Runtime Magic
There is still more that the Objective C Runtime has to offer for this particular problem, and we can use them to jump into Apple's private implementation details and check your own app against it. For release builds, this extra code will compile out, so no need to worry about the all-seeing eye of Sauron Apple when submitting to the store.
You can see from UIViewController.h that there is an ivar defined as UIPopoverController* _popoverController with #package scope. Luckily this is only enforced by the compiler. Nothing is sacred as far as the runtime is concerned, and it's pretty easy to access that ivar from anywhere. We'll add a debug-only runtime check on each access of the property to make sure we're consistent.
// UIViewController+isPresentedInPopover.m
#import "UIViewController+isPresentedInPopover.h"
#import <objc/runtime.h>
#implementation UIViewController (isPresentedInPopover)
- (void)setPresentedInPopover:(BOOL)presentedInPopover
{
objc_setAssociatedObject(self,
#selector(isPresentedInPopover),
[NSNumber numberWithBool:presentedInPopover],
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isPresentedInPopover
{
NSNumber *wrappedBool = objc_getAssociatedObject(self, #selector(isPresentedInPopover));
BOOL userValue = [wrappedBool boolValue];
#if DEBUG
Ivar privatePopoverIvar = class_getInstanceVariable([UIViewController class], "_popoverController");
UIPopoverController *popover = object_getIvar(self, privatePopoverIvar);
BOOL privateAPIValue = popover != nil;
if (userValue != privateAPIValue) {
[NSException raise:NSInternalInconsistencyException format:
#"-[%# %#] "
"returning %# "
"while private UIViewController API suggests %#. "
"Did you forget to set 'presentedInPopover'?",
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
userValue ? #"YES" : #"NO",
privateAPIValue ? #"YES" : #"NO"];
}
#endif
return userValue ?: [[self parentViewController] isPresentedInPopover];
}
#end
When using the property incorrectly, you'll get a message like this on the console:
2012-09-18 14:28:30.375 MyApp[41551:c07] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Consistency error in -[UINavigationController isPresentedInPopover]: returning NO while private UIViewController API suggests YES. Did you forget to set 'presentedInPopover'?'
...but when compiling with the DEBUG flag off or set to 0, it compiles down to the exact same code as before.
For The Free and the Foolhardy
Maybe you're doing Ad-Hoc/Enterprise/personal builds, or you're sufficiently bold to see just what Apple thinks about this one for the App Store. Either way, here's an implementation that just works using the current runtime and UIViewController - no setting properties needed!
// UIViewController+isPresentedInPopover.h
#import <UIKit/UIKit.h>
#interface UIViewController (isPresentedInPopover)
#property (readonly, assign, nonatomic, getter = isPresentedInPopover) BOOL presentedInPopover;
#end
// UIViewController+isPresentedInPopover.m
#import "UIViewController+isPresentedInPopover.h"
#import <objc/runtime.h>
#implementation UIViewController (isPresentedInPopover)
- (BOOL)isPresentedInPopover
{
Ivar privatePopoverIvar = class_getInstanceVariable([UIViewController class], "_popoverController");
UIPopoverController *popover = object_getIvar(self, privatePopoverIvar);
BOOL privateAPIValue = popover != nil;
return privateAPIValue ?: [[self parentViewController] isPresentedInPopover];
}
#end
Most helpful would probably be to make popover a class variable, so in the .m file of the class that is going to present the popover, do something like this:
#interface ExampleViewController()
#property (nonatomic, strong) UIPopoverController *popover
#end
#implementation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"some segue"])
{
//prevent stacking popovers
if ([self.popover isPopoverVisible])
{
[self.popover dismissPopoverAnimated:YES];
self.popover = nil;
}
[segue.destinationViewController setDelegate:self];
self.popover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
}
#end
As #joey wrote above, Apple eliminated the need for the dummy control in iOS 8 with the popoverPresentationController property defined for UIViewController as the "The nearest ancestor in the view controller hierarchy that is a popover presentation controller. (read-only)".
Here is an example in Swift for a UIPopoverPresentationController-based segue defined on a storyboard. In this case, a button has been added programmatically and can be defined in this way as the pop-over's anchor. The sender could also be a selected UITableViewCell or a view from it.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showCallout" {
let button = sender as UIButton
let calloutViewController = segue.destinationViewController as CalloutViewController
if let popover = calloutViewController.popoverPresentationController {
popover.sourceView = button
popover.sourceRect = button.bounds
}
}
}
Taking off from ndoc's anwser: this answer shows a neater way in iOS 6 to prevent a popover from showing multiple time through segues. The method in the link was the one that worked awesomely for me for preventing popover stacking.
If you JUST want to know if your controller is being presented inside a popover (not interested to get a reference to the popover controller), you can simply do this, without storing variables nor hacking private API's.
-(BOOL)isPresentedInPopover
{
for (UIView *superview = self.view.superview; superview != nil; superview = superview.superview)
{
if ([NSStringFromClass([superview class]) isEqualToString:#"_UIPopoverView"])
return YES;
}
return NO;
}

Resources