2013-08-08 19 views
5

Buduję niestandardowy widok, który zawiera kilka innych subviews (NSTextField, WebView, ...). Chciałbym, aby mój niestandardowy widok narysował inną ramkę, gdy jeden z podrzędnych elementów jest pierwszym obiektem odpowiadającym i działa jako pojedynczy element, który można obsługiwać za pomocą elementów menu i skrótów klawiaturowych. Wygląda to mniej więcej tak:Czy istnieje skuteczny sposób na wykrycie zmian w odpowiedziach na pierwszą odpowiedź?

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

Dotychczas miałem sukces subclassing NSTextField i inni powiadomić delegata gdy - (BOOL)becomeFirstResponder i - (BOOL)resignFirstResponder są nazywane. To podejście nie działa z WebView, ponieważ samo w sobie zawiera wiele subviews - nie mogę ich podklasować!

Czy istnieje lepszy sposób na wykrycie, kiedy subviews zmienia status pierwszego respondenta? Lub lepszy sposób tworzenia niestandardowego widoku?

+0

Dokładnie taki sam problem, czy kiedykolwiek znalazłeś rozwiązanie? – Kappe

Odpowiedz

1

Zarówno WebViewEditingDelegate metoda będzie zadzwonić,

Resign pierwszy responder:

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

} 

i kiedy zostać pierwszym responder:

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

Pewnie byłoby miło, gdyby ten interfejs API był dostępny dla UIWebKit :) – BadPirate

2

Innym podejściem byłoby zastąpienie metody -makeFirstResponder: na NSWindow w celu wysłania powiadomienia.

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

Następnie można słuchać do zawiadomienia w widoku niestandardowego lub kontrolera widoku i sprawdzić, czy poprzedni lub następny odpowiedzią są subviews wykorzystujące -isDescendantOf: i ustawić needsDisplay ile potrzeba.

To nie jest idealne rozwiązanie, ponieważ niestandardowy widok nie jest już samowystarczalny. Działa na razie, ale mam nadzieję, że lepsze podejście zostanie udostępnione.

1

miałem ten problem z iOS/UIWebView, który nie implementuje makeFirstResponder w UIWindow ani webViewDidEndEditing lub shouldBeginEditingInDOMRange. Jednak dzięki Swizzlingowi udało mi się stworzyć kategorię pomocników, która pozwala na pobranie aktualnej pierwszej odpowiedzi, a także wysłanie powiadomienia za każdym razem, gdy zmienia się pierwszy reagujący. Naprawdę frustrujące, jak to wszystko powinno być publicznym API, ale nie jest, ponieważ swizzle nie jest zwykle pierwszym goto, ale działało to wystarczająco dobrze.

pierwsze, konfiguracja Twój nagłówek kategoria:

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

Następnie realizacja Kategoria

@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 

Wreszcie, przy użyciu pomocnika jak JR Swizzle, zamienić klas.

#import "JRSwizzle.h" 

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

Myślałem, że się podzielę. Prawidłowy w App Store, ponieważ nie korzysta z prywatnego interfejsu API, a Apple ostrzega przed zawikłaniem klas bazowych, ale nie ma w tym przeszkód.