UITextField: sposta la vista quando appare la tastiera

Attualmente sto lavorando a un’applicazione iPhone con una singola vista, che ha più campi UIText per l’input. Quando la tastiera mostra, si sovrappone ai campi di testo in basso. Così ho aggiunto il corrispondente textFieldDidBeginEditing: metodo, per spostare la vista, che funziona benissimo:

 - (void)textFieldDidBeginEditing:(UITextField *)textField { if ( ( textField != inputAmount ) && ( textField != inputAge ) ) { NSTimeInterval animationDuration = 0.300000011920929; CGRect frame = self.view.frame; frame.origin.y -= kOFFSET_FOR_KEYBOARD; frame.size.height += kOFFSET_FOR_KEYBOARD; [UIView beginAnimations:@"ResizeForKeyboard" context:nil]; [UIView setAnimationDuration:animationDuration]; self.view.frame = frame; [UIView commitAnimations]; } } 

Questo metodo controlla se la fonte del messaggio è uno dei campi di testo che sono visibili quando la tastiera mostra, e in caso contrario, sposta la vista verso l’alto.

Ho anche aggiunto il metodo textFieldDidEndEnditing: che sposta nuovamente la vista verso il basso (e aggiorna alcuni oggetti del modello in base all’input modificato):

 - (void)textFieldDidEndEditing:(UITextField *)textField { if ( ( textField != inputMenge ) && ( textField != inputAlter ) ) { NSTimeInterval animationDuration = 0.300000011920929; CGRect frame = self.view.frame; frame.origin.y += kOFFSET_FOR_KEYBOARD; frame.size.height -= kOFFSET_FOR_KEYBOARD; [UIView beginAnimations:@"ResizeForKeyboard" context:nil]; [UIView setAnimationDuration:animationDuration]; self.view.frame = frame; [UIView commitAnimations]; } // Additional Code } 

Tuttavia, questa soluzione ha un semplice difetto: quando finisco di modificare uno dei campi di testo “nascosti” e tocchiamo un altro campo di testo, la tastiera scompare, la vista si sposta verso il basso, la vista si alza di nuovo e la tastiera riappare.

C’è la possibilità di mantenere la tastiera scomparsa e riapparire tra due modifiche (dei campi di testo “nascosti” – in modo che la vista si muova solo quando il campo di testo selezionato cambia da uno che sarebbe nascosto dalla tastiera a uno che non sarebbe nascosto )?

Questa soluzione è basata su quella di ComSubVie.

vantaggi:

  • Supporta la rotazione del dispositivo – funziona per tutti gli orientamenti;
  • Non codifica i valori per la durata e la curva dell’animazione, li legge dalla notifica della tastiera;
  • Utilizza UIKeyboardWillShowNotification invece di UIKeyboardDidShowNotification per sincronizzare l’animazione della tastiera e le azioni personalizzate;
  • Non usa UIKeyboardBoundsUserInfoKey deprecato;
  • Gestisce il ridimensionamento della tastiera grazie alla pressione del tasto internazionale;
  • Risolta perdita di memoria annullando la registrazione per gli eventi della tastiera;
  • Tutto il codice di gestione della tastiera è incapsulato in una class separata – KBKeyboardHandler ;
  • Flessibilità: la class KBKeyboardHandler può essere facilmente estesa / modificata per soddisfare meglio esigenze specifiche;

limitazioni:

  • Funziona per iOS 4 e versioni successive, necessita di piccole modifiche per supportare versioni precedenti;
  • Funziona per le applicazioni con un singolo UIWindow . Se si utilizzano più UIWindows, potrebbe essere necessario modificare il metodo retrieveFrameFromNotification: .

Uso:

Includere KBKeyboardHandler.h, KBKeyboardHandler.m e KBKeyboardHandlerDelegate.h nel progetto. Implementa il protocollo KBKeyboardHandlerDelegate nel tuo controller di visualizzazione: consiste in un unico metodo, che verrà chiamato quando viene mostrata la tastiera, nascosta o modificata. KBKeyboardHandler e imposta il suo delegato (in genere self). Vedere esempio MyViewController seguito.

KBKeyboardHandler.h :

 #import  #import  @protocol KBKeyboardHandlerDelegate; @interface KBKeyboardHandler : NSObject - (id)init; // Put 'weak' instead of 'assign' if you use ARC @property(nonatomic, assign) id delegate; @property(nonatomic) CGRect frame; @end 

KBKeyboardHandler.m :

 #import "KBKeyboardHandler.h" #import "KBKeyboardHandlerDelegate.h" @implementation KBKeyboardHandler - (id)init { self = [super init]; if (self) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } @synthesize delegate; @synthesize frame; - (void)keyboardWillShow:(NSNotification *)notification { CGRect oldFrame = self.frame; [self retrieveFrameFromNotification:notification]; if (oldFrame.size.height != self.frame.size.height) { CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width, self.frame.size.height - oldFrame.size.height); if (self.delegate) [self notifySizeChanged:delta notification:notification]; } } - (void)keyboardWillHide:(NSNotification *)notification { if (self.frame.size.height > 0.0) { [self retrieveFrameFromNotification:notification]; CGSize delta = CGSizeMake(-self.frame.size.width, -self.frame.size.height); if (self.delegate) [self notifySizeChanged:delta notification:notification]; } self.frame = CGRectZero; } - (void)retrieveFrameFromNotification:(NSNotification *)notification { CGRect keyboardRect; [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect]; self.frame = [[UIApplication sharedApplication].keyWindow.rootViewController.view convertRect:keyboardRect fromView:nil]; } - (void)notifySizeChanged:(CGSize)delta notification:(NSNotification *)notification { NSDictionary *info = [notification userInfo]; UIViewAnimationOptions curve; [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve]; NSTimeInterval duration; [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration]; void (^action)(void) = ^{ [self.delegate keyboardSizeChanged:delta]; }; [UIView animateWithDuration:duration delay:0.0 options:curve animations:action completion:nil]; } @end 

KBKeyboardHandlerDelegate.h :

 @protocol KBKeyboardHandlerDelegate - (void)keyboardSizeChanged:(CGSize)delta; @end 

Esempio MyViewController.h :

 @interface MyViewController : UIViewController ... @end 

Esempio MyViewController.m :

 @implementation MyViewController { KBKeyboardHandler *keyboard; } - (void)dealloc { keyboard.delegate = nil; [keyboard release]; [super dealloc]; } - (void)viewDidLoad { [super viewDidLoad]; keyboard = [[KBKeyboardHandler alloc] init]; keyboard.delegate = self; } - (void)viewDidUnload { [super viewDidUnload]; keyboard.delegate = nil; [keyboard release]; keyboard = nil; } - (void)keyboardSizeChanged:(CGSize)delta { // Resize / reposition your views here. All actions performsd here // will appear animated. // delta is the difference between the previous size of the keyboard // and the new one. // For instance when the keyboard is shown, // delta may has width=768, height=264, // when the keyboard is hidden: width=-768, height=-264. // Use keyboard.frame.size to get the real keyboard size. // Sample: CGRect frame = self.view.frame; frame.size.height -= delta.height; self.view.frame = frame; } 

AGGIORNAMENTO: corretto avviso di iOS 7, grazie a @weienv.

Ho appena risolto questo problema. La soluzione è una combinazione di un UIKeyboardDidShowNotification e UIKeyboardDidHideNotification observer con i metodi textFieldDidBeginEditing: e textFieldDidEndEditing:

Sono necessarie tre variabili aggiuntive, una per memorizzare l’UITextField selezionato corrente (che ho denominato campo attivo), uno per indicare se la vista corrente è stata spostata e una per indicare se la tastiera è visualizzata.

Ecco come appaiono ora i due metodi delegati UITextField :

 - (void)textFieldDidBeginEditing:(UITextField *)textField { activeField = textField; } - (void)textFieldDidEndEditing:(UITextField *)textField { activeField = nil; // Additional Code } 

Quando viene caricata la vista, vengono creati i seguenti due osservatori:

 - (void)viewDidLoad { // Additional Code [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasHidden:) name:UIKeyboardDidHideNotification object:nil]; } 

E i metodi corrispondenti sono implementati come segue:

 - (void)keyboardWasShown:(NSNotification *)aNotification { if ( keyboardShown ) return; if ( ( activeField != inputAmount ) && ( activeField != inputAge ) ) { NSDictionary *info = [aNotification userInfo]; NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey]; CGSize keyboardSize = [aValue CGRectValue].size; NSTimeInterval animationDuration = 0.300000011920929; CGRect frame = self.view.frame; frame.origin.y -= keyboardSize.height-44; frame.size.height += keyboardSize.height-44; [UIView beginAnimations:@"ResizeForKeyboard" context:nil]; [UIView setAnimationDuration:animationDuration]; self.view.frame = frame; [UIView commitAnimations]; viewMoved = YES; } keyboardShown = YES; } - (void)keyboardWasHidden:(NSNotification *)aNotification { if ( viewMoved ) { NSDictionary *info = [aNotification userInfo]; NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey]; CGSize keyboardSize = [aValue CGRectValue].size; NSTimeInterval animationDuration = 0.300000011920929; CGRect frame = self.view.frame; frame.origin.y += keyboardSize.height-44; frame.size.height -= keyboardSize.height-44; [UIView beginAnimations:@"ResizeForKeyboard" context:nil]; [UIView setAnimationDuration:animationDuration]; self.view.frame = frame; [UIView commitAnimations]; viewMoved = NO; } keyboardShown = NO; } 

Questo codice funziona ora come previsto. La tastiera viene chiusa solo quando viene premuto il pulsante Fine, altrimenti rimane visibile e la vista non viene spostata.

Come nota aggiuntiva, penso che sia ansible ottenere dynamicmente animationDuration chiedendo l’object NSNotification , dal momento che ho già giocato con una soluzione simile ma non l’ho fatto funzionare (cosa che fa ora).

Questo controller di visualizzazione deve essere UITextView Delegate ed è necessario impostare self.textview.delegate = self in viewdidload

  -(void) textViewDidBeginEditing:(UITextView *)textView { NSLog(@"%f",self.view.frame.origin.y); [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.25f]; CGRect frame = self.view.frame; frame.origin.y =frame.origin.y -204; [self.view setFrame:frame]; [UIView commitAnimations]; } -(void) textViewDidEndEditing:(UITextView *)textView { [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.25f]; CGRect frame = self.view.frame; frame.origin.y = frame.origin.y + 204; [self.view setFrame:frame]; [UIView commitAnimations]; } 

Ho ottenuto il tuo problema, basta fare una cosa semplice, dare uno sbocco a UIScrollview. imposta una proprietà Tag univoca per ogni campo di testo in vista.

 -(void)textFieldDidBeginEditing:(UITextField *)textField { switch (textField.tag) { case 2: //can be your textfiled tag { CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-150); //set figure y-150 as per your comfirt [scrollview setContentOffset:scrollPoint animated:YES]; }break; case 3: { CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-180); //set figure y-180 as per your comfirt [scrollview setContentOffset:scrollPoint animated:YES]; }break; ... } } -(void)textFieldDidEndEditing:(UITextField *)textField{ if(textField.tag==3){ [scrollview setContentOffset:CGPointZero animated:YES]; } //set the last textfield when you want to disappear keyboard. } 
 Write below code in your view controller. tbl is your table view. -(void)viewWillAppear:(BOOL)animated{ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } -(void) viewWillDisappear:(BOOL)animated { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } #pragma mark - Keyboard Methods -(void)keyboardWillShow:(NSNotification *)notification { // 375 × 667 ( 750 × 1334 ) iPhone 6 //414 × 736 CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height; [UIView animateWithDuration:0.5 animations:^{ tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right); }]; } -(void)keyboardWillChangeFrame:(NSNotification *)notification { CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; // int H = IS_IPHONE_5?504-keyboardRect.size.height:416-keyboardRect.size.height; int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height; [UIView animateWithDuration:0.5 animations:^{ // scroll.frame = rect; tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right); }]; } -(void)keyboardWillHide:(NSNotification *)notification { [UIView animateWithDuration:0.3 animations:^{ // scroll.frame = rect; tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, 0, tbl.contentInset.right); }]; } 

Soluzione abbastanza semplice, funziona con tutte le dimensioni dello schermo

Innanzitutto devi incorporare UITextFields in un UIScrollView. Nel mio caso, avevo diversi UITextFields e un UITextView.

inserisci la descrizione dell'immagine qui

Quindi devi ereditare da UITextFieldDelegate, UITextViewDelegate.

class SettingsVC: UIViewController, UITextFieldDelegate, UITextViewDelegate

Assegna a se stessi i delegati di textfield e textview.

fullNameTextField.delegate = self usernameTextField.delegate = self websiteTextField.delegate = self profileDescription.delegate = self

Quindi utilizzare questo codice:

 var editingTextInput: UIView! override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardShown(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil) } func keyboardShown(notification: NSNotification) { if let infoKey = notification.userInfo?[UIKeyboardFrameEndUserInfoKey], let rawFrame = (infoKey as AnyObject).cgRectValue { let keyboardFrame = view.convert(rawFrame, to: view) let editingTextInputFrame = self.editingTextInput.convert(self.editingTextInput.frame, to: view) if editingTextInputFrame.maxY > keyboardFrame.minY{ let diff = keyboardFrame.minY - editingTextInputFrame.maxY containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true) } } } func textFieldDidBeginEditing(_ textField: UITextField) { self.editingTextInput = textField } func textViewDidBeginEditing(_ textView: UITextView) { self.editingTextInput = textView } func textFieldDidEndEditing(_ textField: UITextField) { containerScrollView.setContentOffset(CGPoint.zero, animated: true) } func textViewDidEndEditing(_ textView: UITextView) { containerScrollView.setContentOffset(CGPoint.zero, animated: true) }