2012-01-05 10 views
19

Sto creando un layout personalizzato del popover fornito da iOS. Ho creato una sottoclasse di UIPopoverBackgroundView e ottenuto di disegnare correttamente lo sfondo per il mio popover. Il mio problema ora è che UIPopoverController crea un'ombra interna sul popover che influisce sul contentViewController del popover. Voglio rimuovere questa ombra interna, quindi viene visualizzato solo il contenuto del mio ContentViewController.Rimuovi l'ombra interna creata da UIPopoverController

Ecco come appare attualmente il popover, con un UILabel per dimostrare l'effetto sul contentViewController.

popover

Esiste un modo per rimuovere questa ombra interiore?

+0

La risposta di kajham, per iOS 6 dovrebbe essere la risposta accettata. –

risposta

25

supporto per questo è stato aggiunto nel ios6.0 con il seguente invito:

+ (BOOL)wantsDefaultContentAppearance

link alla documentazione: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPopoverBackgroundView_class/Reference/Reference.html

+0

Questa dovrebbe essere la risposta accettata. –

+4

questo non funziona per me .. Ho una sottoclasse di UIPopoverBackgroundView, sovrascritto il metodo, e nulla. l'ombra si ferma lì .. qualche aiuto ?? per favore – Frade

+0

@Frade devi eseguire l'override e non chiamare 'super' in' - (void) metodo layoutSubviews' (l'ho trovato qui: http://stackoverflow.com/questions/10044227/custom-uipopoverbackgroundview-no-drop -shadow # comment27674273_12684634) – derpoliuk

1

Non credo che esista un modo elegante/supportato per raggiungere tale obiettivo utilizzando lo standard UIPopover di Apple. Tuttavia, potresti rendere abbastanza semplice la tua classe popover personalizzata. Ci sono alcuni esempi su come farlo sia qui su SO e tutorial sul web più ampio (anche alcune soluzioni pronte per il download). Basta mettere 'custom uipopover' in Google ...

+0

Sì, va bene. Ho solo pensato che sarebbe stato bello poter personalizzare veramente l'aspetto di UIPopover. Da quando Apple ha introdotto alcune funzionalità di personalizzazione con UIPopoverBackgroundView, ho pensato che sarebbe stato possibile modificare effettivamente il look di UIPopover. – Sorig

+0

Questo non è il caso. Reimplementare i popover dappertutto è una cattiva idea. –

+1

@BenLachman Cura di dirci perché? La domanda non dice nulla di "dappertutto". Piuttosto, descrive ciò di cui ha bisogno e chiede se un oggetto UIKit soddisfa le necessità. Al momento non lo fece. Non c'è assolutamente alcun motivo per cui far rotolare il proprio elemento in questo caso è una cattiva idea dentro e fuori di sé. – isaac

10

Poiché non esiste un modo elegante per farlo e poiché non voglio riscrivere l'intero UIPopoverController solo per fare questo, ho creato un semplice hack che rimuove il ombra interna sul popover attraversando la struttura di UIView. L'hack è una categoria su UIPopoverController e l'ho inserita nei file per la mia sottoclasse di UIPopoverBackgroundView. Quindi, ecco il codice:

@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 

Quando voglio mostrare il mio popover mi basta chiamare le presentPopoverWithoutInnerShadowFromRect: e presentPopoverWithoutInnerShadowFromBarButtonItem: metodi al posto di quelli standard. NOTA: Ricordarsi di #import <QuartzCore/QuartzCore.h> per il codice per lavorare

+0

Se ho un UITableView all'interno del popover, le maschere degli angoli appaiono quando si scorre. Qualche idea se c'è un modo per aggirare questo? – bromanko

+0

È possibile utilizzare "removeInnerShadow" per rimuovere le maschere degli angoli e l'ombra del popover. Prova a giocarci. Puoi provare a chiamare "removeInnerShadow" continuamente mentre scorri o forse puoi cercare un modo per interrompere lo scrolling aggiungendo di nuovo le maschere degli angoli. – Sorig

+0

Risulta anche l'impostazione del colore di sfondo aiutato: 'subviewA.layer.backgroundColor = [UIColor clearColor] .CGColor;' – bromanko

10

I juest ho creato la mia versione per il progetto su cui sto lavorando.

In sostanza si dovrebbe usare il proprio personalizzato backgroundClass per popover e in questa classe è necessario definire:

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

Dovrebbe essere praticamente a prova di proiettile per gli aggiornamenti iOS.

+1

Funziona bene con popOvers per le viste, che non sono incorporate in NavigationControllers. Non appena il ViewController (che è incorporato nel NavigationController) viene utilizzato per il popover, l'ombra è tornata di nuovo. c'è qualche soluzione a questo? – FrankZp

+0

#FrankZP Cerca la modifica seguente. –

+0

+1 buona soluzione! Una domanda: potresti spiegare un po 'perché funziona mandare un messaggio a '[self class]' (che è un 'objc_class')? – matm

0

Per FrankZp

Questo funziona bene con popovers per le viste, che non sono incorporati in NavigationControllers. Non appena il ViewController (che è incorporato nel NavigationController) viene utilizzato per il popover, l'ombra è di nuovo indietro. c'è qualche soluzione a questo?

Ecco la modifica per l'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

Anche se sono d'accordo in linea di principio che il modo corretto per gestire questa situazione è quello di rotolare il proprio Popover, in questo caso si tratta di un non-problema per versioni più recenti del sistema operativo. Voglio davvero costruire e mantenere la mia implementazione di popover solo per supportare un sistema operativo che alla fine sarà irrilevante? Se lo desideri, considera alcune delle implementazioni open source gratuite sul web.

Personalmente ho studiato i metodi suggeriti qui e ho trovato il mio utilizzando questa pagina come punto di partenza (grazie!). Funziona per entrambi gli scenari (con o senza una barra di navigazione) ed è un po 'più sicuro a mio parere.

Invece di aggiungere un metodo a UIPopoverController, ho aggiunto una routine al mio UIPopoverBackgroundView per cercare le viste offensive utilizzando una route RELATIVA, piuttosto che ABSOLUTE. In breve, dato che il codice ha un riferimento diretto a UIPopoverBackgroundView (self), può navigare su (superview) e quindi giù (sottoview).

Gli alberi vista simile a questa in entrambi gli scenari:

Con UINavigationBar: enter image description here

Senza UINavigationBar: enter image description here

I due punti di vista che ci interessa sono le Viste UILayoutView e UIImage evidenziate in grassetto e sottolineate in ciascun grafico. Possiamo ottenere un riferimento a questi a partire da UIPopoverBackgroundView utilizzando il codice seguente (presuppone ARC). Eseguo questo da layoutSubviews nella mia implementazione 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]; 

Qui utilizzo un blocco per eseguire l'attraversamento delle sottoview per mantenere il codice conciso. Prende una prospettiva come punto di partenza e una serie di nomi di classi. L'array di nomi di classi è la sequenza di classi vista che mi aspetto dove l'indice 0 è il genitore dell'indice 1 e l'indice 1 è il genitore dell'indice 2, ecc. Restituisce la vista rappresentata dall'ultimo elemento nell'array.

+0

Dovresti aggiungere una condizione per evitare di fare tutto questo in iOS 6+ e solo restituire "NO" da "wantsDefaultContentAppearance". –

+0

+1 per spiegazione schematica dettagliata. Puoi dire anche come regolare la dimensione del bordo di UIPopover? – HDdeveloper

+0

Intendi il confine esterno? Ho usato UIPopoverBackgroundView per usare le immagini per specificare l'aspetto del popover. Se intendi il confine interno, l'ho appena rimosso. L'implementazione predefinita utilizza UIImageView, quindi dovrai sostituire l'immagine se desideri semplicemente cambiare il bordo. –