2010-11-16 2 views
15

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

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

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

Спасибо!

ответ

3

Я недавно искал способ определить, есть ли представление, которое отображается в 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"); 
     } 

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

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

theViewInQuestion = theViewControllerInQuestion.view; 

мне было бы интересно увидеть, если кто-то приходит с лучшим решением.

+1

Звучит как жизнеспособное решение, спасибо за отзыв! Я закончил делать что-то немного другое и более конкретное для моего приложения, чтобы понять это, но похоже, что это будет работать, поэтому я собираюсь отметить его как правильный ответ! Благодаря! – mjdth

+0

Кто-нибудь пробовал это, он безопасен для AppStore? – steipete

+0

Проблема в том, что он находит только UIPopoverView ... Как вы превращаете это в контроллер? – Thomas

6

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

} 

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

0

Я хотел поставить кнопку на вид, если вид не был отображается в 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 --} 
+0

взломать содержимое является опрятной идеей. почему бы не установить его, скажем 400.1 и сравнить с этим? – n13

+0

Я вообще не взламываю содержимое. Я просто установил размер так, чтобы он был достаточно большим, чтобы содержать все элементы, которые мне нужны. Если я устанавливаю его на нецелую ширину, то ребра не будут лежать на границе пикселя. – JScarry

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 
3

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

    } 
} 

** NOTE **

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

+1

Обратите внимание, что если ваше представление находится в UINavigationController, вам понадобится 'UIView * v = self.navigationController.view' (потому что' self.view.superview' является 'nil' в навигационном контроллере.) –

+0

Спасибо, это довольно cool – cleverbit

+1

Я не уверен, почему это должно быть хорошо? Этот способ сделать это уже закончился с iOS 4.x -> 5.1. В будущем он снова сломается. Возможно, это хорошо, если вы ищете постоянную работу, но это не хороший код. – n13

0

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

Я использую followingpiece кода:

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

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

+3

Кажется, что возвращает только действительный объект (т. Е. Не-nil) после отображения представления контроллера вида. т. е. это возвращает 'nil' для меня из' viewWillAppear: ' – levigroker

+0

В iOS 9 это работает для меня в viewWillAppear, но не в viewDidLoad. – arlomedia

0

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

5

Как сказал Артем, у нас есть 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) 
} 
2

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

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

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

0

С self.popoverPresentationController создается лениво в большинстве версий последних IOS, следует проверить нильпоток ность self.popoverPresentationController.presentingViewController, если не ноль это будет означать self в настоящее время представлен в пироге.