2012-01-05 27 views
19

Estoy creando un diseño personalizado del popover que proporciona iOS. He subclasificado el UIPopoverBackgroundView y lo obtuve para dibujar correctamente el fondo de mi popover. Mi problema ahora es que UIPopoverController crea una sombra interna en el popover que afecta al contentViewController del popover. Quiero eliminar esta sombra interna, por lo que solo se muestra el contenido de mi contentViewController.Eliminar la sombra interna que crea UIPopoverController

Así es como se ve actualmente el popover, con un UILabel para demostrar el efecto en contentViewController.

popover

¿Hay alguna manera de eliminar esta sombra interior?

+0

Mira la respuesta de kajham, para iOS 6 debería ser la respuesta aceptada. –

Respuesta

25

Apoyo a esto se le añadió en ios6.0 con la siguiente llamada:

+ (BOOL)wantsDefaultContentAppearance

Enlace a la documentación: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPopoverBackgroundView_class/Reference/Reference.html

+0

Esta debería ser la respuesta aceptada. –

+4

esto no funciona para mí .. Tengo una subclase de UIPopoverBackgroundView, anuló el método y nada. sombras todavía allí ... ¿Alguna ayuda? por favor – Frade

+0

@Frade que necesita anular y no llame a 'super' en el método' - (void) layoutSubviews' (Lo encontré aquí: http://stackoverflow.com/questions/10044227/custom-uipopoverbackview-no-drop -shadow # comment27674273_12684634) – derpoliuk

1

No creo que exista una forma elegante/compatible de lograr eso usando UIPopover estándar de Apple. Sin embargo, puedes crear tu propia clase de popover personalizada con bastante facilidad. Hay bastantes ejemplos de cómo hacerlo, tanto aquí en SO y tutoriales en la web más amplia (incluso algunas soluciones listas para descargar). Simplemente ponga 'custom uipopover' en Google ...

+0

Sí, está bien. Solo pensé que sería bueno poder personalizar realmente la apariencia de UIPopover. Desde que Apple introdujo algunas capacidades de personalización con UIPopoverBackgroundView, pensé que sería posible cambiar la apariencia de UIPopover. – Sorig

+0

Este no es el caso. Replantear popovers por todos lados es una mala idea. –

+1

@BenLachman Cuídenos por qué? La pregunta no dice nada sobre "todo el lugar". Por el contrario, describe lo que necesita y pregunta si un objeto UIKit se adapta a la necesidad. En el momento no lo hizo. No hay absolutamente ninguna razón por la que rodar su propio elemento en este caso es una mala idea en sí misma. – isaac

10

Como no hay una manera elegante de hacerlo y como no quiero reescribir todo el UIPopoverController solo para hacer esto, he creado un truco simple que elimina el sombra interna en el popover atravesando la estructura UIView. El truco es una categoría en UIPopoverController y simplemente lo puse en los archivos de mi subclase de UIPopoverBackgroundView. Así que aquí está el código:

@interface UIPopoverController(removeInnerShadow) 

- (void)removeInnerShadow; 
- (void)presentPopoverWithoutInnerShadowFromRect:(CGRect)rect 
              inView:(UIView *)view 
         permittedArrowDirections:(UIPopoverArrowDirection)direction 
             animated:(BOOL)animated; 

- (void)presentPopoverWithoutInnerShadowFromBarButtonItem:(UIBarButtonItem *)item 
           permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections 
               animated:(BOOL)animated; 

@end 

@implementation UIPopoverController(removeInnerShadow) 

- (void)presentPopoverWithoutInnerShadowFromRect:(CGRect)rect inView:(UIView *)view permittedArrowDirections:(UIPopoverArrowDirection)direction animated:(BOOL)animated 
{ 
    [self presentPopoverFromRect:rect inView:view permittedArrowDirections:direction animated:animated]; 
    [self removeInnerShadow]; 
} 

- (void)presentPopoverWithoutInnerShadowFromBarButtonItem:(UIBarButtonItem *)item 
           permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections 
               animated:(BOOL)animated 
{ 
    [self presentPopoverFromBarButtonItem:item permittedArrowDirections:arrowDirections animated:animated]; 
    [self removeInnerShadow]; 
} 

- (void)removeInnerShadow 
{ 
    UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 

    for (UIView *windowSubView in window.subviews) 
    { 
     if ([NSStringFromClass([windowSubView class]) isEqualToString:@"UIDimmingView"]) 
     { 
      for (UIView *dimmingViewSubviews in windowSubView.subviews) 
      { 
       for (UIView *popoverSubview in dimmingViewSubviews.subviews) 
       { 
        if([NSStringFromClass([popoverSubview class]) isEqualToString:@"UIView"]) 
        { 
         for (UIView *subviewA in popoverSubview.subviews) 
         { 
          if ([NSStringFromClass([subviewA class]) isEqualToString:@"UILayoutContainerView"]) 
          { 
           subviewA.layer.cornerRadius = 0; 
          } 

          for (UIView *subviewB in subviewA.subviews) 
          { 
           if ([NSStringFromClass([subviewB class]) isEqualToString:@"UIImageView"]) 
           { 
            [subviewB removeFromSuperview]; 
           } 
          } 
         } 
        } 
       } 
      } 
     } 
    } 
} 

@end 

Cuando quiero mostrar mi popover acabo de llamar las presentPopoverWithoutInnerShadowFromRect: y presentPopoverWithoutInnerShadowFromBarButtonItem: métodos en lugar de los estándar. NOTA: Recuerde a #import <QuartzCore/QuartzCore.h> para que el código funcione

+0

Si tengo una UITableView dentro del popover, las máscaras de esquina aparecen cuando se desplaza. ¿Alguna idea de si hay una forma de evitar esto? – bromanko

+0

Puede usar "removeInnerShadow" para eliminar las máscaras de esquina y la sombra del popover. Intenta jugar con eso. Puede intentar y llamar a "removeInnerShadow" continuamente mientras se desplaza o tal vez puede buscar la forma de evitar que el desplazamiento agregue las máscaras de esquina nuevamente. – Sorig

+0

Resulta que también establece el color de fondo ayudado: 'subviewA.layer.backgroundColor = [UIColor clearColor] .CGColor;' – bromanko

10

I juest Creé mi propia versión para el proyecto en el que estoy trabajando.

Básicamente se debe utilizar una imagen personalizada para backgroundClass popover y en esta clase debe definir:

- (void)willMoveToWindow:(UIWindow *)newWindow { 
   [super willMoveToWindow:newWindow]; 
   if ([UIPopoverBackgroundView respondsToSelector:@selector(wantsDefaultContentAppearance)]) 
       return; 

   if (![[self class] wantsDefaultContentAppearance]) { 
       for (UIView *view in self.superview.subviews) { 
           for (UIView *subview in view.subviews) { 
               if (subview.layer.cornerRadius != 0.0) { 
                  subview.layer.cornerRadius = 0.0; 
                   for (UIView *subsubview in subview.subviews) { 
                       if (subsubview.class == [UIImageView class]) 
                           subsubview.hidden = YES; 
                   } 
               } 
           } 
       } 
   } 
} 

En caso de ser más o menos a prueba de balas para las actualizaciones de iOS.

+1

Esto funciona bien con popOvers para vistas, que no están incrustadas en NavigationControllers. Tan pronto como ViewController (que está incrustado en el NavigationController) se usa para el popover, la sombra regresa. Hay alguna solución para esto? – FrankZp

+0

#FrankZP Busque la modificación a continuación. –

+0

+1 buena solución! Una pregunta: ¿podría explicar un poco por qué el envío de un mensaje a '[self class]' (que es un 'objc_class') funciona? – matm

0

Para FrankZp

Esto funciona bien con panecillos de puntos de vista, que no están incrustadas en NavigationControllers. Tan pronto como el ViewController (que es incrustado en el NavigationController) se utiliza para el popover, la sombra vuelve a estar activa. Hay alguna solución para esto?

Aquí es la modificación de la UINavigationController:

- (void)removeInnerShadow 
{ 
    UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 

    for (UIView *windowSubView in window.subviews) { 
     if ([NSStringFromClass([windowSubView class]) isEqualToString:@"UIDimmingView"] { 
      for (UIView *dimmingViewSubviews in windowSubView.subviews) { 
       for (UIView *popoverSubview in dimmingViewSubviews.subviews) { 
        if([NSStringFromClass([popoverSubview class]) isEqualToString:@"UIView"]) { 
         for (UIView *subviewA in popoverSubview.subviews) { 
          if ([NSStringFromClass([subviewA class]) isEqualToString:@"UILayoutContainerView"]) { 
           subviewA.layer.cornerRadius = 0; 
          } 

          for (UIView *subviewB in subviewA.subviews) { 
           if ([NSStringFromClass([subviewB class]) isEqualToString:@"UILayoutContainerView"]) { 
            for (UIView * subviewC in subviewB.subviews) { 
             if ([NSStringFromClass([subviewC class]) isEqualToString:@"UIImageView"]) { 
              [subviewC removeFromSuperview]; 
             } 
            } 
           } 

           if ([NSStringFromClass([subviewB class]) isEqualToString:@"UIImageView"]) { 
            [subviewB removeFromSuperview]; 
           } 
          } 
         } 
        } 
       } 
      } 
     } 
    } 
} 
3

Aunque estoy de acuerdo en principio que la forma correcta de manejar esto es rodar su propia Popover, en este caso se trata de un problema no para versiones más nuevas del sistema operativo. ¿De verdad quiero construir y mantener mi propia implementación de popover solo para soportar un SO que eventualmente será irrelevante? Si realmente lo desea, considere algunas de las implementaciones gratuitas de código abierto en la web.

Personalmente, investigué los métodos sugeridos aquí y surgió el mío usando esta página como punto de partida (¡gracias!). Funciona para ambos escenarios (con o sin una barra de navegación) y es un poco más seguro en mi opinión.

En lugar de agregar un método al UIPopoverController, agregué una rutina a mi UIPopoverBackgroundView para buscar las vistas ofensivas usando una ruta RELATIVA, en lugar de ABSOLUTO. En resumen, dado que el código tiene una referencia directa a UIPopoverBackgroundView (self), puede navegar hacia arriba (superview) y luego hacia abajo (subviews).

La vista árboles parecen esto en ambos escenarios:

Con UINavigationBar: enter image description here

Sin UINavigationBar: enter image description here

Los dos puntos de vista que nos interesa son las Vistas UILayoutView y UIImage que están en negrita y subrayadas en cada gráfico. Podemos obtener una referencia a estos a partir de UIPopoverBackgroundView utilizando el siguiente código (supone ARC). Ejecuto esto desde layoutSubviews en mi implementación UIPopoverBackgroundView.

// Helper method for traversing child views based solely on class types 
UIView* (^__unsafe_unretained __block traverseSubviews)(UIView*, NSArray*) = ^(UIView *root, NSArray* nodeTypes) { 
    NSString *typeName = [nodeTypes objectAtIndex:0]; 
    for (UIView *subView in root.subviews) { 
     if ([NSStringFromClass([subView class]) isEqualToString: typeName]) { 
      if (nodeTypes.count == 1) 
       return subView; 
      else 
       return traverseSubviews(subView, [nodeTypes subarrayWithRange:NSMakeRange(1, nodeTypes.count - 1)]); 
     } 
    } 
    return (UIView*)nil; 
}; 

// Find the subviews of interest, taking into account there could be a navigation bar 
UIView *layoutView = traverseSubviews([self superview], @[@"UIView", @"UILayoutContainerView"]); 
if (traverseSubviews(layoutView, @[@"UINavigationBar"])) { 
    layoutView = traverseSubviews(layoutView, @[@"UILayoutContainerView"]); 
} 
UIView *imageView = traverseSubviews(layoutView, @[@"UIImageView"]); 

// Remove the default content appearance 
layoutView.layer.cornerRadius = 0; 
[imageView removeFromSuperview]; 

Utilizo un bloque aquí para hacer el recorrido de las subvistas para mantener el código conciso. Toma una vista como punto de partida y una matriz de nombres de clase. La matriz de nombres de clase es la secuencia de clases de vista que espero, donde el índice 0 es el padre del índice 1 y el índice 1 es el padre del índice 2, etc. Devuelve la vista representada por el último elemento de la matriz.

+0

Debe agregar una condición para evitar hacer algo de esto en iOS 6+ y simplemente devolver 'NO' de' wantsDefaultContentAppearance'. –

+0

+1 para una explicación diagramática detallada. ¿También puede decirme cómo ajustar el tamaño del borde de UIPopover? – HDdeveloper

+0

¿Te refieres al borde exterior? Usé UIPopoverBackgroundView para usar imágenes para especificar el aspecto del popover. Si te refieres al borde interior, simplemente lo eliminé. La implementación predeterminada usa un UIImageView por lo que tendría que reemplazar la imagen si solo quisiera cambiar el borde. –

Cuestiones relacionadas