确定视图是否位于 Popover 视图内部

发布于 2024-10-03 09:13:39 字数 394 浏览 4 评论 0原文

我们在应用程序的 UINavigationControllers 内的许多位置都有常用的视图。有时,UINavigationController 位于弹出视图内。现在,我们放入导航控制器的视图会修改其导航控制器的工具栏按钮,并且在某些情况下,使用我们创建的自定义按钮。我们需要能够从 UIViewcontroller 本身判断视图是否位于 popoverview 内部,以便我们可以显示正确颜色的按钮。

我们可以使用 UIViewController.navigationController 轻松地从 UIViewController 获取导航控制器引用,但似乎没有任何东西可以找到 UIPopoverController 。

有人对如何做到这一点有什么好主意吗?

谢谢!

We have common views that we use in our application in many locations inside of UINavigationControllers. Occasionally the UINavigationControllers are inside of popover views. Now the views we put into the nav controllers modify their navigation controller's toolbar buttons and, in some cases, use custom buttons that we've created. We need to be able to figure out from the UIViewcontroller itself if the view is inside of a popoverview so we can display the correctly colored buttons.

We can easily get the Navigation controller reference from the UIViewController, using UIViewController.navigationController, but there doesn't seem to be anything for finding a UIPopoverController.

Does anyone have any good ideas for how to do this?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

音盲 2024-10-10 09:13:39

正如 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)
}

As Artem said we have UIPopoverPresentationController since iOS8. To determine if view is in popover you can use its .arrowDirection property for example.

Check it in viewWillApear() of presented view controller:

// 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)
}
遗忘曾经 2024-10-10 09:13:39

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

#import "Foundation/Foundation.h"

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

视图控制器想要知道它是否在弹出窗口中,然后定义一个属性 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 属性即可。

Here's another solution; define a protocol (e.g. PopoverSensitiveController) that has only one method:

#import "Foundation/Foundation.h"

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

A view controller that wants to know if it is in a popover then defines a property isInPopover; for example:

#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

Finally, in the splitView delegate (the assumption is that your app uses a split view controller):

#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];
  }

}

Then where ever in the view controller you want to know if you are in a popover, simply use the isInPopover property.

未央 2024-10-10 09:13:39

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

In iOS8 you can use popoverPresentationController property of UIViewController to check if it is contained in a popover presentation controller. From documentation, it returns: "The nearest ancestor in the view controller hierarchy that is a popover presentation controller. (read-only)"

ペ泪落弦音 2024-10-10 09:13:39

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

    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;

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

I was recently looking for a way to determine wether or not a view was being displayed in a popover. This is what I came up with:

    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");
        }

Basically you climb the view's superview tree looking to see if any of its superviews is a UIPopoverView. The one caveat here is that the class UIPopoverView is an undocumented private class. I'm relying on the fact that the class name won't change in the future. YMMV.

In your case:

theViewInQuestion =  theViewControllerInQuestion.view;

I'd be interested to see if anyone else comes up with a better solution.

假装不在乎 2024-10-10 09:13:39

对 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");

    }
}

** 注意 **

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

Modification of the accepted answer for iOS5.1 and newer:

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");

    }
}

** NOTE **

See comments about the reliability of this code.

仅一夜美梦 2024-10-10 09:13:39

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

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

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

My approach for this: (available with iOS 8 or greater)

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

Parent view controller will be the navigation controller which if inside a popover, will have a non-nil popoverPresentationController property.

你是暖光i 2024-10-10 09:13:39

通过使用 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

By working with SpareTime's code I came to this, which works as expected. Nice code, nice solution:

Using the standard UISplitViewController example.

/* 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
拒绝两难 2024-10-10 09:13:39

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

只需重写此方法,

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
}

如果它是弹出窗口,则不会调用“viewControllerForAdaptivePresentationStyle”方法,并且它将保持 true,如果它不是弹出窗口,则会将其设置为 false。

In case that someone else is still looking for a solution i came up with one good enough for me.

Just override this method

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

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

    return controller.presentedViewController
}

Here is an example of YourViewController

class AdvisorHomeFilterViewController: UIViewController {

    // MARK: - Properties

    var isPopover = true
}

If it is popover it will not call 'viewControllerForAdaptivePresentationStyle' method and it will stay true, in case it is not popover it will set it to false.

美人骨 2024-10-10 09:13:39

如果视图显示在弹出窗口中,我想在视图中放置一个按钮。我知道弹出框的宽度,因为我刚刚设置了它。这样我就可以测试我是否在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 --}

I wanted to put up a button in the view if the view wasn't displayed in a popover. I know the width of the popover because I just set it. So i can test whether I'm on an iPad and if the width of the frame is the same as what I set.

- (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 --}
伤感在游骋 2024-10-10 09:13:39

所有这些“精确类名匹配方法”很容易失败,甚至在 Apple 做出最微小的改变时也会出现故障。另外,执行 one-char-vars 和神秘的 for 循环并不完全适合我的风格。

我使用以下代码:

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

All these 'Exact Classname Matching Approaches' are very prone to fail and break at even the slightest changes Apple will make. Also doing one-char-vars and cryptic for-loops is not exactly a solution fitting to my style.

I use followingpiece of code:

- (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;
}
不念旧人 2024-10-10 09:13:39

上面所有的解决方案似乎都有点复杂。我使用一个名为 isInPopover 的变量,如果视图控制器出现在弹出窗口中,我将其设置为 true。在 popoverControllerDidDismissPopoverviewWillDisappear 的视图控制器中,我将布尔值设置为 false。它确实有效并且非常简单。

All the solutions above seems a little bit complicated. I'm using a variable called isInPopover which I set to true if the view controller is presented in a popover. In the view controller in popoverControllerDidDismissPopover or in viewWillDisappear I set the boolean value to false. It does work and is very simple.

爱要勇敢去追 2024-10-10 09:13:39

由于 self.popoverPresentationController 在最新的 iOS 版本中是延迟创建的,因此应该检查 self.popoverPresentationController.presentingViewController 是否为 nil,如果不是 nil,则意味着 self 当前显示在弹出窗口中。

Since self.popoverPresentationController is created lazily in most recent iOS versions, one should check for nil-ness of self.popoverPresentationController.presentingViewController, if not nil this would mean self is currently presented in a popover.

伤感在游骋 2024-10-10 09:13:39

Swift 4版本(可以在扩展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
}

Swift 4 version (function can be added in 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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文