Esecuzione di uno scorrimento di UITableView quando è selezionato il campo di testo


251

Dopo molte prove ed errori, mi arrendo e faccio la domanda. Ho visto molte persone con problemi simili ma non riesco a far funzionare tutte le risposte nel modo giusto.

Ho un UITableViewche è composto da celle personalizzate. Le celle sono composte da 5 campi di testo uno accanto all'altro (una specie di griglia).

Quando provo a scorrere e modificare le celle nella parte inferiore della UITableView, non riesco a posizionare correttamente le mie celle sopra la tastiera.

Ho visto molte risposte parlare di cambiare le dimensioni della vista, ecc ... ma nessuna di queste ha funzionato bene finora.

Qualcuno potrebbe chiarire il modo "giusto" per farlo con un esempio di codice concreto?


11
Questa documentazione di Applle descrive i passaggi per implementare una soluzione per questa domanda. http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
ChrisP

@ChrisP Questo link afferma che non è stato aggiornato per iOS 4.0
Bae

Questo codice può essere utile: gist.github.com/TimMedcalf/9505416
landonandrey

Risposte:


126

Se usi UITableViewController invece di UIViewController, lo farà automaticamente.


13
Hai provato a scoprire che non funzionava? O la soluzione è troppo semplice per farti credere? Basta estendere UITableViewController invece di UIViewController e la cella contenente i campi di testo scorrerà sopra la tastiera ogni volta che i campi di testo diventano il primo risponditore. Nessun codice aggiuntivo necessario.
Sam Ho,

3
Sì, ma soprattutto sull'iPad abbiamo bisogno di un modo per farlo che non coinvolga UITableViewController.
Bob Spryn,

13
Per chiarire, non è una risposta ragionevole dire che ogni volta che usi una vista da tavolo deve essere a schermo intero, specialmente su un iPad. Ci sono orde di esempi di fantastiche app che non lo fanno. Ad esempio, molti dei prodotti Apple, inclusa l'app Contatti sull'iPad.
Bob Spryn,

32
Non funzionerà se si esegue l'override di [super viewWillAppear: YES]. A parte questo, dovrebbe funzionare.
Rambatino,

18
Se si ignora viewWillAppear: (BOOL) animato, non dimenticare di chiamare [super viewWillAppear: animato]; :)
Médéric Petit, l'

93

La funzione che fa lo scroll potrebbe essere molto più semplice:

- (void) textFieldDidBeginEditing:(UITextField *)textField {
    UITableViewCell *cell;

    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
    // Load resources for iOS 6.1 or earlier
        cell = (UITableViewCell *) textField.superview.superview;

    } else {
        // Load resources for iOS 7 or later
        cell = (UITableViewCell *) textField.superview.superview.superview; 
       // TextField -> UITableVieCellContentView -> (in iOS 7!)ScrollView -> Cell!
    }
    [tView scrollToRowAtIndexPath:[tView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

Questo è tutto. Nessun calcolo.


2
E perchè no?! Basta sostituire UITableViewScrollPositionTop con UITableViewScrollPositionMiddle. Devi semplicemente ridimensionare UITableView per regolare l'area visibile, ovviamente.
Mihai Damian,

3
Non sembra funzionare se UITableViewController ha curato il ridimensionamento della vista tabella quando viene mostrata la tastiera: il controller riduce le dimensioni visibili con a contentInset, che apparentemente non viene preso in considerazione quando si richiede visibleRowso indexPathsForVisibleRows.
Julian D.

16
Non funziona per le ultime righe della vista tabella. La tastiera oscurerà comunque tutte le righe che non è possibile scorrere sopra la tastiera.
Alex Zavatone,

3
Per far funzionare il comportamento dello scorrimento automatico nelle ultime righe della tabella, rileva quando queste righe iniziano la modifica e aggiungi un piè di pagina alla fine della vista tabella con una vista vuota di una certa altezza. Ciò consentirà alla vista tabella di scorrere le celle nella posizione corretta.
Sammio2,

10
Arrivare al cellulare attraverso una catena di chiamate per il superview non è affidabile, a meno che non ci si assicuri di arrivare effettivamente al cellulare. Vedi stackoverflow.com/a/17757851/1371070 e stackoverflow.com/a/17758021/1371070
Cezar

70

Sto facendo qualcosa di molto simile è generico, non ho bisogno di calcolare qualcosa di specifico per il tuo codice. Basta controllare le osservazioni sul codice:

In MyUIViewController.h

@interface MyUIViewController: UIViewController <UITableViewDelegate, UITableViewDataSource>
{
     UITableView *myTableView;
     UITextField *actifText;
}

@property (nonatomic, retain) IBOutlet UITableView *myTableView;
@property (nonatomic, retain) IBOutlet UITextField *actifText;

- (IBAction)textFieldDidBeginEditing:(UITextField *)textField;
- (IBAction)textFieldDidEndEditing:(UITextField *)textField;

-(void) keyboardWillHide:(NSNotification *)note;
-(void) keyboardWillShow:(NSNotification *)note;

@end

In MyUIViewController.m

@implementation MyUIViewController

@synthesize myTableView;
@synthesize actifText;

- (void)viewDidLoad 
{
    // Register notification when the keyboard will be show
    [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(keyboardWillShow:)
                                          name:UIKeyboardWillShowNotification
                                          object:nil];

    // Register notification when the keyboard will be hide
    [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(keyboardWillHide:)
                                          name:UIKeyboardWillHideNotification
                                          object:nil];
}

// To be link with your TextField event "Editing Did Begin"
//  memoryze the current TextField
- (IBAction)textFieldDidBeginEditing:(UITextField *)textField
{
    self.actifText = textField;
}

// To be link with your TextField event "Editing Did End"
//  release current TextField
- (IBAction)textFieldDidEndEditing:(UITextField *)textField
{
    self.actifText = nil;
}

-(void) keyboardWillShow:(NSNotification *)note
{
    // Get the keyboard size
    CGRect keyboardBounds;
    [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];

    // Detect orientation
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect frame = self.myTableView.frame;

    // Start animation
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:0.3f];

    // Reduce size of the Table view 
    if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
        frame.size.height -= keyboardBounds.size.height;
    else 
        frame.size.height -= keyboardBounds.size.width;

    // Apply new size of table view
    self.myTableView.frame = frame;

    // Scroll the table view to see the TextField just above the keyboard
    if (self.actifText)
      {
        CGRect textFieldRect = [self.myTableView convertRect:self.actifText.bounds fromView:self.actifText];
        [self.myTableView scrollRectToVisible:textFieldRect animated:NO];
      }

    [UIView commitAnimations];
}

-(void) keyboardWillHide:(NSNotification *)note
{
    // Get the keyboard size
    CGRect keyboardBounds;
    [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];

    // Detect orientation
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect frame = self.myTableView.frame;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:0.3f];

    // Increase size of the Table view 
    if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
        frame.size.height += keyboardBounds.size.height;
    else 
        frame.size.height += keyboardBounds.size.width;

    // Apply new size of table view
    self.myTableView.frame = frame;

    [UIView commitAnimations];
}

@end

Swift 1.2+ versione:

class ViewController: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var activeText: UITextField!
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: Selector("keyboardWillShow:"),
            name: UIKeyboardWillShowNotification,
            object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: Selector("keyboardWillHide:"),
            name: UIKeyboardWillHideNotification,
            object: nil)
    }

    func textFieldDidBeginEditing(textField: UITextField) {
        activeText = textField
    }

    func textFieldDidEndEditing(textField: UITextField) {
        activeText = nil
    }

    func keyboardWillShow(note: NSNotification) {
        if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            var frame = tableView.frame
            UIView.beginAnimations(nil, context: nil)
            UIView.setAnimationBeginsFromCurrentState(true)
            UIView.setAnimationDuration(0.3)
            frame.size.height -= keyboardSize.height
            tableView.frame = frame
            if activeText != nil {
                let rect = tableView.convertRect(activeText.bounds, fromView: activeText)
                tableView.scrollRectToVisible(rect, animated: false)
            }
            UIView.commitAnimations()
        }
    }

    func keyboardWillHide(note: NSNotification) {
        if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            var frame = tableView.frame
            UIView.beginAnimations(nil, context: nil)
            UIView.setAnimationBeginsFromCurrentState(true)
            UIView.setAnimationDuration(0.3)
            frame.size.height += keyboardSize.height
            tableView.frame = frame
            UIView.commitAnimations()
        }
    }
}

usare le notifiche e ottenere l'altezza della tastiera incorporando l'orientamento del dispositivo è stato fantastico, grazie per quello! la parte a scorrimento non ha funzionato per me per qualche motivo, quindi ho dovuto usare questo:[tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
taber

7
Questa è la migliore risposta qui penso. Molto pulito. Solo due cose: 1) il tuo viewDidLoad non sta chiamando [super viewDidLoad] e 2) ho dovuto fare un po 'di tabbar sulla linea frame.size.height. Altrimenti perfetto! Grazie.
Toxaq,

3
Ecco la modifica che toxaq descrive: MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; Quindi sottrai tabBarHeight dall'altezza della tastiera ovunque tu usi l'altezza della tastiera.
Steve N,

1
Se l'utente tocca il campo di testo funziona perfettamente. ma se l'utente tocca un altro campo di testo senza premere il tasto Invio, riduce la dimensione della vista tabella.
Bhavin Ramani,

1
@BhavinRamani è d'accordo. Ho aggiunto una semplice proprietà booleana per ricordare se la tastiera è già visualizzata o meno e saltare la riesecuzione del codice quando non è necessario.
Dirty Henry,

46

La soluzione più semplice per Swift 3 , basata sulla soluzione Bartłomiej Semańczyk :

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

// MARK: Keyboard Notifications

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.2, animations: {
        // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
        self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
    })
}

Un dettaglio minore ... Usare Notificationinvece di NSNotificationsarebbe più "Swift 3-y" :-)
Nicolas Miari

Questo aiuterà a riposizionare se c'è una barra di navigazione - circondare UIView.animate con questo se let - if let frame = self.navigationController? .NavigationBar.frame {let y = frame.size.height + frame.origin.y}
Sean Dev,

quando si verifica la rotazione, si verifica un problema tecnico durante il caricamento e alcune celle scompaiono quando la visualizzazione della tabella viene fatta scorrere manualmente
jothikenpachi,

Buona soluzione grazie! Nota: non è più necessario rimuovere removeObserver.
Nick McConnell,

44

Ho avuto lo stesso problema ma ho notato che appare solo in una vista. Così ho iniziato a cercare le differenze nei controller.

Ho scoperto che il comportamento di scorrimento è impostato nella - (void)viewWillAppear:(BOOL)animatedsuper istanza.

Quindi assicurati di implementare in questo modo:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // your code
}

E non importa se usi UIViewControllero UITableViewController; ho verificato inserendo a UITableViewcome sottoview di self.view in UIViewController. Era lo stesso comportamento. La vista non consentiva di scorrere se [super viewWillAppear:animated];mancava la chiamata .


1
Questo ha funzionato in modo eccellente. Mi chiedevo perché la gente dicesse che UITableView l'avrebbe fatto per me e questo l'aveva risolto. Grazie!
olivaresF

5
Ho avuto anche questo problema, questa risposta dovrebbe arrivare fino in cima!
Amiel Martin,

Ho perso così tanto tempo cercando di capirlo da solo ... grazie;)
budidino,

+1 stava iniziando a piangere un po ', avevo quella linea ma avevo anche bisogno di [tableViewController viewWillAppear: animato]; perché sto aggiungendo un UITableViewController a un UIViewController. non più lacrime :)
Colin Lamarre,

41

potrei essermi perso questo, poiché non ho letto l'intero post qui, ma quello che mi è venuto in mente sembra ingannevolmente semplice. non l'ho messo attraverso lo strizzatore, test in tutte le situazioni, ma sembra che dovrebbe funzionare bene.

basta regolare il contenutoInset della vista tabella dall'altezza della tastiera, quindi scorrere la cella verso il basso:

- (void)keyboardWasShown:(NSNotification *)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
    self.myTableView.contentInset = contentInsets;
    self.myTableView.scrollIndicatorInsets = contentInsets;

    [self.myTableView scrollToRowAtIndexPath:self.currentField.indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

ed ovviamente

- (void)keyboardWasHidden:(NSNotification *)aNotification
{
    [UIView animateWithDuration:.3 animations:^(void) 
    {
        self.myTableView.contentInset = UIEdgeInsetsZero;
        self.myTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
    }];
}

è troppo semplice? mi sto perdendo qualcosa? finora funziona bene per me, ma come ho detto, non l'ho fatto passare attraverso lo strizzatore ...


IMO, questa è la soluzione migliore. L'unica cosa che cambierei è la tua durata hardcoded in[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Andy

È molto semplice. Ma un problema che trovo è che non animerà il cambiamento contentInsete cambierà nettamente i limiti di scorrimento.
Geek

Questo ha funzionato meglio per me, tuttavia, alcuni problemi. 1) Non so dove sia possibile trovare "currentField.indexPath", quindi ho dovuto salvare indexPath.row come tag del campo e creare successivamente IndexPath. 2) Non funziona per le righe nella parte superiore della tabella, le scorre fuori dallo schermo. Ho dovuto aggiungere del codice per scorrere solo se indexPath di currentField è maggiore di quello che può stare sullo schermo. 3) ho dovuto usare kbSize.Width (anziché l'altezza) su iPad se è orizzontale
Travis M.

scusa, siamo così abituati al nostro codice che a volte ci dimentichiamo, eh? currentField è il campo di testo corrente con cui sto lavorando, e indexPath è un'estensione che ho aggiunto alla classe che aggiunge semplicemente un NSIndexPath, quindi so in quale cella si trova.
mickm

Questa è la strada da percorrere, non spostare i frame semplicemente modificando le proprietà della tabella.
Nextorlg

35

Penso di aver trovato la soluzione per abbinare il comportamento delle app di Apple.

Innanzitutto, nella tua vista, WillAppear: iscriviti alle notifiche della tastiera, così sai quando la tastiera mostrerà e si nasconderà, e il sistema ti dirà le dimensioni della tastiera, ma non dimenticare di annullare la registrazione nella tua vista.

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

Implementa metodi simili ai seguenti in modo da adattare le dimensioni del tuo tableView affinché corrisponda all'area visibile una volta che la tastiera mostra. Qui sto monitorando lo stato della tastiera separatamente in modo da poter scegliere da solo quando riportare il tableView a tutta altezza, poiché ricevi queste notifiche ad ogni cambio di campo. Non dimenticare di implementare keyboardWillHide: e scegli un posto appropriato per correggere le dimensioni di tableView.

-(void) keyboardWillShow:(NSNotification *)note
{
    CGRect keyboardBounds;
    [[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardBounds];
    keyboardHeight = keyboardBounds.size.height;
    if (keyboardIsShowing == NO)
    {
        keyboardIsShowing = YES;
        CGRect frame = self.view.frame;
        frame.size.height -= keyboardHeight;

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationBeginsFromCurrentState:YES];
        [UIView setAnimationDuration:0.3f];
        self.view.frame = frame;
        [UIView commitAnimations];
    }
}

Ora ecco il bit di scorrimento, elaboriamo prima alcune dimensioni, quindi vediamo dove siamo nell'area visibile e impostiamo il rettangolo su cui vogliamo scorrere per essere la mezza vista sopra o sotto la metà del campo di testo in base su dove si trova nella vista. In questo caso, abbiamo un array di UITextField e un enum che ne tiene traccia, quindi moltiplicando la rigaHeight per il numero di riga ci dà l'offset effettivo del frame all'interno di questa vista esterna.

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    CGRect frame = textField.frame;
    CGFloat rowHeight = self.tableView.rowHeight;
    if (textField == textFields[CELL_FIELD_ONE])
    {
        frame.origin.y += rowHeight * CELL_FIELD_ONE;
    }
    else if (textField == textFields[CELL_FIELD_TWO])
    {
        frame.origin.y += rowHeight * CELL_FIELD_TWO;
    }
    else if (textField == textFields[CELL_FIELD_THREE])
    {
        frame.origin.y += rowHeight * CELL_FIELD_THREE;
    }
    else if (textField == textFields[CELL_FIELD_FOUR])
    {
        frame.origin.y += rowHeight * CELL_FIELD_FOUR;
    }
    CGFloat viewHeight = self.tableView.frame.size.height;
    CGFloat halfHeight = viewHeight / 2;
    CGFloat midpoint = frame.origin.y + (textField.frame.size.height / 2);
    if (midpoint < halfHeight)
    {
        frame.origin.y = 0;
        frame.size.height = midpoint;
    }
    else
    {
        frame.origin.y = midpoint;
        frame.size.height = midpoint;
    }
    [self.tableView scrollRectToVisible:frame animated:YES];
}

Questo sembra funzionare abbastanza bene.


Bella soluzione. Grazie per averlo pubblicato.
Alex Reynolds,

2
UIKeyboardBoundsUserInfoKeyè obsoleto a partire da iOS 3.2. Vedi la mia soluzione di seguito che funziona su tutte le versioni iOS correnti ≥ 3.0. / @ iPhoneDev
Ortwin Gentz,

Questo era più complicato di quanto doveva essere. La risposta di @utente91083 è stata semplice e funziona.
Richard Brightwell,

1
C'è un piccolo problema in questa soluzione. keyboardWillShow si chiama AFTER textFieldDidBeginEditing, quindi quando vogliamo scorrere fino ad alcune celle, il frame di tableView non è ancora cambiato, quindi non funzionerà
HiveHicks

35

Se è possibile utilizzare UITableViewController, si ottiene la funzionalità gratuitamente. A volte, tuttavia, questa non è un'opzione, in particolare se hai bisogno di più viste non solo di UITableView.

Alcune delle soluzioni presentate qui non funzionano su iOS ≥4, alcune non funzionano su iPad o in modalità orizzontale, alcune non funzionano per le tastiere Bluetooth (dove non vogliamo scorrere), altre no funziona quando si passa tra più campi di testo. Quindi, se scegli una soluzione, assicurati di testare questi casi. Questa è la soluzione che usiamo utilizzata in InAppSettingsKit :

- (void)_keyboardWillShow:(NSNotification*)notification {
    if (self.navigationController.topViewController == self) {
        NSDictionary* userInfo = [notification userInfo];

        // we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0
        NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
        if (!keyboardFrameValue) {
            keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"];
        }

        // Reduce the tableView height by the part of the keyboard that actually covers the tableView
        CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
        if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
            windowRect = IASKCGRectSwap(windowRect);
        }
        CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
        if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
            viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute);
        }
        CGRect frame = _tableView.frame;
        frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
        [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
        _tableView.frame = frame;
        [UIView commitAnimations];

        UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
        NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];

        // iOS 3 sends hide and show notifications right after each other
        // when switching between textFields, so cancel -scrollToOldPosition requests
        [NSObject cancelPreviousPerformRequestsWithTarget:self];

        [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
    }
}

- (void) scrollToOldPosition {
  [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)_keyboardWillHide:(NSNotification*)notification {
    if (self.navigationController.topViewController == self) {
        NSDictionary* userInfo = [notification userInfo];

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
        [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
        _tableView.frame = self.view.bounds;
        [UIView commitAnimations];

        [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];
    }
}   

Ecco il codice completo della classe in InAppSettingsKit. Per testarlo, utilizzare il riquadro secondario "Elenco completo" in cui è possibile testare gli scenari sopra menzionati.


Non so se sia utile usare stringhe anziché costanti, perché se Apple viene all'idea di cambiare la stringa internamente per alcuni motivi, la tua soluzione non funziona più. Allo stesso modo non hai ricevuto un avviso quando diventa obsoleto. Penso

@iPortable: non è l'ideale, lo so. Puoi suggerire una soluzione migliore che funziona su tutte le versioni ≥3.0?
Ortwin Gentz,

1
Funziona come un incantesimo, ma non per UIInterfaceOrientationPortraitUpsideDown. Quindi anche il calcolo della riduzione dell'altezza deve essere basato al rovescio: CGFloat reduceHeight = keyboardRect.size.height - (CGRectGetMinY (viewRectAbsolute) - CGRectGetMinY (windowRect));
Klaas,

Questo ha dei difetti visivi molto evidenti sul mio iPad e sul Simulatore (4.3). Troppo evidente da usare. :(
Bob Spryn,

Mi piace che questa soluzione rappresenterà una barra degli strumenti nella parte inferiore dello schermo.
pdemarest,

24

La soluzione più semplice per Swift :

override func viewDidLoad() {
    super.viewDidLoad()

    searchBar?.becomeFirstResponder()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillShow(_:)), name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillHide(_:)), name: UIKeyboardDidHideNotification, object: nil)
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size.height {
            tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    UIView.animateWithDuration(0.2, animations: { self.table_create_issue.contentInset = UIEdgeInsetsMake(0, 0, 0, 0) })
    // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
    }

Funziona perfettamente, calcoli minimi necessari. Ho aggiunto del codice che ripristina gli inserti di tabella per completare questa risposta.
Vitalii,

La migliore soluzione grazie. Ho inviare una versione Swift 3 qui: stackoverflow.com/a/41040630/1064438
squall2022

Soluzione super perfetta mai vista, ne ho provate altre ma presenta alcuni problemi. La tua soluzione funziona perfettamente su iOS 10.2.
Wangdu Lin,

8

Spero che abbiate già una soluzione per leggere tutti quelli. Ma ho trovato la mia soluzione come segue. Mi aspetto che tu abbia già una cella con UITextField. Quindi, preparandoti, tieni l'indice di riga nel tag del campo di testo.

cell.textField.tag = IndexPath.row;

Creare activeTextFieldun'istanza di UITextFieldambito globale come di seguito:

@interface EditViewController (){

    UITextField *activeTextField;

}

Quindi, ora copia e incolla il mio codice alla fine. E inoltre, non dimenticare di aggiungereUITextFieldDelegate

#pragma mark - TextField Delegation

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{

    activeTextField = textField;

    return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField{

    activeTextField = nil;

}

Registra la tastiera notifications

#pragma mark - Keyboard Activity

- (void)registerForKeyboardNotifications

{

    [[NSNotificationCenter defaultCenter] addObserver:self

                                         selector:@selector(keyboardWasShown:)

                                             name:UIKeyboardDidShowNotification object:nil];



    [[NSNotificationCenter defaultCenter] addObserver:self

                                         selector:@selector(keyboardWillBeHidden:)

                                             name:UIKeyboardWillHideNotification object:nil];



}

Tastiera delle maniglie Notifications:

Chiamato quando UIKeyboardDidShowNotificationviene inviato.

- (void)keyboardWasShown:(NSNotification*)aNotification

{

    NSDictionary* info = [aNotification userInfo];

    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

    NSIndexPath *currentRowIndex = [NSIndexPath indexPathForRow:activeTextField.tag inSection:0];

    [self.tableView scrollToRowAtIndexPath:currentRowIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];

}

Chiamato quando UIKeyboardWillHideNotificationviene inviato

- (void)keyboardWillBeHidden:(NSNotification*)aNotification

{

    UIEdgeInsets contentInsets = UIEdgeInsetsZero;

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

}

Ora rimane solo una cosa, chiama il registerForKeyboardNotificationsmetodo nel ViewDidLoadmetodo come segue:

- (void)viewDidLoad {

    [super viewDidLoad];

    // Registering keyboard notification

    [self registerForKeyboardNotifications];

    // Your codes here...

}

Hai finito, spero che la tua textFieldsvolontà non sia più nascosta dalla tastiera.


6

Combinando e riempendo gli spazi vuoti da diverse risposte (in particolare Ortwin Gentz, utente 98013) e un altro post, questo funzionerà immediatamente per SDK 4.3 su un iPad in modalità verticale o orizzontale:

@implementation UIView (FindFirstResponder)
- (UIResponder *)findFirstResponder
{
  if (self.isFirstResponder) {        
    return self;     
  }

  for (UIView *subView in self.subviews) {
    UIResponder *firstResponder = [subView findFirstResponder];
    if (firstResponder != nil) {
      return firstResponder;
    }
  }

  return nil;
}
@end

@implementation MyViewController

- (UIResponder *)currentFirstResponder {
  return [self.view findFirstResponder];
}

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

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
  [textField resignFirstResponder];
  return NO;
}

- (void)textFieldDidBeginEditing:(UITextField *)textField {
  UITableViewCell *cell = (UITableViewCell*) [[textField superview] superview];
  [_tableView scrollToRowAtIndexPath:[_tableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)keyboardWillShow:(NSNotification*)notification {
  if ([self currentFirstResponder] != nil) {
    NSDictionary* userInfo = [notification userInfo];

    // we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0
    NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
    if (!keyboardFrameValue) {
      keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"];
    }

    // Reduce the tableView height by the part of the keyboard that actually covers the tableView
    CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
    CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
    CGRect frame = _tableView.frame;
    if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
      windowRect = CGRectMake(windowRect.origin.y, windowRect.origin.x, windowRect.size.height, windowRect.size.width);
      viewRectAbsolute = CGRectMake(viewRectAbsolute.origin.y, viewRectAbsolute.origin.x, viewRectAbsolute.size.height, viewRectAbsolute.size.width);
    }
    frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
    _tableView.frame = frame;
    [UIView commitAnimations];

    UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
    NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];

    // iOS 3 sends hide and show notifications right after each other
    // when switching between textFields, so cancel -scrollToOldPosition requests
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    _topmostRowBeforeKeyboardWasShown = [[_tableView indexPathsForVisibleRows] objectAtIndex:0];
    [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
  }
}

- (void) scrollToOldPosition {
  [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)keyboardWillHide:(NSNotification*)notification {
  if ([self currentFirstResponder] != nil) {

    NSDictionary* userInfo = [notification userInfo];

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
    _tableView.frame = self.view.bounds;
    [UIView commitAnimations];

    [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];
  }
}   

@end

Ho usato questo codice in iOS 4.x bene, ma in iOS5 si arresta in modo anomalo in scrollToOldPosition perché _topmostRowBeforeKeyboardWasShown è già stato liberato in quel momento. Non sono sicuro di quale sia la soluzione. Probabilmente ricorda l'indice anziché l'oggetto.
Thomas Tempelmann,

5

Se usi una uitableview per posizionare i tuoi campi di testo ( da Jeff Lamarche ), puoi semplicemente scorrere la vista della tabella usando il metodo delegato in questo modo.

(Nota: i miei campi di testo sono memorizzati in un array con lo stesso indice della riga nella vista tabella)

- (void) textFieldDidBeginEditing:(UITextField *)textField
    {

        int index;
        for(UITextField *aField in textFields){

            if (textField == aField){
                index = [textFields indexOfObject:aField]-1;
            }
        }

         if(index >= 0) 
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];

        [super textFieldDidBeginEditing:textField];
    }

Non si aggiorna il frame tableView. Quindi, le barre di scorrimento e il comportamento di scorrimento sono errati quando viene mostrata la tastiera. Vedi la mia soluzione
Ortwin Gentz,

5

Le notifiche da tastiera funzionano, ma il codice di esempio di Apple presuppone che la vista di scorrimento sia la vista principale della finestra. Questo di solito non è il caso. Devi compensare le barre delle schede, ecc., Per ottenere l'offset giusto.

È più facile di quanto sembri. Ecco il codice che uso in un UITableViewController. Ha due variabili di istanza, hiddenRect e keyboardShown.

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification {
    if (keyboardShown)
        return;

    NSDictionary* info = [aNotification userInfo];

    // Get the frame of the keyboard.
    NSValue *centerValue = [info objectForKey:UIKeyboardCenterEndUserInfoKey];
    NSValue *boundsValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
    CGPoint keyboardCenter = [centerValue CGPointValue];
    CGRect keyboardBounds = [boundsValue CGRectValue];
    CGPoint keyboardOrigin = CGPointMake(keyboardCenter.x - keyboardBounds.size.width / 2.0,
                                         keyboardCenter.y - keyboardBounds.size.height / 2.0);
    CGRect keyboardScreenFrame = { keyboardOrigin, keyboardBounds.size };


    // Resize the scroll view.
    UIScrollView *scrollView = (UIScrollView *) self.tableView;
    CGRect viewFrame = scrollView.frame;
    CGRect keyboardFrame = [scrollView.superview convertRect:keyboardScreenFrame fromView:nil];
    hiddenRect = CGRectIntersection(viewFrame, keyboardFrame);

    CGRect remainder, slice;
    CGRectDivide(viewFrame, &slice, &remainder, CGRectGetHeight(hiddenRect), CGRectMaxYEdge);
    scrollView.frame = remainder;

    // Scroll the active text field into view.
    CGRect textFieldRect = [/* selected cell */ frame];
    [scrollView scrollRectToVisible:textFieldRect animated:YES];

    keyboardShown = YES;
}


// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
    // Reset the height of the scroll view to its original value
    UIScrollView *scrollView = (UIScrollView *) self.tableView;
    CGRect viewFrame = [scrollView frame];
    scrollView.frame = CGRectUnion(viewFrame, hiddenRect);

    keyboardShown = NO;
}

UIKeyboardCenterEndUserInfoKeye UIKeyboardBoundsUserInfoKeysono obsoleti a partire da iOS 3.2. Vedi la mia soluzione di seguito che funziona su tutte le versioni iOS correnti ≥ 3.0.
Ortwin Gentz,

5

Se si utilizza Three20, quindi utilizzare la autoresizesForKeyboardproprietà. Basta impostare il -initWithNibName:bundlemetodo del controller di visualizzazione

self.autoresizesForKeyboard = YES

Questo si occupa di:

  1. Ascolto delle notifiche da tastiera e regolazione della cornice della vista tabella
  2. Scorrendo fino al primo risponditore

Fatto e fatto.


che cos'è Three20 qui? Puoi specificarlo?
Mubin Mall,

5

Il mio approccio:

Prima di sottoclassi UITextField e aggiungo una proprietà indexPath. Nel metodo cellFor ... consegno la proprietà indexPath.

Quindi aggiungo il seguente codice:

UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:textField.indexPath];

CGPoint cellPoint = [cell convertPoint:textField.center toView:self.tableView];
[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, cellPoint.y-50);}];

al textFieldShould / WillBegin ... ecc.

Quando la tastiera scompare, devi invertirla con:

[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, 0);}];

4

Una soluzione più fluida. Scivola nei metodi delegati di UITextField, quindi non richiede messaggi con le notifiche di UIKeyboard.

Note di attuazione:

kSettingsRowHeight - l'altezza di un UITableViewCell.

offsetTarget e offsetThreshold sono generati da kSettingsRowHeight. Se si utilizza un'altezza di riga diversa, impostare tali valori sulla proprietà y del punto. [alt: calcola l'offset della riga in modo diverso.]

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
CGFloat offsetTarget    = 113.0f; // 3rd row
CGFloat offsetThreshold = 248.0f; // 6th row (i.e. 2nd-to-last row)

CGPoint point = [self.tableView convertPoint:CGPointZero fromView:textField];

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

CGRect frame = self.tableView.frame;
if (point.y > offsetThreshold) {
    self.tableView.frame = CGRectMake(0.0f,
                      offsetTarget - point.y + kSettingsRowHeight,
                      frame.size.width,
                      frame.size.height);
} else if (point.y > offsetTarget) {
    self.tableView.frame = CGRectMake(0.0f,
                      offsetTarget - point.y,
                      frame.size.width,
                      frame.size.height);
} else {
    self.tableView.frame = CGRectMake(0.0f,
                      0.0f,
                      frame.size.width,
                      frame.size.height);
}

[UIView commitAnimations];

return YES;

}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];

[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

CGRect frame = self.tableView.frame;
self.tableView.frame = CGRectMake(0.0f,
                  0.0f,
                  frame.size.width,
                  frame.size.height);

[UIView commitAnimations];

return YES;

}


4

Utilizzare il UITextField's delegatemetodo:

veloce

func textFieldShouldBeginEditing(textField: UITextField) -> bool {
  let txtFieldPosition = textField.convertPoint(textField.bounds.origin, toView: yourTableViewHere)
  let indexPath = yourTablViewHere.indexPathForRowAtPoint(txtFieldPosition)
  if indexPath != nil {
     yourTablViewHere.scrollToRowAtIndexPath(indexPath!, atScrollPosition: .Top, animated: true)
  }
  return true
}

Objective-C

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
  CGPoint txtFieldPosition = [textField convertPoint:CGPointZero toView: yourTablViewHere];
  NSLog(@"Begin txtFieldPosition : %@",NSStringFromCGPoint(txtFieldPosition));
  NSIndexPath *indexPath = [yourTablViewHere indexPathForRowAtPoint:txtFieldPosition];

  if (indexPath != nil) {
     [yourTablViewHere scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
  }
  return YES;
}

Ciao, sto avendo problemi a farlo funzionare su Swift. I miei UITextFields si sono connessi a UITableViewCell. Se implemento questo codice nel mio UIViewController non ho accesso a UITextFields. Qualche idea?
Vetuka,

4

Swift 4.2 soluzione completa

Ho creato GIST con un set di protocolli che semplifica il lavoro con l'aggiunta di spazio extra quando la tastiera viene visualizzata, nascosta o modificata.

Caratteristiche :

  • Funziona correttamente con i cambiamenti della cornice della tastiera (ad es. Cambi di altezza della tastiera come emojii → tastiera normale).
  • Supporto TabBar e ToolBar per l'esempio di UITableView (in altri esempi si ricevono inserimenti errati).
  • Durata dinamica dell'animazione (non codificata).
  • Approccio orientato al protocollo che potrebbe essere facilmente modificato per i tuoi scopi.

uso

Esempio di utilizzo di base nel controller di visualizzazione che contiene alcune visualizzazioni di scorrimento (la visualizzazione tabella è supportata anche ovviamente).

class SomeViewController: UIViewController {
  @IBOutlet weak var scrollView: UIScrollView!

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    addKeyboardFrameChangesObserver()
  }

  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    removeKeyboardFrameChangesObserver()
  }
}

extension SomeViewController: ModifableInsetsOnKeyboardFrameChanges {
  var scrollViewToModify: UIScrollView { return scrollView }
}

Nucleo: frame cambia osservatore

Il protocollo KeyboardChangeFrameObservergenererà un evento ogni volta che viene cambiato il frame della tastiera (incluso mostrare, nascondere, cambiare il frame).

  1. Chiama addKeyboardFrameChangesObserver()a viewWillAppear()o metodo simile.
  2. Chiama removeKeyboardFrameChangesObserver()a viewWillDisappear()o metodo simile.

Implementazione: vista a scorrimento

ModifableInsetsOnKeyboardFrameChangesprotocollo aggiunge il UIScrollViewsupporto al protocollo principale. Cambia gli inserti della vista di scorrimento quando viene cambiata la cornice della tastiera.

La tua classe ha bisogno di impostare la vista a scorrimento, i propri inserti saranno aumentati / diminuiti con i cambi di cornice della tastiera.

var scrollViewToModify: UIScrollView { get }

3

Dato che hai campi di testo in una tabella, il modo migliore è davvero di ridimensionare la tabella: devi impostare tableView.frame in modo che sia più piccolo in altezza in base alle dimensioni della tastiera (penso intorno a 165 pixel) e quindi espanderlo di nuovo quando la tastiera viene ignorata.

Opzionalmente puoi anche disabilitare l'interazione dell'utente per tableView anche in quel momento, se non vuoi che l'utente scorra.


In secondo luogo, e mi registro per UIKeyboardWillShowNotification per trovare dinamicamente le dimensioni della tastiera.
benzado,

Tuttavia, il numero restituito dall'oggetto di notifica non funziona. O almeno non in 2.2, il numero restituito era errato e ho dovuto codificare il valore 165 per regolare correttamente l'altezza (era fuori da cinque a dieci pixel)
Kendall Helmstetter Gelner

2

Funziona perfettamente, e anche su iPad.

- (BOOL)textFieldShouldReturn:(UITextField *)textField 
{

    if(textField == textfield1){
            [accountName1TextField becomeFirstResponder];
        }else if(textField == textfield2){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield3 becomeFirstResponder];

        }else if(textField == textfield3){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield4 becomeFirstResponder];

        }else if(textField == textfield4){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield5 becomeFirstResponder];

        }else if(textField == textfield5){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield6 becomeFirstResponder];

        }else if(textField == textfield6){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:4 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield7 becomeFirstResponder];

        }else if(textField == textfield7){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:5 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield8 becomeFirstResponder];

        }else if(textField == textfield8){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:6 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield9 becomeFirstResponder];

        }else if(textField == textfield9){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:7 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textField resignFirstResponder];
        }

Perché stai usando iffing e usi casi speciali per ogni campo di testo? Identifica ciascun campo di testo da NSIndexPath della cella e modifica quella cattiva istruzione if in 2 righe di codice. Volete davvero una chiamata cellForRowAtIndexPath e quindi ottenere textField dalla cella.
Alex Zavatone,

In realtà considerando quanto sia incredibilmente instabile questa situazione su iOS, penso che sia OK scrivere codice "completamente svolto, ridicolmente letterale" per questa situazione.
Fattie,

Considerando questa risposta è stata data oltre 6 anni fa.
WrightsCS

2

Ho provato quasi lo stesso approccio e ho escogitato un codice più semplice e più piccolo per lo stesso. Ho creato un iTextView di IBOutlet e associato a UITextView nell'IB.

 -(void)keyboardWillShow:(NSNotification *)notification
    {
        NSLog(@"Keyboard");
        CGRect keyFrame = [[[notification userInfo]objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue];

        [UIView beginAnimations:@"resize view" context:nil];
        [UIView setAnimationCurve:1];
        [UIView setAnimationDuration:1.0];
        CGRect frame = iTableView.frame;
        frame.size.height = frame.size.height -  keyFrame.size.height;
        iTableView.frame = frame;
        [iTableView scrollRectToVisible:frame animated:YES];
        [UIView commitAnimations];

    }

2

Quindi, dopo ore di estenuante lavoro nel tentativo di utilizzare queste soluzioni attuali (e completamente fallendo), ho finalmente fatto funzionare bene le cose e le ho aggiornate per utilizzare i nuovi blocchi di animazione. La mia risposta è interamente basata sulla risposta di Ortwin sopra .

Quindi, per qualsiasi motivo, il codice sopra non funzionava per me. La mia configurazione sembrava abbastanza simile agli altri, ma forse perché ero su un iPad o 4.3 ... nessuna idea. Stava facendo un po 'di stravagante matematica e sparando la mia vista da tavolo dallo schermo.

Vedi il risultato finale della mia soluzione: http://screencast.com/t/hjBCuRrPC (ignora la foto. :-P)

Quindi sono andato alla radice di ciò che stava facendo Ortwin, ma ho cambiato il modo in cui stava facendo un po 'di matematica per sommare l'origine .y & size.height della mia vista del tavolo con l'altezza della tastiera. Quando sottraggo l'altezza della finestra da quel risultato, mi dice quanta intersezione ho in corso. Se è maggiore di 0 (ovvero c'è qualche sovrapposizione) eseguo l'animazione dell'altezza della cornice.

Inoltre, ci sono stati alcuni problemi di ridisegno che sono stati risolti da 1) In attesa di scorrere fino alla cella fino al completamento dell'animazione e 2) utilizzando l'opzione UIViewAnimationOptionBeginFromCurrentState quando si nasconde la tastiera.

Un paio di cose da notare.

  • _topmostRowBeforeKeyboardWasShown e _originalFrame sono variabili di istanza dichiarate nell'intestazione.
  • self.guestEntryTableView è il mio tableView (sono in un file esterno)
  • IASKCGRectSwap è il metodo di Ortwin per capovolgere le coordinate di un frame
  • Aggiornare l'altezza di tableView solo se verrà mostrato almeno 50px
  • Dato che non sono in un UIViewController non ho self.view, quindi restituisco tableView alla sua cornice originale

Ancora una volta, non mi sarei avvicinato a questa risposta se Ortwin non ne avesse fornito il nocciolo. Ecco il codice:

- (IBAction)textFieldDidBeginEditing:(UITextField *)textField
{
    self.activeTextField = textField;

    if ([self.guestEntryTableView indexPathsForVisibleRows].count) {
        _topmostRowBeforeKeyboardWasShown = (NSIndexPath*)[[self.guestEntryTableView indexPathsForVisibleRows] objectAtIndex:0];
    } else {
        // this should never happen
        _topmostRowBeforeKeyboardWasShown = [NSIndexPath indexPathForRow:0 inSection:0];
        [textField resignFirstResponder];
    }
}

- (IBAction)textFieldDidEndEditing:(UITextField *)textField
{
    self.activeTextField = nil;
}

- (void)keyboardWillShow:(NSNotification*)notification {
    NSDictionary* userInfo = [notification userInfo];

    NSValue* keyboardFrameValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];

    // Reduce the tableView height by the part of the keyboard that actually covers the tableView
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
    CGRect viewRectAbsolute = [self.guestEntryTableView convertRect:self.guestEntryTableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
    CGRect keyboardFrame = [keyboardFrameValue CGRectValue];
    if (UIInterfaceOrientationLandscapeLeft == orientation ||UIInterfaceOrientationLandscapeRight == orientation ) {
        windowRect = IASKCGRectSwap(windowRect);
        viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute);
        keyboardFrame = IASKCGRectSwap(keyboardFrame);
    }

    // fix the coordinates of our rect to have a top left origin 0,0
    viewRectAbsolute = FixOriginRotation(viewRectAbsolute, orientation, windowRect.size.width, windowRect.size.height);

    CGRect frame = self.guestEntryTableView.frame;
    _originalFrame = self.guestEntryTableView.frame;

    int remainder = (viewRectAbsolute.origin.y + viewRectAbsolute.size.height + keyboardFrame.size.height) - windowRect.size.height;

    if (remainder > 0 && !(remainder > frame.size.height + 50)) {
        frame.size.height = frame.size.height - remainder;
        float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
        [UIView animateWithDuration: duration
                        animations:^{
                            self.guestEntryTableView.frame = frame;
                        }
                        completion:^(BOOL finished){
                            UITableViewCell *textFieldCell = (UITableViewCell*) [[self.activeTextField superview] superview];
                            NSIndexPath *textFieldIndexPath = [self.guestEntryTableView indexPathForCell:textFieldCell];
                            [self.guestEntryTableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
                        }];
    }

}

- (void)keyboardWillHide:(NSNotification*)notification {
    NSDictionary* userInfo = [notification userInfo];
    float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    [UIView animateWithDuration: duration
                          delay: 0.0
                        options: (UIViewAnimationOptionBeginFromCurrentState)
                     animations:^{
                         self.guestEntryTableView.frame = _originalFrame;
                     }
                     completion:^(BOOL finished){
                         [self.guestEntryTableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
                     }];

}   

#pragma mark CGRect Utility function
CGRect IASKCGRectSwap(CGRect rect) {
    CGRect newRect;
    newRect.origin.x = rect.origin.y;
    newRect.origin.y = rect.origin.x;
    newRect.size.width = rect.size.height;
    newRect.size.height = rect.size.width;
    return newRect;
}

CGRect FixOriginRotation(CGRect rect, UIInterfaceOrientation orientation, int parentWidth, int parentHeight) {
    CGRect newRect;
    switch(orientation)
    {
        case UIInterfaceOrientationLandscapeLeft:
            newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), rect.origin.y, rect.size.width, rect.size.height);
            break;
        case UIInterfaceOrientationLandscapeRight:
            newRect = CGRectMake(rect.origin.x, parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height);
            break;
        case UIInterfaceOrientationPortrait:
            newRect = rect;
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height);
            break;
    }
    return newRect;
}

Aggiunta la mia funzione FixOriginRotation che corregge il sistema di coordinate della vista prima di aggiornare il suo frame, ecc. Penso che questo sia parte del motivo per cui all'inizio avevo problemi. Non ero a conoscenza del sistema di coordinate della finestra iOS ruotato con il dispositivo!
Bob Spryn,

2

Questo soluton funziona per me, PER FAVORE nota la linea

[tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES];

Puoi modificare il valore 160 in modo che corrisponda al tuo lavoro

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect bkgndRect = activeField.superview.frame;
                        bkgndRect.size.height += kbSize.height;
     [activeField.superview setFrame:bkgndRect];
     [tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
   activeField = textField;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
 {
     activeField = nil;
 }
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    tableView.contentInset = contentInsets;
    tableView.scrollIndicatorInsets = contentInsets;
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect bkgndRect = activeField.superview.frame;
    //bkgndRect.size.height += kbSize.height;
    [activeField.superview setFrame:bkgndRect];
    [tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height) animated:YES];
}

2

Discussione molto interessante, ho anche affrontato lo stesso problema potrebbe essere peggio perché

  1. Stavo usando una cella personalizzata e il campo di testo era all'interno di quello.
  2. Ho dovuto usare UIViewController per soddisfare le mie esigenze, quindi non posso sfruttare UITableViewController.
  3. Avevo i criteri di filtro / ordinamento nella mia cella della tabella, cioè le tue celle continuano a cambiare e tenere traccia del percorso dell'indice e tutto non aiuta.

Quindi leggi i thread qui e implementato la mia versione, che mi ha aiutato a trasferire i miei contenuti su iPad in modalità orizzontale . Ecco il codice (questo non è assolutamente infallibile, ma ha risolto il mio problema) Per prima cosa devi avere un delegato nella tua classe di cella personalizzata, che inizia la modifica, invia il campo di testo al tuo viewcontroller e imposta activefield = theTextField lì

// ATTUATO SOLO PER MANEGGIARE LA MODALITÀ DEL PAESAGGIO

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect aRect = myTable.frame;

    CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width);

    aRect.size.height -= kbSize.height+50;
// This will the exact rect in which your textfield is present
        CGRect rect =  [myTable convertRect:activeField.bounds fromView:activeField];
// Scroll up only if required
    if (!CGRectContainsPoint(aRect, rect.origin) ) {


            [myTable setContentOffset:CGPointMake(0.0, rect.origin.y) animated:YES];

    }


}

// Chiamato quando viene inviato UIKeyboardWillHideNotification

- (void)keyboardWillHide:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    myTable.contentInset = contentInsets;
    myTable.scrollIndicatorInsets = contentInsets;
    NSDictionary* info = [aNotification userInfo];
    CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width);
    CGRect bkgndRect = activeField.superview.frame;
    bkgndRect.size.height += kbSize.height;
    [activeField.superview setFrame:bkgndRect];
    [myTable setContentOffset:CGPointMake(0.0, 10.0) animated:YES];
}

-anoop4real


2

Ho appena risolto un problema del genere da solo dopo aver segnalato una vasta gamma di soluzioni trovate tramite Google e Stack Overflow.

Innanzitutto, assicurati di aver impostato un IBOutlet di UIScrollView, quindi dai un'occhiata da vicino a Apple Doc: Gestione della tastiera . Infine, se puoi scorrere lo sfondo, ma la tastiera copre ancora i campi di testo, dai un'occhiata a questo pezzo di codice:

// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;

if (aRect.size.height < activeField.frame.origin.y+activeField.frame.size.height) {

    CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y+activeField.frame.size.height-aRect.size.height);

    [scrollView setContentOffset:scrollPoint animated:YES];

La differenza principale tra questo pezzo e quello di Apple sta nella condizione if. Credo che il calcolo di Apple della distanza di scorrimento e delle condizioni per stabilire se i campi di testo coperti dalla tastiera non sono accurati, quindi ho apportato la mia modifica come sopra.

Fammi sapere se funziona


2

Un esempio in Swift, utilizzando il punto esatto del campo di testo da Ottieni indexPath di UITextField in UITableViewCell con Swift :

func textFieldDidBeginEditing(textField: UITextField) {
    let pointInTable = textField.convertPoint(textField.bounds.origin, toView: self.accountsTableView)
    let textFieldIndexPath = self.accountsTableView.indexPathForRowAtPoint(pointInTable)
    accountsTableView.scrollToRowAtIndexPath(textFieldIndexPath!, atScrollPosition: .Top, animated: true)
}

1

Un altro metodo semplice (funziona solo con una sezione)

//cellForRowAtIndexPath
UItextField *tf;
[cell addSubview:tf];
tf.tag = indexPath.row;
tf.delegate = self;

//textFieldDidBeginEditing:(UITextField *)text
[[self.tableView scrollToRowsAtIndexPath:[NSIndexPath indexPathForRow:text.tag in section:SECTIONINTEGER] animated:YES];

1

Se UITableView è gestito da una sottoclasse di UITableViewController e non UITableView, e il delegato del campo di testo è UITableViewController, dovrebbe gestire automaticamente tutto lo scorrimento - tutti questi altri commenti sono molto difficili da implementare nella pratica.

Per un buon esempio, vedi il progetto del codice di esempio Apple: TaggedLocations.

Puoi vedere che scorre automaticamente, ma non sembra esserci alcun codice che lo faccia. Questo progetto ha anche celle di visualizzazione tabella personalizzate, quindi se si sviluppa l'applicazione con essa come guida, si dovrebbe ottenere il risultato desiderato.


1

Ecco come ho fatto questo lavoro, che è una miscela delle risposte di Sam Ho e Marcel W e alcune delle mie correzioni di errori fatte al mio codice schifoso. Stavo usando un UITableViewController. La tabella ora si ridimensiona correttamente quando viene mostrata la tastiera.

1) In viewDidLoadho aggiunto:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) Avevo dimenticato di chiamare gli superequivalenti in viewWillAppeare awakeFromNib. Ho aggiunto questi in.


1

UITableViewControllerfa lo scorrimento automaticamente, anzi. La differenza rispetto all'utilizzo di a UIViewControllerè che devi creare Navbar-Buttonitems a livello di codice usando NavigationController, quando si usa a TableViewController.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.