2012-12-13 13 views
8

Abbiamo ricevuto un progetto ENORME da esternalizzazione che stiamo cercando di "riparare". Ci sono centinaia di controller di visualizzazione all'interno del progetto. Il nostro obiettivo è quello di determinare facilmente quale classe stiamo attualmente guardando sul dispositivo.Registrazione del nome classe di tutti i UIViewControllers in un progetto

La nostra soluzione (che non ha funzionato, da qui la domanda SO) segue.

l'override del metodo viewDidAppear di UIViewController tramite una categoria con questo:

-(void)viewDidAppear:(BOOL)animated 
{ 
    NSLog(@"Current View Class: %@", NSStringFromClass(self.class)); 
    [self viewDidAppear:animated]; 
    //Also tried this: 
    //[super viewDidAppear:animated]; 
} 

questa categoria sarebbe stato messo nel .pch del progetto.

Ciò non richiederebbe alcun codice aggiuntivo da inserire nelle centinaia di controller di visualizzazione ed essere facilmente attivato e disattivato. Non ha funzionato perché, come abbiamo imparato oggi, < meme> non si limita a sostituire un metodo esistente tramite categoria < /meme>.

Cosa ci manca?!?

+0

Questo codice ha un ciclo infinito, non è vero?Dovresti chiamare [super viewDidAppear: animated]; –

+0

@BrunoDomingues poiché questa è una categoria su UIViewController, la chiamata super chiamerebbe viewDidAppear su NSObject (superclasse di UIViewController) che non esiste. – adamweeks

+0

È possibile trovare la vista visibile corrente (controller di visualizzazione) da UIWindow rootViewController. – 9dan

risposta

14

La risposta è di swizzle i metodi! Ecco cosa ci siamo inventati:

#import "UIViewController+Logging.h" 
#import <objc/runtime.h> 

@implementation UIViewController (Logging) 

-(void)swizzled_viewDidAppear:(BOOL)animated 
{ 
    NSLog(@"Current View Class: %@", NSStringFromClass(self.class)); 
    [self swizzled_viewDidAppear:animated]; 
} 

+ (void)load 
{ 
    Method original, swizzled; 

    original = class_getInstanceMethod(self, @selector(viewDidAppear:)); 
    swizzled = class_getInstanceMethod(self, @selector(swizzled_viewDidAppear:)); 

    method_exchangeImplementations(original, swizzled); 

} 
@end 
+0

Lo stavamo usando oggi e c'è un "trucco" che abbiamo incontrato. Se la sottoclasse chiama viewDidAppear ma non chiama [super viewDidAppear], non otterrai un log. – adamweeks

+0

Mi sono appena imbattuto in questo. Per il piacere di chiunque altro di leggere commenti, tutte le sottoclassi di UIViewController DEVONO chiamare [super viewDidAppear], quindi se questo manca, si tratta di un errore del programmatore diverso. – Shinigami

1

I controller di visualizzazione condividono una classe di base comune? se così fosse, potresti metterlo lì nell'implementazione della classe base di [viewDidAppear:]. Se non condividono una base comune, allora forse sarebbe un compito utile in quanto potrebbe essere utile comunque andare avanti (codice di analisi comune, ecc.)

+0

Purtroppo no. Sono tutte semplicemente sottoclassi di UIViewController. Esaminando il metodo swizzling per la soluzione. – adamweeks

+0

So che hai centinaia di controller, ma una buona ricerca/sostituzione potrebbe facilmente aiutarti ad ereditare da una comune classe di controller. Ciò renderebbe facile questo particolare compito e offrirà altri vantaggi. –

0

Si può fare trovare ampia applicazione e sostituire da Xcode, ma non sarà necessariamente trovare ogni caso (ma nemmeno gli approcci che si è tentato). Puoi cercare "[super viewDidLoad];" e sostituire con "[super viewDidLoad]; NSLog (@" Classe di visualizzazione corrente:% @ ", NSStringFromClass (self.class));"

+0

Alla ricerca di una soluzione più semplice che funzioni a livello di applicazione, piuttosto che dover modificare ogni classe come descritto nella domanda. – adamweeks

3

Ecco soluzione per questo

Nel file PCH includono questo

#define UIViewController MyViewController 
#import "MyViewController.h" 

Creare la nuova classe UIViewController sub come

.h file di

#import <UIKit/UIKit.h> 

#ifdef UIViewController 
#undef UIViewController 
#endif 
@interface MyViewController : UIViewController 

@end 
#ifndef UIViewController 
#define UIViewController MyViewController 
#endif 

E. m file

#import "MyViewController.h" 

@implementation MyViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    NSLog(@"Current View Class: %@", NSStringFromClass(self.class)); 
} 

@end 
+0

Ho appena avuto l'opportunità di testare questo codice e funziona pure. Questo metodo ha qualche vantaggio/svantaggio rispetto al metodo swizzling? – adamweeks

+0

Quello che il mio codice sta facendo è che sta sostituendo la super classe 'UIViewController' a' MyViewController'. Non ho trovato alcun inconveniente per entrambi gli approcci in questo momento .. –

+0

Grazie per la soluzione, ancora in attesa di ulteriori informazioni prima di accettare una risposta. – adamweeks

0

L'app utilizza i controller di navigazione per visualizzare i controller di visualizzazione? In tal caso, è possibile utilizzare i metodi del navigationController per segnalare il regolatore di corrente:

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated 
    { 
     [self reportNewController:viewController]; 
    } 

    - (void) reportNewController:(UIViewController *)viewController 
    { 
     NSString *name = viewController.title; 
      NSLog(@"Name is %@",name); 
    }