确定视图是否在Popover视图中

时间:2010-11-16 06:54:08

标签: objective-c ipad uipopovercontroller

我们在UINavigationControllers内的许多位置都有我们的应用程序中使用的常见视图。偶尔UINavigationController是在popover视图中。现在我们放入导航控制器的视图会修改导航控制器的工具栏按钮,在某些情况下,还会使用我们创建的自定义按钮。如果视图位于popoverview中,我们需要能够从UIViewcontroller本身找出来,以便我们可以显示正确颜色的按钮。

我们可以使用UIViewController.navigationController从UIViewController轻松获取导航控制器参考,但似乎找不到UIPopoverController

有没有人对如何做到这一点有任何好的想法?

谢谢!

13 个答案:

答案 0 :(得分:7)

正如Artem所说,自 iOS8 以来我们已UIPopoverPresentationController。要确定视图是否在弹出窗口中,您可以使用其.arrowDirection属性。

在呈现的视图控制器的viewWillApear()中查看它:

// get it from parent NavigationController
UIPopoverPresentationController* popoverPresentationVC = self.parentViewController.popoverPresentationController; 
if (UIPopoverArrowDirectionUnknown > popoverPresentationVC.arrowDirection) {
// presented as popover
} else {
// presented as modal view controller (on iPhone)
}

答案 1 :(得分:6)

这是另一种解决方案;定义一个只有一个方法的协议(例如PopoverSensitiveController):

#import "Foundation/Foundation.h"

@protocol PopoverSensitiveController 
-(void) setIsInPopover:(BOOL) inPopover;
@end

一个想知道它是否在popover中的视图控制器然后定义一个属性isInPopover;例如:

#import 
#import "PopoverSensitiveController.h"

#pragma mark -
#pragma mark Interface
@interface MyViewController : UIViewController  {
}

#pragma mark -
#pragma mark Properties
@property (nonatomic) BOOL isInPopover;

#pragma mark -
#pragma mark Instance Methods
...other stuff...
@end

最后,在splitView委托中(假设您的应用程序使用拆分视图控制器):

#import "MySplitViewControllerDelegate.h"
#import "SubstitutableDetailViewController.h"
#import "PopoverSensitiveController.h"

#pragma mark -
#pragma mark Implementation
@implementation MySplitViewControllerDelegate

#pragma mark -
#pragma mark UISplitViewControllerDelegate protocol methods
-(void) splitViewController:(UISplitViewController *) splitViewController willHideViewController:(UIViewController *) aViewController withBarButtonItem:(UIBarButtonItem *) barButtonItem forPopoverController:(UIPopoverController *) pc {

  // Keep references to the popover controller and the popover button, and tell the detail view controller to show the button
  popoverController = [pc retain];
  popoverButtonItem = [barButtonItem retain];
  if ([[splitViewController.viewControllers objectAtIndex:1] respondsToSelector:@selector(showRootPopoverButtonItem:)]) {
      UIViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
      [detailViewController showRootPopoverButtonItem:barButtonItem];
  }
  if ([[splitViewController.viewControllers objectAtIndex:1] respondsToSelector:@selector(showRootPopoverButtonItem:)]) {
      UIViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
      [detailViewController showRootPopoverButtonItem:barButtonItem];
  }

  // If the view controller wants to know, tell it that it is a popover
  if ([aViewController respondsToSelector:@selector(setIsInPopover:)]) {
    [(id) aViewController setIsInPopover:YES];
  }

  // Make sure the proper view controller is in the popover controller and the size is as requested
  popoverController.contentViewController = aViewController;
  popoverController.popoverContentSize = aViewController.contentSizeForViewInPopover;

}

-(void) splitViewController:(UISplitViewController *) splitViewController willShowViewController:(UIViewController *) aViewController invalidatingBarButtonItem:(UIBarButtonItem *) barButtonItem {

  // Tell the detail view controller to hide the button.
  if ([[splitViewController.viewControllers objectAtIndex:1] respondsToSelector:@selector(invalidateRootPopoverButtonItem:)]) {
    UIViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
    [detailViewController invalidateRootPopoverButtonItem:barButtonItem];
  }

  // If the view controller wants to know, tell it that it is not in a popover anymore
  if ([aViewController respondsToSelector:@selector(setIsInPopover:)]) {
    [(id) aViewController setIsInPopover:NO];
  }

  // Now clear out everything
  [popoverController release];
  popoverController = nil;
  [popoverButtonItem release];
  popoverButtonItem = nil;

}

-(void) setPopoverButtonForSplitViewController:(UISplitViewController *) splitViewController {

  // Deal with the popover button
  UIViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
  [detailViewController showRootPopoverButtonItem:popoverButtonItem];

  // If the view controller wants to know, tell it that it is a popover (initialize the controller properly)
  if ([[splitViewController.viewControllers objectAtIndex:0] respondsToSelector:@selector(setIsInPopover:)]) {
    [(id) [splitViewController.viewControllers objectAtIndex:0] setIsInPopover:YES];
  }

}

然后在视图控制器中你想知道你是否在弹出框中,只需使用isInPopover属性。

答案 2 :(得分:5)

在iOS8中,您可以使用UIViewController的popoverPresentationController属性来检查它是否包含在弹出式表示控制器中。从文档中,它返回:"视图控制器层次结构中最近的祖先,它是一个弹出式表示控制器。 (只读)"

答案 3 :(得分:3)

我最近在寻找一种方法来确定视图是否在弹出框中显示。这就是我想出的:

    UIView *v=theViewInQuestion;        
    for (;v.superview != nil; v=v.superview) {
        if (!strcmp(object_getClassName(v), "UIPopoverView")) {
            NSLog(@"\n\n\nIM IN A POPOVER!\n\n\n\n");
        }

基本上你爬上视图的超级视图树,看看它的任何超级视图是否是UIPopoverView。这里要注意的是,类UIPopoverView是一个未记录的私有类。我依赖于类名在将来不会改变的事实。 YMMV。

在你的情况下:

theViewInQuestion =  theViewControllerInQuestion.view;

我有兴趣看看是否有其他人想出更好的解决方案。

答案 4 :(得分:3)

修改iOS5.1及更高版本的已接受答案:

for (UIView *v = self.view; v.superview != nil; v=v.superview) { 

    if ([v isKindOfClass:[NSClassFromString(@"_UIPopoverView") class]]) {

        NSLog(@"\n\n\nIM IN A POPOVER!\n\n\n\n");

    }
}

**注意**

请参阅有关此代码可靠性的评论。

答案 5 :(得分:2)

我的方法:(适用于iOS 8或更高版本)

- (BOOL)isContainedInPopover
{
    UIPopoverPresentationController* popoverPresentationVC = self.parentViewController.popoverPresentationController;
    return (popoverPresentationVC != nil);
}

父视图控制器将是导航控制器,如果在弹出窗口内,将具有非零popoverPresentationController属性。

答案 6 :(得分:1)

通过使用SpareTime的代码,我来到了这个,它按预期工作。不错的代码,很好的解决方案:

使用标准的UISplitViewController示例。

/* MasterViewController.h */

#import "UIPopoverViewDelegate.h"

@interface masterViewController : UITableViewController <UIPopoverViewDelegate>
@property (nonatomic) BOOL isInPopover;
@end

/* MasterViewController.m */

#import "MasterViewController.h"

@implementation MasterViewController

@synthesize isInPopover = _isInPopover;

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    if (self.isInPopover)
    {
        // Code for appearing in popover
    }
    else
    {
        // Code for not appearing in popover
    }
}

@end

/* DetailViewController.h */

#import "UIPopoverViewDelegate.h"

@interface detailViewController : UIViewController <UISplitViewControllerDelegate>
@end

/* DetailViewController.m */

#import "DetailViewController.h"

@implementation detailViewController

- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{

    /* This method is called when transitioning to PORTRAIT orientation. */

    UIViewController *hiddenViewController = [(UINavigationController *)viewController childViewControllers].lastObject;

    if ([hiddenViewController respondsToSelector:@selector(setIsInPopover:)])
        [(id <UIPopoverViewDelegate>)hiddenViewController setIsInPopover:YES];
}

- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{

    /* This method is called when transitioning to LANDSCAPE orientation. */

    UIViewController *shownViewController = [(UINavigationController *)viewController childViewControllers].lastObject;

    if ([shownViewController respondsToSelector:@selector(setIsInPopover:)])
        [(id <UIPopoverViewDelegate>)shownViewController setIsInPopover:NO];
}

@end

/* UIPopoverViewDelegate.h */

@protocol UIPopoverViewDelegate
@required
-(void)setIsInPopover:(BOOL)inPopover;
@end

答案 7 :(得分:1)

万一其他人仍在寻找解决方案,我想出一个对我来说足够好的解决方案。

只需重写此方法

func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {

    (controller.presentedViewController as? YourViewControler).isPopover = false

    return controller.presentedViewController
}

这是YourViewController的示例

class AdvisorHomeFilterViewController: UIViewController {

    // MARK: - Properties

    var isPopover = true
}

如果它是popover,则不会调用'viewControllerForAdaptivePresentationStyle'方法,它将保持为true;如果不是popover,则将其设置为false。

答案 8 :(得分:0)

如果视图不是显示在弹出窗口中,我想在视图中放置一个按钮。我知道弹出窗口的宽度,因为我只是设置它。所以我可以测试我是否在iPad上以及框架的宽度是否与我设置的相同。

- (void)viewWillAppear:(BOOL)animated {
[self setContentSizeForViewInPopover:CGSizeMake(400, 500)];

NSInteger frameWidth = self.view.frame.size.width;
//Let you go back to the game if on an iPod.
if ( ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) && !(frameWidth == 400) ) { ---code to display a button --}

答案 9 :(得分:0)

所有这些'精确的类名匹配方法'很容易失败,即使是苹果公司将做出的最微小的改变也会中断。同时做一个char-vars和神秘的for-loop也不是一个适合我风格的解决方案。

我使用以下代码:

- (BOOL) isInPopOver {
    UIView *currentView = self.view;
    while( currentView ) {
        NSString *classNameOfCurrentView = NSStringFromClass([currentView class]);
        NSLog( @"CLASS-DETECTED: %@", classNameOfCurrentView );
        NSString *searchString = @"UIPopoverView";
        if( [classNameOfCurrentView rangeOfString:searchString options:NSCaseInsensitiveSearch].location != NSNotFound ) {
            return YES;
        }
        currentView = currentView.superview;
    }
    return NO;
}

答案 10 :(得分:0)

上述所有解决方案似乎都有点复杂。我正在使用一个名为isInPopover的变量,如果视图控制器以弹出框形式显示,我将其设置为true。在popoverControllerDidDismissPopoverviewWillDisappear中的视图控制器中,我将布尔值设置为false。它确实有效,而且很简单。

答案 11 :(得分:0)

由于self.popoverPresentationController是在最近的iOS版本中懒散创建的,因此应该检查self.popoverPresentationController.presentingViewController的nil-ness,如果不是n,这将意味着self当前在弹出窗口中显示。

答案 12 :(得分:0)

Swift 4版本(可以在extension UIViewController中添加功能):

func isInPopover() -> Bool {
    guard UIDevice.current.userInterfaceIdiom == .pad else { return false }

    var checkingVC: UIViewController? = self
    repeat {
        if checkingVC?.modalPresentationStyle == .popover {
            return true
        }
        checkingVC = checkingVC?.parent
    } while checkingVC != nil
    return false
}