Ios 如何从显示在popover中的UIViewController中找到UIPopopOvercontroller?

Ios 如何从显示在popover中的UIViewController中找到UIPopopOvercontroller?,ios,ipad,popover,Ios,Ipad,Popover,使用UIViewController的实例,是否有任何方法可以找到用于显示它的UIPopoverController?我还想首先找到显示UIPopoverController的UIViewController 我通常会使用委托或其他类型的通知将信号从显示的视图控制器发送到显示的视图控制器,但在这种情况下,我尝试创建一个可重用的自定义序列,该序列将取消popover,然后移动到主视图中的另一个视图。您会认为这很简单(UIViewController甚至有一个私有的\u popoverControl

使用UIViewController的实例,是否有任何方法可以找到用于显示它的UIPopoverController?我还想首先找到显示UIPopoverController的UIViewController


我通常会使用委托或其他类型的通知将信号从显示的视图控制器发送到显示的视图控制器,但在这种情况下,我尝试创建一个可重用的自定义序列,该序列将取消popover,然后移动到主视图中的另一个视图。

您会认为这很简单(UIViewController甚至有一个私有的
\u popoverController
属性!),但它不是

一般的答案是,在创建
UIViewController
时,您必须在它显示的
UIViewController
中保存对
uipopcovercontroller
的引用

  • 如果您正在以编程方式创建
    UIPopoverController
    ,则此时应将引用存储在
    UIViewController
    子类中

  • // UIViewController+isPresentedInPopover.h
    
    #import <UIKit/UIKit.h>
    
    @interface UIViewController (isPresentedInPopover)
    
    @property (assign, nonatomic, getter = isPresentedInPopover) BOOL presentedInPopover;
    
    @end
    
  • 如果您使用的是故事板和序列,则可以通过
    prepareforsgue
    方法从序列中取出
    UIPopoverController

    UIPopoverController* popover = [(UIStoryboardPopoverSegue*)segue popoverController];
    

  • 当然,请确保您的segue确实是一个uistoryboardpooversegue!

    最有帮助的可能是将popover设置为类变量,因此在将显示popover的类的.m文件中,执行以下操作:

        @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
    

    我的建议是利用您自己的自定义属性和UIKit中的私有API的组合。为了避免应用商店拒绝,任何私有API都应该编译为发布版本,并且应该仅用于检查您的实现

    首先,让我们将自定义属性构建到
    UIViewController
    上的一个类别中。这样可以在实现中获得一些额外的功能,而不需要返回并从某个自定义视图控制器子类派生每个类

    // UIViewController+isPresentedInPopover.h
    
    #import <UIKit/UIKit.h>
    
    @interface UIViewController (isPresentedInPopover)
    
    @property (assign, nonatomic, getter = isPresentedInPopover) BOOL presentedInPopover;
    
    @end
    
    如果不正确地使用该属性,您将在控制台上看到如下消息:

    2012-09-18 14:28:30.375 MyApp[41551:c07]*由于未捕获的异常“NSinternalinconsistenceexception”而终止应用程序,原因:“-[UINavigationController IsPresentedPover]中的一致性错误]:在私有UIViewController API建议是时返回否。您是否忘记设置“PresentedPover”?”

    …但当在调试标志为off或设置为0的情况下编译时,它会编译为与以前完全相同的代码

    为了自由和鲁莽 也许你正在进行即席/企业/个人构建,或者你足够大胆,可以看到苹果对App Store的想法。不管怎样,这里有一个只使用当前运行时和
    UIViewController
    -无需设置属性的实现

    // UIViewController+isPresentedInPopover.h
    
    #import <UIKit/UIKit.h>
    
    @interface UIViewController (isPresentedInPopover)
    
    @property (readonly, assign, nonatomic, getter = isPresentedInPopover) BOOL presentedInPopover;
    
    @end
    
    //UIViewController+isPresentedInputOver.h
    #进口
    @界面UIViewController(iPresentedPopover)
    @属性(只读、赋值、非原子、getter=isPresentedInPopover)BOOL presentedInPopover;
    @结束
    

    //UIViewController+isPresentedInputOver.m
    #导入“UIViewController+isPresentedPopover.h”
    #进口
    @实现UIViewController(isPresentedPopover)
    -(BOOL)在弹出窗口中显示
    {
    Ivar privatePopoverIvar=class_getInstanceVariable([UIViewController类],“_popoverController”);
    UIPopoverController*popover=object_getIvar(self,privatePopoverIvar);
    BOOL privateAPIValue=popover!=nil;
    返回privateAPIValue?:[[self-parentViewController]显示在Popover];
    }
    @结束
    
    从ndoc的anwser起飞:在iOS 6中显示了一种更简洁的方法,可以防止popover在多个时间段中显示。链接中的方法对我来说非常有效,可以防止popover堆叠。

    如果你只想知道你的控制器是否在popover中显示(对获取popover控制器的引用不感兴趣),您可以简单地执行此操作,而无需存储变量,也无需攻击私有API

    -(BOOL)isPresentedInPopover
    {
        for (UIView *superview = self.view.superview; superview != nil; superview = superview.superview)
        {
            if ([NSStringFromClass([superview class]) isEqualToString:@"_UIPopoverView"])
                return YES;
        }
        return NO;
    }
    

    正如@joey在上面所写的,Apple在iOS 8中不再需要虚拟控件,将
    popoverPresentationController
    属性定义为
    UIViewController
    ,作为“视图控制器层次结构中最接近的祖先,即popover表示控制器(只读)”

    以下是Swift中一个在故事板上定义的基于
    UIPopoverPresentationController
    的segue的示例。在这种情况下,通过编程方式添加了一个按钮,可以通过这种方式将其定义为弹出窗口的锚定。发送方也可以是选定的
    UITableViewCell
    或其中的一个视图

    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
            }
        }
    }
    

    为什么不在神奇的复选标记下面给出答案呢?访问私有变量的一种更简单的方法是
    [self valueForKey:@“\u popoverController”]
    。这是因为
    +[UIViewController accessInstanceVariablesDirectly]<代码>返回>代码>是> <代码>。因为我的答案中的方法只取决于头文件中的规范,而不是代码< > Access StutsReavababelEngult< /Cube >的实现细节,我认为运行时方法是更好的选择。如果您不同意,请随意使用<代码> UIPOPopReultPrimePopOver =[self-valueForKey:@“\u popoverController”];
    +1
    获取详细答案:)如果你要这样做,为什么不在一个类别变量中设置UIPopoverController?那么就没有访问任何私有API或难看的硬编码字符串的权限。而且你可以创建一个UIPopoverController类别,只在当前自动设置变量。无论如何,我只是想有一个更好的方法在取消/完成时关闭popover,所以我结束了d停止使用块。回调和委托留下了太多混乱的代码。哇。谢谢你的理智!其他人-忽略上面所有复杂的理论,使用这个!!!
    -(BOOL)isPresentedInPopover
    {
        for (UIView *superview = self.view.superview; superview != nil; superview = superview.superview)
        {
            if ([NSStringFromClass([superview class]) isEqualToString:@"_UIPopoverView"])
                return YES;
        }
        return NO;
    }
    
    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
            }
        }
    }