2009-12-29 13 views
10

Sto usando un UITextView per replicare approssimativamente la casella di testo SMS sopra la tastiera. Sto usando un UITextView invece di un campo in modo che possa espandersi con più linee.Come spostare il suggerimento di correzione UITextView sopra il testo

Il problema è che, nel mio UITextView, i suggerimenti di correzione compaiono nello sotto il del testo, causandone la parziale oscuramento dalla tastiera.

Nell'app SMS, i suggerimenti vengono visualizzati sopra il testo. Il posizionamento non sembra essere una proprietà di UITextView o UITextInputTraits.

Qualche idea su come replicare questo comportamento? Grazie!

+0

Solo due scelte: 1) utilizzando metodi privati ​​o 2) spostando UITextView abbastanza in alto da non oscurare la bolla di correzione. – bentford

risposta

8

Il problema è che la tastiera è implementato come un UIWindow separata, piuttosto che come vista all'interno dell'UIWindow principale, quindi il layout con esso è complicato. Ecco alcune indicazioni nella giusta direzione:

  • Hunt attraverso la proprietà dell'applicazione -windows per trovare il UITextEffectsWindow finestra privata e capire la sua cornice. Questa è la tastiera
  • Cerca nelle sottoview di TextView per trovare la vista privata UIAutocorrectInlinePrompt. Questa è la bolla di correzione automatica.
  • Spostare la visualizzazione secondaria in una vista wrapper separata (aggiunta a TextView) e quindi spostare la vista di wrapper in modo che si trovi sopra la suddetta finestra della tastiera.

Noterete due citazioni di "privato" sopra. Questo porta tutti i caveat rilevanti. Non ho idea del motivo per cui Apple ha permesso al problema di persistere quando anche le loro app hanno dovuto aggirarlo.

+1

Mi chiedo ... sta attraversando le sottoview senza fare esplicito riferimento alle classi private considerate una violazione? Voglio dire, presumo che estende UIView giusto? Cosa succede se mi capita di cercare attraverso l'albero di UIViews e cambiare la posizione di uno di loro? Non sembra "privato" per me, ma forse Apple non è d'accordo ... – DougW

+0

Le visualizzazioni secondarie trasversali sono completamente basate su API pubbliche. Ma non importa cosa finirai per affidarti a comportamenti non documentati. Il principale pericolo qui è che un aggiornamento potrebbe spezzarti, quindi nel codice di produzione vorrei fallire con grazia. Non prenderei in considerazione nessuna di queste sottoclassi private per essere fortemente stabili, dal momento che sono entrambe un po 'funky, e le cose più funky hanno il rischio di essere ridisegnate da Apple. Che ci crediate o no, Apple non rende le cose private solo per intenderci :) Molto spesso, sono le cose che Apple ha in programma di fare il refactoring. Prendilo per quello che vale. –

+0

Per caso hai qualche codice di esempio? Sono curioso di sapere se hai usato il metodo swizzling o cosa potresti aver fatto per implementare questo ... – marcc

0

Se la parte inferiore del tuo UITextView cancella la tastiera, dovresti essere in grado di ridimensionare il tuo UITextView per essere abbastanza alto da vedere le correzioni. Le correzioni non vengono visualizzate al di fuori del frame di UITextView.

Se si desidera simulare ciò che si sta ricevendo nell'app di SMS (correzioni sopra), probabilmente sarà necessario eseguire il rollover.

-2

Assicurarsi che il delegato del controller di visualizzazione stia ascoltando la notifica quando viene visualizzata la tastiera in modo da ridimensionare UITextView in modo che la tastiera non oscuri UITextView. Quindi la tua correzione non sarà oscurata dalla tastiera. Vedi:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/12641-uitextview-scroll-while-editing.html

Ecco una copia del codice da quella pagina nel caso in cui il link originale è rotto:

// the amount of vertical shift upwards keep the Notes text view visible as the keyboard appears 
#define kOFFSET_FOR_KEYBOARD     140.0 

// the duration of the animation for the view shift 
#define kVerticalOffsetAnimationDuration  0.50 

- (IBAction)textFieldDoneEditing:(id)sender 
{ 
    [sender resignFirstResponder]; 
} 

- (IBAction)backgroundClick:(id)sender 
{ 
    [latitudeField resignFirstResponder]; 
    [longitudeField resignFirstResponder]; 
    [notesField resignFirstResponder]; 

    if (viewShifted) 
    { 
     [UIView beginAnimations:nil context:NULL]; 
     [UIView setAnimationDuration:kVerticalOffsetAnimationDuration]; 

     CGRect rect = self.view.frame; 
     rect.origin.y += kOFFSET_FOR_KEYBOARD; 
     rect.size.height -= kOFFSET_FOR_KEYBOARD; 
     self.view.frame = rect; 

     [UIView commitAnimations]; 

     viewShifted = FALSE; 
    }  
} 

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView 
{ 
    if (!viewShifted) {  // don't shift if it's already shifted 

     [UIView beginAnimations:nil context:NULL]; 
     [UIView setAnimationDuration:kVerticalOffsetAnimationDuration]; 

     CGRect rect = self.view.frame;  
     rect.origin.y -= kOFFSET_FOR_KEYBOARD; 
     rect.size.height += kOFFSET_FOR_KEYBOARD; 
     self.view.frame = rect; 

     [UIView commitAnimations]; 

     viewShifted = TRUE; 
    } 
    return YES; 
} 
3

Effettuando la ricerca di UIAutocorrectInlinePrompt in un layout sottoposto a override o swizzled, è possibile modificare il layout della correzione in modo che appaia sopra. Puoi eseguire questa operazione senza chiamare API private cercando le viste secondarie di particolari classi posizionate come ti aspetteresti. Questo esempio mostra quale vista è quale, controlla che la correzione non sia già sopra il testo e sposta la correzione sopra e la disegna sulla finestra in modo che non sia limitata da UITextView stesso. Ovviamente se la mela modifica l'implementazione sottostante, questo non riuscirà a spostare la correzione. Aggiungi questo all'implementazione overriden o swizzled layoutSubViews.

- (void) moveSpellingCorrection { 
for (UIView *view in self.subviews) 
{ 
    if ([[[view class] description] isEqualToString:@"UIAutocorrectInlinePrompt"]) 
    { 
    UIView *correctionShadowView = nil; // [view correctionShadowView]; 

    for (UIView *subview in view.subviews) 
    { 
    if ([[[subview class] description] isEqualToString:@"UIAutocorrectShadowView"]) 
    { 
    correctionShadowView = subview; 
    break; 
    } 
    } 

    if (correctionShadowView) 
    { 
    UIView *typedTextView = nil; //[view typedTextView]; 
    UIView *correctionView = nil; //[view correctionView]; 

    for (UIView *subview in view.subviews) 
    { 
    if ([[[subview class] description] isEqualToString:@"UIAutocorrectTextView"]) 
    { 
     if (CGRectContainsRect(correctionShadowView.frame,subview.frame)) 
     { 
     correctionView = subview; 
     } 
     else 
     { 
     typedTextView = subview; 
     } 
    } 

    } 
    if (correctionView && typedTextView) 
    { 

    CGRect textRect = [typedTextView frame]; 
    CGRect correctionRect = [correctionView frame]; 
    if (textRect.origin.y < correctionRect.origin.y) 
    { 
     CGAffineTransform moveUp = CGAffineTransformMakeTranslation(0,-50.0); 
     [correctionView setTransform: moveUp]; 
     [correctionShadowView setTransform: moveUp]; 

     CGRect windowPos = [self convertRect: view.frame toView: nil ]; 
     [view removeFromSuperview]; 
     [self.window addSubview: view]; 
     view.frame = windowPos; 
    } 

    } 
    } 

    } 

} 
} 
+0

Grazie per il codice. È decisamente interessante, ma sono d'accordo con alcuni degli altri commenti che è troppo imprevedibile per uso generale. – DougW

+1

Ciò richiede una correzione: non tiene conto dell'orientamento dell'interfaccia. – Eiko

+0

Sembrava funzionare bene, ma la riparazione ha causato il passaggio della vista automatica a una posizione y molto bassa dopo aver toccato un secondo tasto (cioè quando il suo contenuto è esteso). Qualche idea del perché? –

0

Mettere il metodo seguito, adjustAutocorrectPromptView in layoutSubviews lavorato per me in verticale e orizzontale. Ho una categoria che fornisce i metodi bottom e top in vista ma tu hai l'idea.

NSArray * subviewsWithDescription(UIView *view, NSString *description) 
{ 
    return [view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"class.description == '%@'", description]]]; 
} 

- (void) adjustAutocorrectPromptView; 
{ 
    UIView *autocorrectPromptView = [subviewsWithDescription(self, @"UIAutocorrectInlinePrompt") lastObject]; 

    if (! autocorrectPromptView) 
    { 
     return; 
    } 

    UIView *correctionShadowView = [subviewsWithDescription(autocorrectPromptView, @"UIAutocorrectShadowView") lastObject]; 

    if (! correctionShadowView) 
    { 
     return; 
    } 

    UIView *typedTextView = nil; //[view typedTextView]; 
    UIView *correctionView = nil; //[view correctionView]; 

    for (UIView *subview in subviewsWithDescription(autocorrectPromptView, @"UIAutocorrectTextView")) 
    { 
     if (CGRectContainsRect(correctionShadowView.frame,subview.frame)) 
     { 
      correctionView = subview; 
     } 
     else 
     { 
      typedTextView = subview; 
     } 
    } 

    if (correctionView && typedTextView) 
    { 
     if (typedTextView.top < correctionView.top) 
     { 
      correctionView.bottom = typedTextView.top; 
      correctionShadowView.center = correctionView.center; 
     } 
    } 
} 
1

effettivamente facendo

textview.scrollEnabled = NO; 

imposterà la bolla sulla parte superiore del testo ... l'avvertenza è che si perde lo scorrimento, nel mio caso non è stato un problema a causa di un campo di testo havinng solo per immissione con limite di caratteri

+0

+1 combinato con contentSize è possibile disabilitare/abilitare lo scorrimento quando è necessario. – EsbenB

+0

Questo non lo fa per me. iOS9.3.1 in esecuzione sul simulatore di iPhone 6s, ho ancora il testo di correzione automatica qui sotto. –

+0

sì, questo è stato fatto sul soggezione di iOS7, avrei dovuto controllare questa soluzione se funziona ancora su nuovi dispositivi –

1

In realtà, la tastiera utilizza semplicemente il risultato di - [UITextInput textInputView] per determinare dove posizionare la vista di correzione (e per chiedere se la visualizzazione supporta la correzione). Quindi tutto quello che dovete fare è questo:

- (UIView *)textInputView { 
    for (UIWindow *window in [UIApplication sharedApplication].windows) { 
    if ([window isKindOfClass:NSClassFromString(@"UITextEffectsWindow")] && 
     window != self.window) { 
     return window; 
    } 
    } 

    // Fallback just in case the UITextEffectsWindow has not yet been created. 
    return self; 
} 

Si noti che è probabile che anche bisogno di aggiornare - [UITextInput firstRectForRange:] per utilizzare il sistema della finestra/dispositivo di coordinate, in modo da poter fare questo:

- (CGRect)firstRectForRange:(CoreTextTokenTextRange *)range { 
    CGRect firstRect = [self firstRectForRangeInternal:range]; 

    return [self convertRect:firstRect toView:[self textInputView]]; 
} 

(Nel contesto precedente, self è una classe che implementa UITextInput).

Problemi correlati