2013-08-08 8 views
5

Sto costruendo una visualizzazione personalizzata che contiene diversi altri sottoview (NSTextField, WebView, ...). Mi piacerebbe che la mia vista personalizzata disegnasse un bordo diverso quando una delle sottoview è la prima a rispondere e agisce come un singolo oggetto che può essere interpretato con voci di menu e scorciatoie da tastiera. Sembra qualcosa di simile:Esiste un modo efficace per rilevare le modifiche al primo soccorritore nelle visualizzazioni secondarie?

+-------------+ 
| NSTextField | 
+-------------+ 
| WebView  | 
+-------------+ 

Finora, ho avuto successo sottoclassi NSTextField e altri per notificare un delegato quando - (BOOL)becomeFirstResponder e - (BOOL)resignFirstResponder sono chiamati. Questo approccio non funziona con WebView, dato che esso contiene molte sottoview - Non posso sottoclassi tutte!

Esiste un modo migliore per rilevare quando le sottoview modificano il loro stato di primo risponditore? O un modo migliore per creare una visualizzazione personalizzata?

+0

Esattamente lo stesso problema qui, hai mai trovato una soluzione? – Kappe

risposta

1

Sia WebViewEditingDelegate metodo sarà chiamata,

dimettersi first responder:

-(void)webViewDidEndEditing:(NSNotification *)notification 
{ 

} 

e quando diventare first responder:

-(BOOL)webView:(WebView *)webView shouldBeginEditingInDOMRange:(DOMRange *)range 
{ 
    return YES; 
} 
+0

Certo sarebbe bello se questa API fosse disponibile per UIWebKit :) – BadPirate

2

Un approccio diverso sarebbe quello di ignorare il metodo -makeFirstResponder: su NSWindow per inviare una notifica.

- (BOOL)makeFirstResponder:(NSResponder *)responder { 
    id previous = self.firstResponder ?: [NSNull null]; 
    id next = responder ?: [NSNull null]; 

    NSDictionary *userInfo = @{ 
    BrFirstResponderPreviousKey: previous, 
    BrFirstResponderNextKey: next, 
    }; 

    [[NSNotificationCenter defaultCenter] postNotificationName:BrFirstResponderWillChangeNotification object:self userInfo:userInfo]; 

    return [super makeFirstResponder:responder]; 
} 

È quindi possibile ascoltare la notifica nella visualizzazione personalizzata o un controller della vista e verificare se i soccorritori precedenti o successivi sono subviews utilizzando -isDescendantOf: e impostare needsDisplay come necessario.

Questa non è una soluzione ideale, tuttavia, poiché la visualizzazione personalizzata non è più autonoma. Funziona per ora, ma si spera che verrà condiviso un approccio migliore.

1

Ho avuto questo problema con iOS/UIWebView, che non implementa makeFirstResponder in UIWindow, né webViewDidEndEditing o shouldBeginEditingInDOMRange. Tuttavia, con l'uso di Swizzling sono stato in grado di creare una categoria di supporto che consente il recupero del primo risponditore corrente e di inviare una notifica ogni volta che il primo soccorritore cambia. Davvero frustrante come tutto questo dovrebbe essere l'API pubblica, ma non lo è, dato che il swizzle non è normalmente un primo goto, ma questo ha funzionato abbastanza bene.

In primo luogo, impostare l'intestazione categoria:

@interface UIResponder (Swizzle) 
+ (UIResponder *)currentFirstResponder; 
- (BOOL)customBecomeFirstResponder; 
@end 

Poi Categoria implementazione

@implementation UIResponder (Swizzle) 
// It's insanity that there is no better way to get a notification when the first responder changes, but there it is. 
static UIResponder *sCurrentFirstResponder; 
+ (UIResponder *)currentFirstResponder { 
    return sCurrentFirstResponder; 
} 

- (BOOL)customBecomeFirstResponder { 
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:2]; 
    if(sCurrentFirstResponder) { 
     [userInfo setObject:sCurrentFirstResponder forKey:NSKeyValueChangeOldKey]; 
    } 
    sCurrentFirstResponder = self; 
    if(sCurrentFirstResponder) { 
     [userInfo setObject:sCurrentFirstResponder forKey:NSKeyValueChangeNewKey]; 
    } 
    [[NSNotificationCenter defaultCenter] postNotificationName:kFirstResponderDidChangeNotification 
                 object:nil 
                 userInfo:userInfo]; 
    return [self customBecomeFirstResponder]; 
} 
@end 

Infine, utilizzando un aiutante come JR Swizzle, scambiare le classi.

#import "JRSwizzle.h" 

- (void)applicationLoaded { 
    if(![UIResponder jr_swizzleMethod:@selector(becomeFirstResponder) withMethod:@selector(customBecomeFirstResponder) error:&error]) { 
     NSLog(@"Error swizzling - %@",error); 
    } 
} 

Pensavo di condividere. Valido in App Store in quanto non utilizza API private, e mentre Apple mette in guardia contro le classi base di swizzling non vi è alcun editto contro di farlo.

Problemi correlati