objective-c - emergente - formulario access ventana independiente
Determine si una vista está dentro de una vista emergente (13)
Al trabajar con el código de SpareTime llegué a esto, que funciona como se esperaba. Buen código, buena solución:
Usando el ejemplo estándar de 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
Tenemos puntos de vista comunes que utilizamos en nuestra aplicación en muchas ubicaciones dentro de UINavigationControllers
. Ocasionalmente, los UINavigationController
están dentro de las vistas de popover. Ahora las vistas que colocamos en los controladores de navegación modifican los botones de la barra de herramientas de su controlador de navegación y, en algunos casos, utilizan botones personalizados que hemos creado. Necesitamos poder descifrarnos desde el UIViewcontroller
si la vista está dentro de una ventana emergente para poder mostrar los botones de colores correctos.
Podemos obtener fácilmente la referencia del controlador de navegación de UIViewController, utilizando UIViewController.navigationController
, pero no parece haber nada para encontrar un UIPopoverController
.
¿Alguien tiene alguna buena idea de cómo hacer esto?
¡Gracias!
Aquí hay otra solución; defina un protocolo (por ejemplo, PopoverSensitiveController) que tenga un solo método:
#import "Foundation/Foundation.h" @protocol PopoverSensitiveController -(void) setIsInPopover:(BOOL) inPopover; @end
Un controlador de vista que desea saber si se encuentra en una ventana emergente define una propiedad isInPopover; por ejemplo:
#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
Finalmente, en el delegado splitView (se supone que su aplicación utiliza un controlador de vista dividida):
#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]; } }
Entonces, en cualquier lugar del controlador de vista que desee saber si se encuentra en una ventana emergente, simplemente use la propiedad isInPopover.
Como dijo Artem, tenemos UIPopoverPresentationController
desde iOS8 . Para determinar si la vista está en una .arrowDirection
puede usar su propiedad .arrowDirection
, por ejemplo.
Compruébelo en viewWillApear()
del controlador de vista presentado:
// 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)
}
Dado que self.popoverPresentationController
se crea perezosamente en la mayoría de las versiones recientes de iOS, uno debe verificar la self.popoverPresentationController.presentingViewController
de self.popoverPresentationController.presentingViewController
, si no nil, esto significaría que self
se presenta actualmente en una ventana emergente.
En caso de que alguien más todavía esté buscando una solución, se me ocurrió una suficientemente buena para mí.
Simplemente anula este método
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
(controller.presentedViewController as? YourViewControler).isPopover = false
return controller.presentedViewController
}
Aquí hay un ejemplo de YourViewController
class AdvisorHomeFilterViewController: UIViewController {
// MARK: - Properties
var isPopover = true
}
Si es una ventana emergente, no llamará al método ''viewControllerForAdaptivePresentationStyle'' y se mantendrá verdadero, en caso de que no sea una ventana emergente, se configurará como falso.
En iOS8 puede usar la propiedad popoverPresentationController de UIViewController para verificar si está contenida en un controlador de presentación emergente. De la documentación, devuelve: "El antepasado más cercano en la jerarquía del controlador de vista que es un controlador de presentación emergente. (Solo lectura)"
Hace poco estuve buscando una manera de determinar si una vista se mostraba o no en una ventana emergente. Esto es lo que se me ocurrió:
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");
}
Básicamente, subes al árbol de vistas de la vista para ver si alguna de sus vistas de supervisión es un UIPopoverView. La única advertencia aquí es que la clase UIPopoverView es una clase privada no documentada. Confío en el hecho de que el nombre de la clase no cambiará en el futuro. YMMV.
En tu caso:
theViewInQuestion = theViewControllerInQuestion.view;
Me interesaría ver si a alguien más se le ocurre una solución mejor.
Mi enfoque para esto: (disponible con iOS 8 o superior)
- (BOOL)isContainedInPopover
{
UIPopoverPresentationController* popoverPresentationVC = self.parentViewController.popoverPresentationController;
return (popoverPresentationVC != nil);
}
El controlador de vista principal será el controlador de navegación que, si está dentro de una popoverPresentationController
, tendrá una propiedad popoverPresentationController
no nil.
Modificación de la respuesta aceptada para iOS5.1 y posteriores:
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");
}
}
** NOTA **
Ver comentarios sobre la fiabilidad de este código.
Quería colocar un botón en la vista si la vista no se mostraba en una ventana emergente. Sé el ancho de la ventana emergente porque acabo de configurarlo. Entonces puedo probar si estoy en un iPad y si el ancho del marco es el mismo que el que establezco.
- (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 --}
Todas las soluciones anteriores parecen un poco complicadas. Estoy usando una variable llamada isInPopover
que establezco en verdadero si el controlador de vista se presenta en una ventana emergente. En el controlador de vista en popoverControllerDidDismissPopover
o en viewWillDisappear
, establezco el valor booleano en false. Funciona y es muy simple.
Todos estos ''enfoques de concordancia de nombres de clase exactos'' son muy propensos a fallar y romperse incluso con los cambios más pequeños que Apple hará. También hacer one-char-vars y cryptic for-loops no es exactamente una solución que se ajuste a mi estilo.
Yo uso la siguiente pieza de código:
- (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;
}
Versión Swift 4 (la función se puede agregar en la 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
}