Erstellen eines UITableView-Bildlaufs, wenn ein Textfeld ausgewählt ist


251

Nach vielen Versuchen und Irrtümern gebe ich auf und stelle die Frage. Ich habe viele Leute mit ähnlichen Problemen gesehen, kann aber nicht alle Antworten richtig verstehen.

Ich habe eine, UITableViewdie aus benutzerdefinierten Zellen besteht. Die Zellen bestehen aus 5 Textfeldern nebeneinander (ähnlich einem Raster).

Wenn ich versuche, die Zellen am unteren Rand der zu scrollen und zu bearbeiten UITableView, kann ich meine Zellen nicht richtig über der Tastatur positionieren.

Ich habe viele Antworten gesehen, die über das Ändern der Ansichtsgröße usw. sprachen, aber bisher hat keine davon gut funktioniert.

Könnte jemand anhand eines konkreten Codebeispiels den "richtigen" Weg dazu klären?


11
In dieser Applle-Dokumentation werden die Schritte zum Implementieren einer Lösung für diese Frage beschrieben. http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
ChrisP

@ChrisP Dieser Link besagt, dass es nicht für iOS 4.0 aktualisiert wurde
Bae

Dieser Code kann hilfreich sein: gist.github.com/TimMedcalf/9505416
landonandrey

Folgen Sie unter URL, es wird funktionieren: stackoverflow.com/questions/48922266/…
Venkatesh G

Antworten:


126

Wenn Sie UITableViewController anstelle von UIViewController verwenden, wird dies automatisch durchgeführt.


13
Haben Sie versucht und festgestellt, dass das nicht funktioniert? Oder ist die Lösung zu einfach für Sie zu glauben? Erweitern Sie einfach den UITableViewController anstelle von UIViewController, und die Zelle mit den Textfeldern wird über die Tastatur gescrollt, wenn die Textfelder zum Ersthelfer werden. Kein zusätzlicher Code erforderlich.
Sam Ho

3
Ja, aber besonders auf dem iPad benötigen wir eine Möglichkeit, die den UITableViewController nicht einbezieht.
Bob Spryn

13
Zur Verdeutlichung ist es keine vernünftige Antwort zu sagen, dass jedes Mal, wenn Sie eine Tabellenansicht verwenden, diese im Vollbildmodus angezeigt werden muss, insbesondere auf einem iPad. Es gibt unzählige Beispiele für großartige Apps, die dies nicht tun. Zum Beispiel viele von Apple, einschließlich der Kontakte-App auf dem iPad.
Bob Spryn

32
Es funktioniert nicht, wenn Sie [super viewWillAppear: YES] überschreiben. Davon abgesehen sollte es funktionieren.
Rambatino

18
Wenn Sie viewWillAppear: (BOOL) animiert überschreiben, vergessen Sie nicht, [super viewWillAppear: animiert] aufzurufen. :)
Médéric Petit

93

Die Funktion zum Scrollen könnte viel einfacher sein:

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

Das ist es. Überhaupt keine Berechnungen.


2
Und warum nicht?! Ersetzen Sie einfach UITableViewScrollPositionTop durch UITableViewScrollPositionMiddle. Sie müssen nur die UITableView neu skalieren, um den sichtbaren Bereich anzupassen.
Mihai Damian

3
Scheint nicht zu funktionieren, wenn ein UITableViewController sich um die Größenänderung der Tabellenansicht gekümmert hat, wenn die Tastatur angezeigt wird: Der Controller reduziert die sichtbare Größe mit a contentInset, was bei der Anfrage nach visibleRowsoder anscheinend nicht berücksichtigt wird indexPathsForVisibleRows.
Julian D.

16
Funktioniert nicht für die letzten Zeilen der Tabellenansicht. Die Tastatur verdeckt weiterhin alle Zeilen, die nicht über die Tastatur gescrollt werden können.
Alex Zavatone

3
Um das automatische Bildlaufverhalten für die letzten Zeilen der Tabelle zu aktivieren, erkennen Sie, wann diese Zeilen mit der Bearbeitung beginnen, und fügen Sie am Ende der Tabellenansicht eine Fußzeile mit einer leeren Ansicht einer bestimmten Höhe hinzu. Dadurch kann die Tabellenansicht die Zellen an die richtige Stelle scrollen.
Sammio2

10
Das Erreichen der Zelle über eine Kette von Aufrufen zur Überwachung ist unzuverlässig, es sei denn, Sie stellen sicher, dass Sie tatsächlich zur Zelle gelangen. Siehe stackoverflow.com/a/17757851/1371070 und stackoverflow.com/a/17758021/1371070
Cezar

70

Ich mache etwas sehr ähnliches, es ist generisch, es ist nicht nötig, etwas Spezifisches für Ihren Code zu berechnen. Überprüfen Sie einfach die Anmerkungen zum Code:

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+ Version:

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()
        }
    }
}

Es war fantastisch, die Benachrichtigungen zu verwenden und die Tastaturhöhe unter Berücksichtigung der Geräteorientierung zu ermitteln. Vielen Dank dafür! Der Scrolling-Teil funktionierte aus irgendeinem Grund nicht für mich, daher musste ich [tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
Folgendes

7
Dies ist die beste Antwort hier, denke ich. Sehr sauber. Nur zwei Dinge: 1) Ihr viewDidLoad ruft nicht [super viewDidLoad] auf und 2) ich musste in einigen Registerkarten in den Zeilen frame.size.height rechnen. Ansonsten perfekt! Vielen Dank.
Toxaq

3
Hier ist die Modifikation, die toxaq beschreibt: MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; Subtrahieren Sie dann tabBarHeight von der Tastaturhöhe, wo immer Sie die Tastaturhöhe verwenden.
Steve N

1
Wenn der Benutzer auf ein Textfeld tippt, funktioniert es perfekt. Wenn der Benutzer jedoch auf ein anderes Textfeld tippt, ohne die Eingabetaste zu drücken, wird die Tabellenansicht verkleinert.
Bhavin Ramani

1
@ BhavinRamani stimmte zu. Ich habe eine einfache boolesche Eigenschaft hinzugefügt, um zu merken, ob die Tastatur bereits angezeigt wird oder nicht, und die erneute Ausführung des Codes zu überspringen, wenn dies nicht erforderlich ist.
Dirty Henry

46

Die einfachste Lösung für Swift 3 , basierend auf der Lösung von 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)
    })
}

Ein kleines Detail ... Verwenden Notificationstatt NSNotificationwäre eher "Swift 3-y" :-)
Nicolas Miari

Dies hilft bei der Neupositionierung, wenn es eine Navigationsleiste gibt - umgeben Sie UIView.animate mit dieser, wenn let - wenn let frame = self.navigationController? .NavigationBar.frame {let y = frame.size.height + frame.origin.y}
Sean Dev

Wenn eine Rotation stattfindet, gibt es einen Fehler beim Laden und einige Zellen verschwinden, wenn die Tabellenansicht manuell gescrollt wird
jothikenpachi

Gute Lösung danke! Hinweis - Sie müssen den removeObserver nicht mehr ausführen.
Nick McConnell

44

Ich hatte das gleiche Problem, bemerkte jedoch, dass es nur in einer Ansicht angezeigt wird. Also fing ich an, nach den Unterschieden in den Controllern zu suchen.

Ich fand heraus, dass das Bildlaufverhalten in - (void)viewWillAppear:(BOOL)animatedder Superinstanz eingestellt ist.

Stellen Sie also sicher, dass Sie Folgendes implementieren:

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

Und es spielt keine Rolle, ob Sie UIViewControlleroder verwenden UITableViewController; überprüfte es, indem man eine UITableViewals Unteransicht von self.view in die UIViewController. Es war das gleiche Verhalten. In der Ansicht konnte nicht gescrollt werden, wenn der Anruf [super viewWillAppear:animated];fehlte.


1
Das hat hervorragend funktioniert. Ich habe mich gefragt, warum die Leute sagten, UITableView würde es für mich tun und das hat es gelöst. Vielen Dank!
OlivaresF

5
Ich hatte auch dieses Problem, diese Antwort sollte es bis an die Spitze schaffen!
Amiel Martin

Ich habe so viel Zeit verloren, um es selbst herauszufinden ... danke;)
Budidino

+1 fing an ein wenig zu weinen, ich hatte diese Zeile, brauchte aber auch [tableViewController viewWillAppear: animiert]; weil ich einem UIViewController einen UITableViewController hinzufüge. keine Tränen mehr :)
Colin Lamarre

41

Ich habe das vielleicht verpasst, da ich nicht den ganzen Beitrag hier gelesen habe, aber was ich mir ausgedacht habe, scheint täuschend einfach zu sein. Ich habe dies nicht durch den Wringer gestellt und in allen Situationen getestet, aber es scheint, als sollte es gut funktionieren.

Passen Sie einfach den contentInset der Tabellenansicht an die Höhe der Tastatur an und scrollen Sie die Zelle nach unten:

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

und natürlich

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

ist das zu einfach vermisse ich etwas Bisher funktioniert es gut für mich, aber wie gesagt, ich habe es nicht durch den Wringer gesteckt ...


IMO, das ist die beste Lösung. Das einzige, was ich ändern würde, ist Ihre fest codierte Dauer auf[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Andy

Es ist sehr einfach. Aber ein Problem, das ich finde, ist, dass es die Änderung nicht animiert contentInsetund die Bildlaufgrenzen scharf ändert.
Geek

Dieser hat am besten für mich funktioniert, jedoch ein paar Probleme. 1) Ich weiß nicht, wo Sie "currentField.indexPath" bekommen können, also musste ich die indexPath.row als Tag des Feldes speichern und später den indexPath erstellen. 2) Funktioniert nicht für Zeilen am oberen Rand der Tabelle, sondern rollt sie vom Bildschirm. Musste etwas Code hinzufügen, um nur zu scrollen, wenn der indexPath des currentField größer ist als das, was auf den Bildschirm passen kann. 3) musste kbSize.Width (anstelle der Höhe) auf dem iPad verwenden, wenn es Landschaft ist
Travis M.

Entschuldigung, wir gewöhnen uns so sehr an unseren eigenen Code, dass wir ihn manchmal vergessen, oder? currentField ist das aktuelle Textfeld, mit dem ich arbeite, und indexPath ist eine Erweiterung, die ich der Klasse hinzugefügt habe, die einfach einen NSIndexPath hinzufügt, damit ich weiß, in welcher Zelle sich dies befindet.
mickm

Dies ist der richtige Weg, um keine Frames zu verschieben, sondern nur die Tabelleneigenschaften zu ändern.
Nextorlg

35

Ich glaube, ich habe eine Lösung gefunden, die dem Verhalten von Apples Apps entspricht.

Zuerst in Ihrer viewWillAppear: Abonnieren Sie die Tastaturbenachrichtigungen, damit Sie wissen, wann die Tastatur ein- und ausgeblendet wird, und das System Ihnen die Größe der Tastatur anzeigt. Vergessen Sie jedoch nicht, die Registrierung in Ihrer viewWillDisappear: aufzuheben.

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

Implementieren Sie die folgenden Methoden, damit Sie die Größe Ihrer tableView so anpassen, dass sie mit dem sichtbaren Bereich übereinstimmt, sobald die Tastatur angezeigt wird. Hier verfolge ich den Status der Tastatur separat, damit ich selbst auswählen kann, wann die tableView wieder auf die volle Höhe eingestellt werden soll, da Sie diese Benachrichtigungen bei jeder Feldänderung erhalten. Vergessen Sie nicht, keyboardWillHide: zu implementieren, und wählen Sie einen geeigneten Ort, um Ihre tableView-Größe festzulegen.

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

Hier ist das Bildlaufbit. Wir arbeiten zuerst einige Größen aus, dann sehen wir, wo wir uns im sichtbaren Bereich befinden, und stellen das Rechteck, auf das wir scrollen möchten, so ein, dass es entweder die Halbansicht über oder unter der Mitte des Textfelds ist auf wo es in der Ansicht ist. In diesem Fall haben wir ein Array von UITextFields und eine Aufzählung, die diese verfolgt. Wenn Sie also rowHeight mit der Zeilennummer multiplizieren, erhalten Sie den tatsächlichen Versatz des Frames in dieser Außenansicht.

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

Das scheint ganz gut zu funktionieren.


Schöne Lösung. Danke, dass du es gepostet hast.
Alex Reynolds

2
UIKeyboardBoundsUserInfoKeyist ab iOS 3.2 veraltet. Siehe meine Lösung unten, die für alle aktuellen iOS-Versionen ≥ 3.0 funktioniert. / @ iPhoneDev
Ortwin Gentz

Das war komplizierter als es sein musste. Die Antwort von @ user91083 war einfach und funktioniert.
Richard Brightwell

1
Bei dieser Lösung gibt es ein kleines Problem. keyboardWillShow heißt AFTER textFieldDidBeginEditing. Wenn wir also zu einer Zelle scrollen möchten, hat sich der Frame von tableView noch nicht geändert, sodass er nicht funktioniert
HiveHicks

35

Wenn Sie verwenden können UITableViewController, erhalten Sie die Funktionalität kostenlos. Manchmal ist dies jedoch keine Option, insbesondere wenn Sie mehrere Ansichten benötigen, nicht nur die UITableView.

Einige der hier vorgestellten Lösungen funktionieren nicht unter iOS ≥4, andere nicht auf dem iPad oder im Querformat, andere nicht für Bluetooth-Tastaturen (bei denen kein Bildlauf gewünscht wird), andere nicht Arbeit beim Umschalten zwischen mehreren Textfeldern. Wenn Sie sich für eine Lösung entscheiden, sollten Sie diese Fälle testen. Dies ist die Lösung , die wir verwenden verwenden 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];
    }
}   

Hier ist der vollständige Code der Klasse in InAppSettingsKit. Verwenden Sie zum Testen den untergeordneten Bereich "Vollständige Liste", in dem Sie die oben genannten Szenarien testen können.


Ich weiß nicht, ob es nützlich ist, Zeichenfolgen anstelle von Konstanten zu verwenden, denn wenn Apple aus bestimmten Gründen auf die Idee kommt, die Zeichenfolge intern zu ändern, funktioniert Ihre Lösung nicht mehr. Ebenso haben Sie keine Warnung erhalten, wenn es veraltet ist. Ich denke

@iPortable: Es ist nicht ideal, ich weiß. Können Sie eine bessere Lösung vorschlagen, die auf allen Versionen ≥ 3.0 ausgeführt wird?
Ortwin Gentz

1
Funktioniert wie Charme, aber nicht für UIInterfaceOrientationPortraitUpsideDown. Dann muss die Berechnung der Höhenreduzierung ebenfalls verkehrt herum erfolgen: CGFloat reduHeight = keyboardRect.size.height - (CGRectGetMinY (viewRectAbsolute) - CGRectGetMinY (windowRect));
Klaas

Dies hat sehr auffällige visuelle Störungen auf meinem iPad und dem Simulator (4.3). Zu auffällig zu benutzen. :(
Bob Spryn

Ich finde es gut, dass diese Lösung eine Symbolleiste am unteren Bildschirmrand enthält.
pdemarest

24

Die einfachste Lösung für 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
    }

Funktioniert einwandfrei, minimale Berechnungen erforderlich. Ich habe Code hinzugefügt, der Tabelleneinfügungen wiederherstellt, um diese Antwort zu beenden.
Vitalii

Beste Lösung danke. Ich habe eine Swift 3-Version hier gepostet
squall2022

Super perfekte Lösung jemals gesehen, ich habe andere ausprobiert, habe aber einige Probleme. Ihre Lösung funktioniert perfekt auf iOS 10.2.
Wangdu Lin

8

Ich hoffe ihr habt schon eine Lösung, wenn ihr all diese liest. Aber ich fand meine Lösung wie folgt. Ich erwarte, dass Sie bereits eine Zelle mit haben UITextField. Behalten Sie also beim Vorbereiten den Zeilenindex im Tag des Textfelds.

cell.textField.tag = IndexPath.row;

Erstellen Sie eine activeTextFieldInstanz UITextFieldmit globalem Gültigkeitsbereich wie folgt:

@interface EditViewController (){

    UITextField *activeTextField;

}

Jetzt kopieren Sie einfach meinen Code und fügen ihn am Ende ein. Und vergessen Sie auch nicht hinzuzufügenUITextFieldDelegate

#pragma mark - TextField Delegation

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

    activeTextField = textField;

    return YES;
}

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

    activeTextField = nil;

}

Registriert die Tastatur 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];



}

Griffe Tastatur Notifications:

Wird aufgerufen, wenn das UIKeyboardDidShowNotificationgesendet wird.

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

}

Wird aufgerufen, wenn das UIKeyboardWillHideNotificationgesendet wird

- (void)keyboardWillBeHidden:(NSNotification*)aNotification

{

    UIEdgeInsets contentInsets = UIEdgeInsetsZero;

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

}

Jetzt bleibt noch eines übrig: Rufen Sie die registerForKeyboardNotificationsMethode in der ViewDidLoadMethode wie folgt auf:

- (void)viewDidLoad {

    [super viewDidLoad];

    // Registering keyboard notification

    [self registerForKeyboardNotifications];

    // Your codes here...

}

Sie sind fertig, hoffen, dass Ihr textFieldsWille nicht mehr von der Tastatur verdeckt wird.


6

Das Kombinieren und Ausfüllen der Lücken aus mehreren Antworten (insbesondere Ortwin Gentz, Benutzer 98013) und einem anderen Beitrag funktioniert sofort für SDK 4.3 auf einem iPad im Hoch- oder Querformat:

@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

Ich habe diesen Code in iOS 4.x verwendet, aber in iOS5 stürzt er in scrollToOldPosition ab, da _topmostRowBeforeKeyboardWasShown zu diesem Zeitpunkt bereits freigegeben ist. Ich bin mir noch nicht sicher, was die Lösung ist. Erinnern Sie sich wahrscheinlich an den Index anstelle des Objekts.
Thomas Tempelmann

5

Wenn Sie eine geeignete Ansicht verwenden, um Ihre Textfelder ( von Jeff Lamarche ) zu platzieren, können Sie die Tabellenansicht einfach mit der Delegate-Methode wie folgt scrollen.

(Hinweis: Meine Textfelder werden in einem Array mit demselben Index wie die Zeile in der Tabellenansicht gespeichert.)

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

Sie aktualisieren den tableView-Frame nicht. Dann sind die scrollBars und das Bildlaufverhalten falsch, wenn die Tastatur angezeigt wird. Siehe meine Lösung.
Ortwin Gentz

5

Tastaturbenachrichtigungen funktionieren, aber der Beispielcode von Apple setzt voraus, dass die Bildlaufansicht die Stammansicht des Fensters ist. Dies ist normalerweise nicht der Fall. Sie müssen Registerkartenleisten usw. ausgleichen, um den richtigen Versatz zu erhalten.

Es ist einfacher als es klingt. Hier ist der Code, den ich in einem UITableViewController verwende. Es hat zwei Instanzvariablen, hiddenRect und 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;
}

UIKeyboardCenterEndUserInfoKeyund UIKeyboardBoundsUserInfoKeysind ab iOS 3.2 veraltet. Siehe meine Lösung unten, die für alle aktuellen iOS-Versionen ≥ 3.0 funktioniert.
Ortwin Gentz

5

Wenn Sie verwenden Three20, verwenden Sie die autoresizesForKeyboardEigenschaft. Stellen Sie einfach die -initWithNibName:bundleMethode Ihres View Controllers ein

self.autoresizesForKeyboard = YES

Dies kümmert sich um:

  1. Abhören von Tastaturbenachrichtigungen und Anpassen des Rahmens der Tabellenansicht
  2. Zum Ersthelfer scrollen

Gemacht und gemacht.


Was ist Three20 hier? Können Sie das angeben?
Mubin Mall

5

Mein Ansatz:

Ich unterteile zuerst UITextField und füge eine indexPath-Eigenschaft hinzu. In der cellFor ... -Methode übergebe ich die indexPath-Eigenschaft.

Dann füge ich folgenden Code hinzu:

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

zum textFieldShould / WillBegin ... etc.

Wenn die Tastatur verschwindet, müssen Sie sie umkehren mit:

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

4

Eine optimierte Lösung. Es wird in die UITextField-Delegatmethoden integriert, sodass kein Durcheinander mit UIKeyboard-Benachrichtigungen erforderlich ist.

Implementierungshinweise:

kSettingsRowHeight - Die Höhe einer UITableViewCell.

offsetTarget und offsetThreshold basieren auf kSettingsRowHeight. Wenn Sie eine andere Zeilenhöhe verwenden, setzen Sie diese Werte auf die y-Eigenschaft von point. [alt: Berechne den Zeilenversatz auf andere Weise.]

- (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

Verwenden Sie die UITextField's delegateMethode:

Schnell

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
}

Ziel 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;
}

Hallo, ich habe ein Problem damit, dass dies auf Swift funktioniert. Meine UITextFields sind mit UITableViewCell verbunden. Wenn ich diesen Code in meinem UIViewController implementiere, habe ich keinen Zugriff auf UITextFields. Irgendwelche Ideen?
Vetuka

4

Swift 4.2 Komplettlösung

Ich habe GIST mit einer Reihe von Protokollen erstellt , die die Arbeit mit dem Hinzufügen von zusätzlichem Speicherplatz vereinfachen, wenn die Tastatur angezeigt, ausgeblendet oder geändert wird.

Eigenschaften :

  • Funktioniert korrekt mit Änderungen am Tastaturrahmen (z. B. Änderungen der Tastaturhöhe wie Emojii → normale Tastatur).
  • TabBar- und ToolBar-Unterstützung für das UITableView-Beispiel (bei anderen Beispielen erhalten Sie falsche Einfügungen).
  • Dynamische Animationsdauer (nicht fest codiert).
  • Protokollorientierter Ansatz, der für Sie leicht geändert werden kann.

Verwendung

Grundlegendes Verwendungsbeispiel in View Controller, das eine Bildlaufansicht enthält (Tabellenansicht wird natürlich auch unterstützt).

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 }
}

Kern: Frame wechselt Beobachter

Das Protokoll KeyboardChangeFrameObserverlöst jedes Mal ein Ereignis aus, wenn der Tastaturrahmen geändert wurde (einschließlich Anzeigen, Ausblenden, Ändern des Rahmens).

  1. Rufen Sie addKeyboardFrameChangesObserver()an viewWillAppear()oder ähnliche Methode.
  2. Rufen Sie removeKeyboardFrameChangesObserver()an viewWillDisappear()oder ähnliche Methode.

Implementierung: Bildlaufansicht

ModifableInsetsOnKeyboardFrameChangesDas Protokoll UIScrollViewunterstützt das Kernprotokoll. Es ändert die Einfügungen der Bildlaufansicht, wenn der Tastaturrahmen geändert wird.

Ihre Klasse muss die Bildlaufansicht einstellen. Die Einfügungen werden bei Änderungen des Tastaturrahmens vergrößert / verkleinert.

var scrollViewToModify: UIScrollView { get }

3

Da Sie Textfelder in einer Tabelle haben, ist es am besten, die Größe der Tabelle zu ändern. Sie müssen den tableView.frame so einstellen, dass er um die Größe der Tastatur kleiner ist (ich denke etwa 165 Pixel), und ihn dann erneut erweitern, wenn Die Tastatur wird entlassen.

Optional können Sie zu diesem Zeitpunkt auch die Benutzerinteraktion für tableView deaktivieren, wenn der Benutzer nicht scrollen soll.


Ich stimme dem zu und registriere mich für UIKeyboardWillShowNotification, um die Größe der Tastatur dynamisch zu ermitteln.
Benzado

Die vom Benachrichtigungsobjekt zurückgegebene Nummer funktioniert jedoch nicht. Oder zumindest nicht in 2.2, die zurückgegebene Zahl war falsch und ich musste den 165-Wert hart codieren, um die Höhe korrekt anzupassen (er war um fünf bis zehn Pixel versetzt)
Kendall Helmstetter Gelner

2

Dies funktioniert perfekt und auch auf dem 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];
        }

Warum suchen und verwenden Sie Sonderfälle für jedes Textfeld? Identifizieren Sie jedes Textfeld aus dem NSIndexPath der Zelle und ändern Sie diese böse if-Anweisung in zwei Codezeilen. Sie möchten wirklich einen Aufruf von cellForRowAtIndexPath und erhalten dann das textField aus der Zelle.
Alex Zavatone

Wenn man bedenkt, wie unglaublich flockig diese Situation unter iOS ist, denke ich, dass es in Ordnung ist, "vollständig abgewickelten, lächerlich wörtlichen" Code für diese Situation zu schreiben.
Fattie

In Anbetracht dieser Antwort wurde vor über 6 Jahren gegeben.
WrightsCS

2

Ich habe fast den gleichen Ansatz versucht und mir einen einfacheren und kleineren Code dafür ausgedacht. Ich habe ein IBOutlet iTextView erstellt und es mit dem UITextView im IB verknüpft.

 -(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

Nach stundenlanger anstrengender Arbeit beim Versuch, diese aktuellen Lösungen zu verwenden (und völlig gescheitert), habe ich endlich die Dinge zum Laufen gebracht und sie aktualisiert, um die neuen Animationsblöcke zu verwenden. Meine Antwort basiert vollständig auf der obigen Antwort von Ortwin .

Aus irgendeinem Grund funktionierte der obige Code für mich einfach nicht. Mein Setup schien anderen ziemlich ähnlich zu sein, aber vielleicht, weil ich auf einem iPad oder 4.3 war ... keine Ahnung. Es war ein bisschen verrückt zu rechnen und meine Tischansicht vom Bildschirm zu schießen.

Siehe Endergebnis meiner Lösung: http://screencast.com/t/hjBCuRrPC (Bitte ignorieren Sie das Foto .:-P)

Also ging ich zum Kern dessen über, was Ortwin tat, änderte jedoch die Art und Weise, wie Mathematik berechnet wurde, um den Ursprung und die Größe meiner Tabellenansicht mit der Höhe der Tastatur zu addieren. Wenn ich die Höhe des Fensters von diesem Ergebnis subtrahiere, wird mir angezeigt, wie viel Schnittpunkt ich habe. Wenn es größer als 0 ist (auch bekannt als Überlappung), führe ich die Animation der Rahmenhöhe durch.

Darüber hinaus gab es einige Probleme beim erneuten Zeichnen, die behoben wurden, indem 1) auf das Scrollen zur Zelle gewartet wurde, bis die Animation abgeschlossen war, und 2) die Option UIViewAnimationOptionBeginFromCurrentState beim Ausblenden der Tastatur verwendet wurde.

Ein paar Dinge zu beachten.

  • _topmostRowBeforeKeyboardWasShown & _originalFrame sind Instanzvariablen, die im Header deklariert sind.
  • self.guestEntryTableView ist meine tableView (ich bin in einer externen Datei)
  • IASKCGRectSwap ist Ortwins Methode zum Umdrehen der Koordinaten eines Frames
  • Ich aktualisiere die Höhe der Tabellenansicht nur, wenn mindestens 50 Pixel davon angezeigt werden
  • Da ich nicht in einem UIViewController bin, habe ich keine self.view, also kehre ich einfach die tableView in ihren ursprünglichen Frame zurück

Auch hier wäre ich dieser Antwort nicht nahe gekommen, wenn Ortwin nicht den Kern davon geliefert hätte. Hier ist der Code:

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

Es wurde meine FixOriginRotation-Funktion hinzugefügt, mit der das Koordinatensystem der Ansicht korrigiert wird, bevor Sie den Rahmen usw. aktualisieren. Ich denke, dies ist ein Teil der Gründe, warum ich zuerst Probleme hatte. Wusste nicht, dass das iOS-Fensterkoordinatensystem mit dem Gerät gedreht wurde!
Bob Spryn

2

Diese Lösung funktioniert für mich, bitte beachten Sie die Zeile

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

Sie können den Wert 160 so ändern, dass er mit Ihnen übereinstimmt

- (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

Sehr interessanter Diskussionsthread, ich stand auch vor dem gleichen Problem, das vielleicht schlimmer ist, weil

  1. Ich habe eine benutzerdefinierte Zelle verwendet und das Textfeld befand sich darin.
  2. Ich musste UIViewController verwenden, um meine Anforderungen zu erfüllen, sodass ich UITableViewController nicht nutzen kann.
  3. Ich hatte Filter- / Sortierkriterien in meiner Tabellenzelle, dh Ihre Zellen ändern sich ständig und verfolgen den Indexpfad, und alles wird nicht helfen.

So lesen Sie die Threads hier und umgesetzt meine Version, die mir geholfen , in Hochschieben meine Inhalte in iPad im Landschaftsmodus. Hier ist Code (dies ist nicht narrensicher und alles, aber es hat mein Problem behoben). Zuerst müssen Sie einen Delegaten in Ihrer benutzerdefinierten Zellenklasse haben, der zu Beginn der Bearbeitung das Textfeld an Ihren Ansichtscontroller sendet und dort das aktive Feld = theTextField setzt

// IMPLEMENTIERT NUR FÜR DEN LANDSCHAFTSMODUS

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

    }


}

// Wird aufgerufen, wenn die UIKeyboardWillHideNotification gesendet wird

- (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

Ich habe ein solches Problem gerade selbst gelöst, nachdem ich auf eine Vielzahl von Lösungen verwiesen habe, die über Google und Stack Overflow gefunden wurden.

Stellen Sie zunächst sicher, dass Sie ein IBOutlet Ihres UIScrollView eingerichtet haben. Schauen Sie sich dann Apple Doc: Keyboard Management genau an . Wenn Sie den Hintergrund scrollen können, die Tastatur jedoch weiterhin die Textfelder abdeckt, sehen Sie sich bitte diesen Code an:

// 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];

Der Hauptunterschied zwischen diesem Stück und dem von Apple liegt im if-Zustand. Ich glaube, dass Apples Berechnung der Bildlaufentfernung und der Bedingung, ob das von der Tastatur abgedeckte Textfeld nicht korrekt ist, meine Änderung wie oben vorgenommen hat.

Lass mich wissen ob es funktioniert


2

Ein Beispiel in Swift unter Verwendung des genauen Punkts des Textfelds aus Get indexPath von UITextField in UITableViewCell mit 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

Eine andere einfache Methode (funktioniert nur mit einem Abschnitt)

//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

Wenn Ihre UITableView von einer Unterklasse von UITableViewController und nicht von UITableView verwaltet wird und der Textfelddelegierte der UITableViewController ist, sollte der gesamte Bildlauf automatisch verwaltet werden - all diese anderen Kommentare sind in der Praxis sehr schwer zu implementieren.

Ein gutes Beispiel finden Sie im Apple-Beispielcodeprojekt: TaggedLocations.

Sie können sehen, dass es automatisch scrollt, aber es scheint keinen Code zu geben, der dies tut. Dieses Projekt verfügt auch über benutzerdefinierte Tabellenansichtszellen. Wenn Sie also Ihre Anwendung als Leitfaden erstellen, sollten Sie das gewünschte Ergebnis erhalten.


1

Hier ist, wie ich diese Arbeit gemacht habe, die eine Mischung aus den Antworten von Sam Ho und Marcel W und einigen meiner eigenen Fehlerkorrekturen an meinem beschissenen Code ist. Ich habe einen UITableViewController verwendet. Die Größe der Tabelle wird jetzt korrekt geändert, wenn die Tastatur angezeigt wird.

1) In viewDidLoadIch fügte hinzu:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) Ich hatte vergessen, die superÄquivalente in viewWillAppearund zu nennen awakeFromNib. Ich habe diese wieder hinzugefügt.


1

UITableViewControllerführt das Scrollen tatsächlich automatisch durch. Der Unterschied zur Verwendung von a UIViewControllerbesteht darin, dass Sie Navbar-Buttonitems programmgesteuert mithilfe von erstellen müssen NavigationController, wenn Sie a verwenden TableViewController.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.