Определите, находится ли представление внутри представления Popover

У нас есть общие представления, которые мы используем в нашем приложении во многих местах внутри UINavigationControllers. Иногда UINavigationController находится внутри просмотров. Теперь представления, которые мы помещаем в контроллеры nav, изменяют их кнопки панели навигации контроллера навигации и, в некоторых случаях, используют пользовательские кнопки, которые мы создали. Нам нужно иметь возможность выяснить из самого UIViewcontroller, если представление находится внутри всплывающего окна, чтобы мы могли отображать правильно цветные кнопки.

Мы можем легко получить ссылку на контроллер навигации из UIViewController, используя UIViewController.navigationController, но, похоже, ничего не найдено для нахождения UIPopoverController.

Есть ли у кого-нибудь хорошие идеи, как это сделать?

Спасибо!

Ответы

Ответ 1

Недавно я искал способ определить, не видно ли представление в popover. Вот что я придумал:

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

В основном вы входите в дерево просмотра view view, чтобы посмотреть, является ли какой-либо из его супервизоров UIPopoverView. Одно из предостережений здесь заключается в том, что класс UIPopoverView является недокументированным частным классом. Я полагаюсь на то, что имя класса не изменится в будущем. YMMV.

В вашем случае:

theViewInQuestion =  theViewControllerInQuestion.view;

Мне было бы интересно узнать, найдет ли кто-нибудь лучшее решение.

Ответ 2

Здесь другое решение; определить протокол (например, 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];
  }

}

Затем, где когда-либо в контроллере представления вы хотите узнать, находитесь ли вы в popover, просто используйте свойство isInPopover.

Ответ 3

В iOS8 вы можете использовать свойство popoverPresentationController UIViewController, чтобы проверить, содержится ли он в контроллере представления popover. Из документации он возвращает: "Ближайший предк в иерархии диспетчера представлений, который является контроллером представления popover (только для чтения)"

Ответ 4

Как сказал Артем, мы имеем UIPopoverPresentationController с iOS8. Чтобы определить, находится ли вид в popover, вы можете использовать его свойство .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)
}

Ответ 5

Модификация принятого ответа для 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");

    }
}

** ПРИМЕЧАНИЕ **

Смотрите комментарии о надежности этого кода.

Ответ 6

Мой подход для этого: (доступно с iOS 8 или выше)

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

Контроллер родительского контроля будет навигационным контроллером, который, если внутри popover, будет иметь свойство non-nil popoverPresentationController.

Ответ 7

Работая с кодом 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

Ответ 8

Я хотел поставить кнопку в представлении, если представление не было показано в popover. Я знаю ширину popover, потому что я просто установил ее. Поэтому я могу проверить, есть ли у меня 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

Все эти "Точные совпадения подходов класса" очень подвержены ошибкам и ломаются даже при незначительных изменениях, которые Apple сделает. Кроме того, выполнение одного char -vars и криптографических for-loops не является точно решением, соответствующим моему стилю.

Я использую следующий код кода:

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

Все приведенные выше решения кажутся немного сложными. Я использую переменную с именем isInPopover, которую я установил в true, если контроллер представления представлен в popover. В контроллере представления в popoverControllerDidDismissPopover или в viewWillDisappear я устанавливаю значение boolean равным false. Он работает и очень прост.

Ответ 11

Так как self.popoverPresentationController создается лениво в самых последних версиях iOS, нужно проверить, существует ли nil-ness self.popoverPresentationController.presentingViewController, если это не означает, что это означает, что self теперь отображается в popover.