Определите, находится ли представление внутри представления 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.