Come ridimensionare UITextView su iOS quando appare una tastiera?

C’è UITextView inserito nella scheda in UITabBarController (su iPhone).

  1. Riempi UITextView con molte linee.
  2. Mostra una tastiera per modificare il testo.

Cosa succede? La tastiera nasconde metà di UITextView con il cursore. Imansible modificare il testo come risultato.

Come risolvere il problema per tutti i dispositivi mobili Apple (con diversa risoluzione dello schermo)? Grazie mille per l’aiuto!

Il miglior risultato è stato raggiunto dal seguente codice. Inoltre, non dimenticare di impostare il colore di sfondo su UIView e posizionare UITextView prima di altri controlli sullo schermo superiore (ad es. UITabBar).

La modifica di un testo alla fine non è ancora perfetta ora. Potresti provare a migliorare.

FirstViewController.h:

 @interface FirstViewController : UIViewController { IBOutlet UIBarButtonItem *buttonDone; IBOutlet UITextView *textView; UITabBarController* tabBarController; // set from superview in AppDelegate (MainWindow.xib) } @property (nonatomic, retain) UITabBarController* tabBarController; 

FirstViewController.m:

 @synthesize tabBarController; - (void)viewDidAppear:(BOOL)animated { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } - (void)viewWillDisappear:(BOOL)animated { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up { NSDictionary* userInfo = [aNotification userInfo]; NSTimeInterval animationDuration; UIViewAnimationCurve animationCurve; CGRect keyboardEndFrame; [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve]; [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration]; [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame]; [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:animationDuration]; [UIView setAnimationCurve:animationCurve]; CGRect newFrame = textView.frame; CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil]; keyboardFrame.size.height -= tabBarController.tabBar.frame.size.height; newFrame.size.height -= keyboardFrame.size.height * (up?1:-1); textView.frame = newFrame; [UIView commitAnimations]; } - (void)keyboardWillShown:(NSNotification*)aNotification { buttonDone.enabled = true; [self moveTextViewForKeyboard:aNotification up:YES]; } - (void)keyboardWillHide:(NSNotification*)aNotification { buttonDone.enabled = false; [self moveTextViewForKeyboard:aNotification up:NO]; } 

PS È difficile codificare per iOS senza stackoverflow …

Con il layout automatico, è molto più semplice (a condizione che tu comprenda il layout automatico) per gestire:

Invece di provare a identificare e ridimensionare le viste interessate, è sufficiente creare una cornice principale per tutti i contenuti della vista. Quindi, se viene visualizzato il kbd, ridimensionate il fotogramma e, se avete impostato correttamente i vincoli, la vista riorganizzerà tutte le sue viste secondarie. Non c’è bisogno di giocherellare con un sacco di codice difficile da leggere per questo.

In effetti, in una domanda simile ho trovato un link a questo eccellente tutorial su questa tecnica.

Ho riscontrato diversi problemi nel tentativo di far scorrere la visualizzazione del testo e animare correttamente sia per iOS 7 che per iOS 8 e con la nuova funzione QuickType. All’inizio mi sono concentrato sull’animazione degli insiemi di visualizzazione a scorrimento, ma il comportamento differiva tra iOS 7 e 8 e non riuscivo a farlo funzionare correttamente per entrambi.

Poi ho capito che posso semplificare le cose semplicemente concentrandomi sul frame e questo ha funzionato con un codice molto più semplice. In sintesi:

  • registrati per UIKeyboardDidChangeFrameNotification (questo notificherà quando viene visualizzato / nascosto anche il QuickType).
  • capire quanto spazio verticale è necessario per modificare la cornice della vista del testo per.
  • animare la modifica della dimensione del fotogramma.

Ecco un codice che illustra quanto sopra:

 - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrameWithNotification:) name:UIKeyboardDidChangeFrameNotification object:nil]; } - (void)keyboardDidChangeFrameWithNotification:(NSNotification *)notification { CGFloat keyboardVerticalIncrease = [self keyboardVerticalIncreaseForNotification:notification]; [self animateTextViewFrameForVerticalOffset:keyboardVerticalIncrease]; } - (CGFloat)keyboardVerticalIncreaseForNotification:(NSNotification *)notification { CGFloat keyboardBeginY = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].origin.y; CGFloat keyboardEndY = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y; CGFloat keyboardVerticalIncrease = keyboardBeginY - keyboardEndY; return keyboardVerticalIncrease; } - (void)animateTextViewFrameForVerticalOffset:(CGFloat)offset { CGFloat constant = self.bottomConstraint.constant; CGFloat newConstant = constant + offset; self.bottomConstraint.constant = newConstant; [self.view layoutIfNeeded]; [UIView animateWithDuration:0.5 animations:^{ [self.view layoutIfNeeded]; }]; } 

Una breve nota sull’animazione. Ho usato Autolayout, quindi ho scelto di animare la vista del testo NSAutoLayoutConstraint, non direttamente la cornice. E per farlo, chiamo [self.view layoutIfNeeded] prima e all’interno del blocco di animazione. Questo è il modo corretto di animare i vincoli. Ho trovato questo suggerimento qui .

Vale la pena notare che la risposta di upvoted funziona solo se il dispositivo è in modalità verticale (e non capovolta), in altre modalità i limiti vanno male. Credo che potresti risolvere questo problema utilizzando i limiti per la correzione, ma non ho potuto farlo funzionare in modo che la regolazione seguente funzionasse per me:

 - (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up { NSDictionary* userInfo = [aNotification userInfo]; NSTimeInterval animationDuration; UIViewAnimationCurve animationCurve; CGRect keyboardEndFrame; [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve]; [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration]; [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame]; [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:animationDuration]; [UIView setAnimationCurve:animationCurve]; CGRect newFrame = self.view.frame; if (keyboardEndFrame.size.height >keyboardEndFrame.size.width) { //we must be in landscape if (keyboardEndFrame.origin.x==0) { //upside down so need to flip origin newFrame.origin = CGPointMake(keyboardEndFrame.size.width, 0); } newFrame.size.width -= keyboardEndFrame.size.width * (up?1:-1); } else { //in portrait if (keyboardEndFrame.origin.y==0) { //upside down so need to flip origin newFrame.origin = CGPointMake(0, keyboardEndFrame.size.height); } newFrame.size.height -= keyboardEndFrame.size.height * (up?1:-1); } self.view.frame = newFrame; [UIView commitAnimations]; } 

Innanzitutto aggiungere alcuni metodi di tastiera a NSNotificationCenter defaultCenter

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:self.view.window]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:self.view.window]; 

allora puoi cambiare le misure:

 - (void)keyboardWillShow:(NSNotification *)notif { [thetextView setFrame:CGRectMake(20, 49, 280, 187)]; //Or where ever you want the view to go } - (void)keyboardWillHide:(NSNotification *)notif { [thetextView setFrame:CGRectMake(20, 49, 280, 324)]; //return it to its original position } 
 - (void)registerKeyboardNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } - (void)unregisterKeyboardNotifications { [[NSNotificationCenter defaultCenter] removeObserver:self]; } -(void) keyboardWillHide:(NSNotification *)note { //adjust frame } -(void) keyboardWillShow:(NSNotification *)note { //adjust frame } 

e annullare la registrazione della notifica anche nel dealloc

 - (void)unregisterKeyboardNotifications { [[NSNotificationCenter defaultCenter] removeObserver:self]; } 

Anni passati, la domanda è ancora attuale. Apple sicuramente dovrebbe gestire tutte queste cose da sé. Ma non è così. Ecco la nuova soluzione basata sulla documentazione ufficiale di Apple più correzioni di bug. Supporta iOS 8, iOS 9, inputAccessoryView ed è pronto per le nuove versioni di iOS e nuovi dispositivi.

 /* Apple's solution to resize keyboard but with accessory view support */ - (void)keyboardDidShow:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.origin.y; UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0); editor.contentInset = contentInsets; editor.scrollIndicatorInsets = contentInsets; } - (void)keyboardWillHide:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; editor.contentInset = contentInsets; editor.scrollIndicatorInsets = contentInsets; // button to hide the keyboard buttonDone.enabled = false; } /* Fix issues with size classs and accessory view */ - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; // fix incorrect size of the inputAccessoryView when size class changed // willTransitionToTraitCollection and traitCollectionDidChange can't help us if (editor && editor.inputAccessoryView && !editor.inputAccessoryView.hidden) { [editor resignFirstResponder]; } } /* Hide accessory view if a hardware keyboard is present */ #define gThresholdForHardwareKeyboardToolbar 160.f // it's minimum height of the software keyboard on iPhone 4 in landscape mode - (bool)isExternalKeyboard:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.origin.y; return keyboardHeight < gThresholdForHardwareKeyboardToolbar; } - (void)keyboardWillShow:(NSNotification*)aNotification { if ([self isExternalKeyboard:aNotification]) { // hardware keyboard is present if (editor && editor.inputAccessoryView) { editor.inputAccessoryView.hidden = true; } } else { // only on-screen keyboard if (editor && editor.inputAccessoryView) { editor.inputAccessoryView.hidden = false; } } // button to hide the keyboard buttonDone.enabled = true; } 

In breve, registra la notifica della tastiera e fai il tuo ridimensionamento quando sei avvisato.

Come follow-up, la tecnica in cui si aggiorna il frame quando si verifica la notifica della tastiera non funziona per iOS 7. Per una soluzione alternativa, vedere quanto segue:

Come ridimensionare UITextView quando viene mostrata la tastiera con iOS 7

Ho provato la risposta migliore qui ma ho trovato un problema. Se hai un altro campo di testo sulla stessa pagina, fai clic sul campo di testo, mostra la tastiera. Noterai che la visualizzazione del testo si restringe. Tuttavia, se si fa clic sulla visualizzazione del testo ora, si noterà che le dimensioni della visualizzazione del testo si riducono di nuovo mentre non dovrebbe.

La mia soluzione a questo problema è di mantenere una proprietà nel controller della vista che rappresenta lo stato della tastiera (mostrato / nascosto). Se la tastiera è visibile, la visualizzazione del testo non dovrebbe essere ridotta. Nel caso in cui si utilizzino tastiere di dimensioni diverse per diversi input di testo, è necessario mantenere anche le vecchie dimensioni della tastiera.

Tieni presente che anche questa soluzione non ha tenuto conto di un orientamento diverso, il che potrebbe influire sul modo in cui si calcola la dimensione della visualizzazione del testo.

 @implementation MyViewController { BOOL keyboardShown; NSInteger keyboardHeight; } - (void)moveTextViewForKeyboard:(NSNotification*)aNotification up: (BOOL) up{ NSDictionary* userInfo = [aNotification userInfo]; NSTimeInterval animationDuration; UIViewAnimationCurve animationCurve; CGRect keyboardEndFrame; [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve]; [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration]; [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame]; [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:animationDuration]; [UIView setAnimationCurve:animationCurve]; CGRect newFrame = self.textView.frame; CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil]; NSInteger oldHeight = self->keyboardShown ? self->keyboardHeight : 0; NSInteger newHeight = up ? keyboardFrame.size.height : 0; NSInteger change = oldHeight - newHeight; self->keyboardShown = up; self->keyboardHeight = keyboardFrame.size.height; newFrame.size.height += change; self.textView.frame = newFrame; [UIView commitAnimations]; }