Wie kann ich ein UITextField nach oben bewegen, wenn die Tastatur vorhanden ist - beim Starten der Bearbeitung?

1691

Mit dem iOS SDK:

Ich habe ein UIViewmit UITextFields, das eine Tastatur aufruft. Ich brauche es, um:

  1. Ermöglichen Sie das Scrollen des Inhalts von UIScrollView, um die anderen Textfelder anzuzeigen, sobald die Tastatur aufgerufen wird

  2. Automatisch "springen" (durch Scrollen nach oben) oder verkürzen

Ich weiß, dass ich eine brauche UIScrollView. Ich habe versucht, die Klasse von my UIViewin a zu ändern , kann UIScrollViewaber die Textfelder immer noch nicht nach oben oder unten scrollen.

Benötige ich sowohl ein UIViewals auch ein UIScrollView? Geht einer in den anderen?

Was muss implementiert werden, um automatisch zum aktiven Textfeld zu scrollen?

Idealerweise wird so viel wie möglich von der Einrichtung der Komponenten in Interface Builder durchgeführt. Ich möchte nur Code für das schreiben, was er benötigt.

Hinweis: Das UIView(oder UIScrollView), mit dem ich arbeite, wird durch eine Tab-Leiste ( UITabBar) angezeigt , die wie gewohnt funktionieren muss.


Bearbeiten: Ich füge die Bildlaufleiste nur für den Fall hinzu, dass die Tastatur hochgefahren wird. Auch wenn es nicht benötigt wird, bietet es meiner Meinung nach eine bessere Benutzeroberfläche, da der Benutzer dann beispielsweise Textfelder scrollen und ändern kann.

Ich habe es zum Laufen gebracht, wo ich die Bildgröße ändere, UIScrollViewwenn die Tastatur auf und ab geht. Ich benutze einfach:

-(void)textFieldDidBeginEditing:(UITextField *)textField { 
    //Keyboard becomes visible
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
                     scrollView.frame.origin.y, 
scrollView.frame.size.width,
scrollView.frame.size.height - 215 + 50);   //resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
   //keyboard will hide
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
       scrollView.frame.origin.y, 
     scrollView.frame.size.width,
      scrollView.frame.size.height + 215 - 50); //resize
}

Dies bewegt sich jedoch nicht automatisch nach oben oder zentriert die unteren Textfelder im sichtbaren Bereich, was ich wirklich gerne hätte.

philfreo
quelle
6
Überprüfen Sie dies heraus. Kein Ärger für dich. TPKeyboardAvoiding
Aruna
21
Es ist von Apple dokumentiert, ich denke, es ist der beste Weg: developer.apple.com/library/ios/#documentation/StringsTextFonts/…
Maik639
58
Verwenden Sie diesen Code. Sie benötigen nur 1 Zeile in der Datei appdelegate.m und es funktioniert. github.com/hackiftekhar/IQKeyboardManager
Pradeep Mittal
9
Der beste Weg, den ich bisher gefunden habe, ist dieses Open-Source- TPKeyboardAvoiding
Mongi Zaidi
2
Eine andere Möglichkeit besteht darin, solche Inhaltstextfelder und alle in TableViewController hinzuzufügen und Tableview dies zu überlassen.
Vicky Dhas

Antworten:

1036
  1. Sie benötigen nur eine, ScrollViewwenn der Inhalt, den Sie jetzt haben, nicht in den iPhone-Bildschirm passt. (Wenn Sie die ScrollViewals Übersicht der Komponenten hinzufügen, um beim Hochfahren der TextFieldTastatur einen Bildlauf durchzuführen , ist dies nicht erforderlich.)

  2. Die Standardmethode, um zu verhindern, dass das TextFields von der Tastatur abgedeckt wird, besteht darin, die Ansicht nach oben / unten zu verschieben, wenn die Tastatur angezeigt wird.

Hier ist ein Beispielcode:

#define kOFFSET_FOR_KEYBOARD 80.0

-(void)keyboardWillShow {
    // Animate the current view out of the way
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)keyboardWillHide {
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:mailTf])
    {
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:YES];
        }
    }
}

//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3]; // if you want to slide up the view

    CGRect rect = self.view.frame;
    if (movedUp)
    {
        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
        // 2. increase the size of the view so that the area behind the keyboard is covered up.
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
    }
    else
    {
        // revert back to the normal state.
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

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

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}
RPDP
quelle
3
Was bedeutet _textField? Ich habe es in meinen Code kopiert, es heißt, _textField ist nicht deklariert.
Cocoa Dev
Es ist das Feld, das Sie verwenden, um zu sagen "Wenn der Benutzer hier bearbeitet, sollte die Ansicht nach oben gleiten" oder so etwas ... Sie können das jedoch entfernen, wenn Sie mehr Felder haben.
Patrick
ist es nicht schlimm anzurufen - (void) setViewMovedUp: (BOOL) moveUp in keyBoardWillSHow- und KeyBoardWillHide-Ereignissen !!
Abduliam Rehmanius
4
Nicht besonders nützlich, wenn Sie Rotationen der Hauptansicht unterstützen.
FractalDoctor
2
Damit dies funktioniert, musste ich den textFieldDidBeginEditingAbschnitt auskommentieren.
Avance
445

Ich hatte auch große Probleme mit dem UIScrollViewKomponieren von mehreren UITextFields, von denen einer oder mehrere von ihnen durch die Tastatur verdeckt werden, wenn sie bearbeitet werden.

Hier sind einige Dinge zu beachten, wenn Sie UIScrollViewnicht richtig scrollen.

1) Stellen Sie sicher, dass Ihre contentSize größer als die UIScrollViewFrame-Größe ist. Der Weg zu verstehen UIScrollViewsist, dass das UIScrollViewwie ein Anzeigefenster für den in contentSize definierten Inhalt ist. Wenn also UIScrollviewirgendwo gescrollt werden soll, muss die contentSize größer sein als die UIScrollView. Andernfalls ist kein Bildlauf erforderlich, da alles, was in contentSize definiert ist, bereits sichtbar ist. Übrigens, default contentSize =CGSizeZero .

2) Nachdem Sie nun verstanden haben, dass UIScrollViewes sich tatsächlich um ein Fenster in Ihrem "Inhalt" handelt, können Sie sicherstellen, dass die Tastatur Ihr UIScrollView's"Anzeigefenster" nicht verdeckt UIScrollView, indem Sie die Größe ändern , damit Sie das UIScrollViewFenster haben, wenn die Tastatur vorhanden ist Größe nur auf die ursprüngliche UIScrollViewRahmengröße. Höhe abzüglich der Höhe der Tastatur. Dadurch wird sichergestellt, dass Ihr Fenster nur diesen kleinen sichtbaren Bereich aufweist.

3) Hier ist der Haken: Als ich dies zum ersten Mal implementierte, dachte ich, ich müsste CGRectdas bearbeitete Textfeld abrufen und die UIScrollView'sscrollRecToVisible-Methode aufrufen. Ich habe die UITextFieldDelegateMethode textFieldDidBeginEditingmit dem Aufruf der scrollRecToVisibleMethode implementiert . Dies funktionierte tatsächlich mit einem seltsamen Nebeneffekt, dass das Scrollen die Position einrasten ließUITextField . Am längsten konnte ich nicht herausfinden, was es war. Dann habe ich die textFieldDidBeginEditingDelegate-Methode auskommentiert und alles funktioniert !! (???). Wie sich herausstellte, glaube ich, dass das UIScrollViewtatsächlich implizit das aktuell bearbeitete UITextFieldimplizit in das sichtbare Fenster bringt . Meine Implementierung der UITextFieldDelegateMethode und der anschließende Aufruf von scrollRecToVisiblewar redundant und war die Ursache für die seltsame Nebenwirkung.

So sind hier die Schritte , um richtig Ihre blättern UITextFieldin einem UIScrollViewan Ort und Stelle , wenn die Tastatur erscheint.

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:self.view.window];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification 
                                               object:self.view.window];
    keyboardIsShown = NO;
    //make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
    CGSize scrollContentSize = CGSizeMake(320, 345);
    self.scrollView.contentSize = scrollContentSize;
}

- (void)keyboardWillHide:(NSNotification *)n
{
    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;


    // resize the scrollview
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];

    keyboardIsShown = NO;
}

- (void)keyboardWillShow:(NSNotification *)n
{
    // This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown.  This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`.  If we were to resize the `UIScrollView` again, it would be disastrous.  NOTE: The keyboard notification will fire even when the keyboard is already shown.
    if (keyboardIsShown) {
        return;
    }

    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;

    // resize the noteView
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];
    keyboardIsShown = YES;
}
  1. Registrieren Sie sich für die Tastaturbenachrichtigungen unter viewDidLoad
  2. Heben Sie die Registrierung für die Tastaturbenachrichtigungen bei auf viewDidUnload
  3. Stellen Sie sicher, dass das contentSizeeingestellt und größer als Ihr UIScrollViewat istviewDidLoad
  4. Verkleinern Sie das, UIScrollViewwenn die Tastatur vorhanden ist
  5. Wieder zurück das , UIScrollViewwenn die Tastatur weg geht.
  6. Verwenden Sie ein ivar, um festzustellen, ob die Tastatur bereits auf dem Bildschirm angezeigt wird, da die Tastaturbenachrichtigungen jedes Mal gesendet werden, wenn eine UITextFieldRegisterkarte angezeigt wird, auch wenn die Tastatur bereits vorhanden ist, um ein Verkleinern der Tastatur zu vermeiden, UIScrollViewwenn sie bereits verkleinert ist

Eine Sache zu beachten ist, dass das UIKeyboardWillShowNotificationwird auch dann ausgelöst, wenn die Tastatur bereits auf dem Bildschirm angezeigt wird, wenn Sie auf eine andere tippen UITextField. Ich habe mich darum gekümmert, indem ich einen Ivar verwendet habe, um die Größe nicht zu ändern, UIScrollViewwenn die Tastatur bereits auf dem Bildschirm angezeigt wird. Eine versehentliche Größenänderung, UIScrollViewwenn die Tastatur bereits vorhanden ist, wäre katastrophal!

Hoffe, dieser Code erspart einigen von Ihnen viel Kopfschmerzen.

Shiun
quelle
3
Großartig, aber zwei Probleme: 1. UIKeyboardBoundsUserInfoKeyist veraltet. 2. Tastaturgröße wird in "Bildschirmkoordinaten" angegeben, sodass Ihre viewFrame-Berechnungen fehlschlagen, wenn der Rahmen gedreht oder skaliert wird.
Martin Wickman
21
@ Martin Wickman - Verwenden Sie CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;anstelle der veraltetenUIKeyboardBoundsUserInfoKey
sottenad
1
HI, ich habe das Gleiche getan, aber die Textansicht wird nur nach oben verschoben, wenn der Benutzer mit der Eingabe beginnt. Ist es das erwartete Verhalten oder fehlt mir etwas?
3
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].sizesollte sein [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size. Tolle Lösung!
j7nn7k
1
Ich mag Ihre Lösung, aber ich denke, ich kann sie noch einfacher machen: Kümmern Sie sich nicht um das Notification Observer-Zeug; Rufen Sie stattdessen die richtigen Animationsroutinen innerhalb der entsprechenden Delegatmethoden auf - für UITextView sind dies textViewDidBeginEditing und textViewDidEndEditing.
AlexChaffee
270

Es ist eigentlich am besten, nur die Apple-Implementierung zu verwenden, wie in den Dokumenten angegeben . Der von ihnen bereitgestellte Code ist jedoch fehlerhaft. Ersetzen Sie den Teil keyboardWasShown:direkt unter den Kommentaren durch Folgendes:

NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);

CGPoint origin =  activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
    CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
    [backScrollView setContentOffset:scrollPoint animated:YES];
}

Die Probleme mit Apples Code sind folgende: (1) Sie berechnen immer, ob der Punkt innerhalb des Rahmens der Ansicht liegt, aber es ist ein ScrollView, sodass er möglicherweise bereits gescrollt hat und Sie diesen Versatz berücksichtigen müssen:

origin.y -= scrollView.contentOffset.y

(2) Sie verschieben das contentOffset um die Höhe der Tastatur, aber wir wollen das Gegenteil (wir wollen das contentOffsetum die Höhe verschieben, die auf dem Bildschirm sichtbar ist, nicht um das, was nicht ist):

activeField.frame.origin.y-(aRect.size.height)
DK_
quelle
1
In Situationen, in denen die Bildlaufansicht den Bildschirm nicht ausfüllt, sollte aRect auf den Rahmen der
Bildlaufansicht eingestellt werden
2
Soll der CGPoint-Ursprung nicht = activeField.frame.origin + activeField.frame.size.height sein, weil das gesamte Textfeld angezeigt werden soll und wenn nur einige Pixel sichtbar sind, wird der Code nicht in das Feld eingegeben Bedingung.
Htafoya
1
Diese Lösung funktioniert nicht im Querformat - das Textfeld wird oben im Ansichtsfenster angezeigt. iPad mit iOS 7.1.
Andrew
4
Für eine bessere Unterstützung von iOS 8 würde ich empfehlen, UIKeyboardFrameEndUserInfoKeyanstelle UIKeyboardFrameBeginUserInfoKeyder Tastaturgröße zu verwenden, da hierdurch Dinge wie benutzerdefinierte Tastaturänderungen und das Ein- und Ausschalten von Texterkennung erfasst werden.
Endareth
1
@Egor: Ihr Fix macht es viel besser - aber die letzte Zeile muss umgekehrt sein:self.scrollView.contentOffset = self.currentSVoffset;
Morten Holmgaard
244

In textFieldDidBeginEdittingund in textFieldDidEndEditingrufen Sie die Funktion [self animateTextField:textField up:YES]wie folgt auf:

-(void)textFieldDidBeginEditing:(UITextField *)textField 
{ 
    [self animateTextField:textField up:YES]; 
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField:textField up:NO];
}

-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
    const int movementDistance = -130; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? movementDistance : -movementDistance); 

    [UIView beginAnimations: @"animateTextField" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

Ich hoffe, dieser Code wird Ihnen helfen.

In Swift 2

func animateTextField(textField: UITextField, up: Bool) 
{
     let movementDistance:CGFloat = -130
     let movementDuration: Double = 0.3

     var movement:CGFloat = 0
     if up 
     {
         movement = movementDistance
     }
     else 
     {
         movement = -movementDistance
     }
     UIView.beginAnimations("animateTextField", context: nil)
     UIView.setAnimationBeginsFromCurrentState(true)
     UIView.setAnimationDuration(movementDuration)
     self.view.frame = CGRectOffset(self.view.frame, 0, movement)
     UIView.commitAnimations()
}


func textFieldDidBeginEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:true)
}

func textFieldDidEndEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:false)
}

SWIFT 3

 func animateTextField(textField: UITextField, up: Bool)
    {
        let movementDistance:CGFloat = -130
        let movementDuration: Double = 0.3

        var movement:CGFloat = 0
        if up
        {
            movement = movementDistance
        }
        else
        {
            movement = -movementDistance
        }
        UIView.beginAnimations("animateTextField", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration)
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }


    func textFieldDidBeginEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:true)
    }

    func textFieldDidEndEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:false)
    }
Sumanthkodi
quelle
1
warum nicht verwenden [UIView animateWithDuration: animations:^{ }];?
Andre Cytryn
2
Dies funktioniert gut, obwohl const int motionDistance = -130; // nach Bedarf anpassen muss flexibler geändert werden
Hammer
7
Unglaublich einfach bei kleinen Implementierungen. Kein Durcheinander mit ScrollViews und mehrdeutigen Problemen beim automatischen Layout.
James Perih
4
Ähm ... Sie verwenden den Parameter textField überhaupt nicht. Warum dann als Funktionsparameter? Darüber hinaus können Sie den ternären Operator auch in Swift verwenden. Macht den Code weniger gesprächig.
stk
1
Wenn die Hintergrundfarbe der Ansicht nicht schwarz ist, stellen Sie sicher, dass die Farbe des Fensters Ihrer Ansicht entspricht, damit der Benutzer nicht dahinter sieht. dh self.window.backgroundColor = [UIColor whiteColor];
bvmobileapps
134

Nur mit TextFields:

1a) Verwenden von Interface Builder: Wählen Sie Alle Textfelder => Bearbeiten => Einbetten => ScrollView

1b) Manuelles Einbetten von TextFields in UIScrollView mit dem Namen scrollView

2) Einstellen UITextFieldDelegate

3) Stellen Sie jede ein textField.delegate = self;(oder stellen Sie Verbindungen her Interface Builder)

4) Kopieren / Einfügen:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
    [scrollView setContentOffset:scrollPoint animated:YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    [scrollView setContentOffset:CGPointZero animated:YES];
}
DAS
quelle
8
Es verschiebt sich aber auch nach oben, wenn textFieldes bereits sichtbar ist.
TheTiger
1
Ändern müssen , CGPointMake(0, textField.frame.origin.y);umCGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
Fury
@Egor Auch nach deinem Kommentar funktioniert es nicht. Wie "TheTiger" erwähnt, wird die Ansicht nach oben verschoben, auch wenn das Textfeld sichtbar ist.
Rak Appdev
Änderung für XCode 10: "Alle Textfelder auswählen => Editor => Einbetten => Bildlaufansicht"
tibalt
116

Für Universal Solution war hier mein Ansatz zur Implementierung von IQKeyboardManager .

Geben Sie hier die Bildbeschreibung ein

Schritt 1: - Ich Added globale Benachrichtigungen über UITextField, UITextViewund UIKeyboardin einer Singleton - Klasse. Ich nenne es IQKeyboardManager .

Schritt 2: - Wenn Sie gefunden werden UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationoder UITextViewTextDidBeginEditingNotificationBenachrichtigungen, ich versuche zu bekommen topMostViewControllerInstanz aus der UIWindow.rootViewControllerHierarchie. Um richtig aufzudecken UITextField/ UITextViewSie darauf topMostViewController.viewbraucht ‚s Rahmen angepasst werden.

Schritt 3: - Ich habe die erwartete Bewegungsentfernung in topMostViewController.viewBezug auf die zuerst beantwortete UITextField/ berechnet UITextView.

Schritt 4: - Ich habe topMostViewController.view.framemich entsprechend der erwarteten Bewegungsentfernung nach oben / unten bewegt.

Schritt 5: - Wenn Sie gefunden werden UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationoder UITextViewTextDidEndEditingNotificationBenachrichtigung, ich noch einmal versuchen zu bekommen topMostViewControllerInstanz aus der UIWindow.rootViewControllerHierarchie.

Schritt 6: - Ich habe die gestörte Entfernung berechnet topMostViewController.view, deren ursprüngliche Position wiederhergestellt werden muss.

Schritt 7: - Ich habe topMostViewController.view.frameentsprechend der gestörten Entfernung wiederhergestellt .

Schritt 8: - Ich habe die Singleton- IQKeyboardManager- Klasseninstanz beim Laden der App instanziiert , sodass sich jedes UITextField/ UITextViewin der App automatisch an die erwartete Bewegungsentfernung anpasst.

Das ist alles IQKeyboardManager für Sie tun mit NO Codezeile wirklich !! Sie müssen nur die zugehörige Quelldatei per Drag & Drop in das Projekt ziehen. IQKeyboardManager auch unterstützen Geräteausrichtung , automatische UIToolbar - Management , KeybkeyboardDistanceFromTextField und viel mehr , als Sie denken.

Mohd Iftekhar Qurashi
quelle
Fügen Sie meinem Projekt das IQKeyBoardManagerSwift-Verzeichnis hinzu und es funktioniert nicht. Kann nicht aktiviert werden, da es in AppDelegate nicht erkannt wird ...
user3722523
2
Dies fühlt sich an wie Phishing. Die eigentliche Lösung wird nicht angezeigt. Stattdessen sehen wir einen Werbespot für diesen GitHub-Account.
Brian
101

Ich habe eine universellen zusammen, Drop-in UIScrollView, UITableViewund sogar UICollectionViewUnterklasse , die Pflege zu bewegen , alle Textfelder in ihn aus dem Weg der Tastatur erfolgt.

Wenn die Tastatur angezeigt wird, findet die Unterklasse die Unteransicht, die bearbeitet werden soll, und passt den Rahmen- und Inhaltsversatz an, um sicherzustellen, dass die Ansicht sichtbar ist. Die Animation entspricht dem Popup der Tastatur. Wenn die Tastatur verschwindet, wird die vorherige Größe wiederhergestellt.

Es sollte grundsätzlich mit jedem Setup funktionieren, entweder mit einer UITableViewSchnittstelle oder mit manuell platzierten Ansichten.

Hier ist es: Lösung zum Verschieben von Textfeldern aus dem Weg der Tastatur

Michael Tyson
quelle
Das ist es! Dies ist die beste, effizienteste und perfekteste Lösung! Außerdem werden Drehungen für Bildlaufansichten ordnungsgemäß verarbeitet. Achten Sie beim Drehen darauf, dass Sie die Größe automatisch vertikal anpassen, aber nicht unten verankern. In meinem Fall habe ich der Bildlaufansicht eine UITextView hinzugefügt. Danke Trauben!
Christopher
Sehr gute Arbeit! Sicher, ich bin faul mit Ihrer Lösung anstelle der DIY-Lösung, aber mein Chef ist glücklicher, also ja! Selbst wenn jemand es selbst tun möchte, mag ich Ihren Unterklassenansatz, anstatt jedem Controller Code hinzuzufügen. Ich war schockiert, dass iOS dies nicht standardmäßig tat wie Android
andererseits finde
Passen seltsame Dinge wie meine Bildlaufansicht alle in den Bildschirm, sodass sie nicht gescrollt werden können? Nach dem Öffnen und Schließen der Tastatur ist der Inhalt jetzt größer (es sieht so aus, als ob am unteren Rand der Seite etwas Unsichtbares hinzugefügt und nicht entfernt wurde) und kann gescrollt werden.
Almo
90

Für schnelle Programmierer:

Dies UITextFieldDelegateerledigt alles für Sie. Fügen Sie diese einfach in Ihre View Controller-Klasse ein, implementieren Sie sie in Ihren View Controller und setzen Sie den Delegaten von textField aufself

textField.delegate = self // Setting delegate of your UITextField to self

Implementieren Sie die Delegate-Rückrufmethoden:

func textFieldDidBeginEditing(textField: UITextField) {
    animateViewMoving(true, moveValue: 100)
}

func textFieldDidEndEditing(textField: UITextField) {
    animateViewMoving(false, moveValue: 100)
}

// Lifting the view up
func animateViewMoving (up:Bool, moveValue :CGFloat){
    let movementDuration:NSTimeInterval = 0.3
    let movement:CGFloat = ( up ? -moveValue : moveValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration )
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}

Für Swift 4, 4.2, 5: Ändern

self.view.frame = CGRectOffset(self.view.frame, 0,  movement)

zu

self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)

Letzter Hinweis zu dieser Implementierung: Wenn Sie einen anderen Ansichts-Controller auf den Stapel schieben, während die Tastatur angezeigt wird, wird ein Fehler erzeugt, bei dem die Ansicht wieder in den mittleren Rahmen zurückgesetzt wird, der Tastaturversatz jedoch nicht zurückgesetzt wird. Zum Beispiel ist Ihre Tastatur der erste Antwortende für nameField, aber dann drücken Sie eine Taste, die Ihren Help View Controller auf Ihren Stapel drückt. Um den Offset-Fehler zu beheben, rufen Sie nameField.resignFirstResponder () auf, bevor Sie den Ansichts-Controller verlassen, und stellen Sie sicher, dass auch die delegate-Methode textFieldDidEndEditing aufgerufen wird. Ich mache das in der viewWillDisappear-Methode.

Satnam Sync
quelle
3
SwiftLint gefiel es nicht, self.view.frame = CGRectOffset(self.view.frame, 0, movement)also änderte ich diese Zeile inself.view.frame.offsetInPlace(dx: 0, dy: movement)
Levibostian
2
Swift 4 ändere self.view.frame = CGRectOffset (self.view.frame, 0, Bewegung) in self.view.frame.offsetBy (dx: 0, dy: Bewegung)
Asinox
Zu Ihrer Information, damit dies funktioniert, müssen Sie setzen. self.view.frame = self.view.frame.offsetBy (dx: 0, dy: motion)
Josh Wolff
64

Es gibt bereits viele Antworten, aber dennoch hatte keine der oben genannten Lösungen all das ausgefallene Positionierungsmaterial, das für eine "perfekte" fehlerfreie, abwärtskompatible und flimmerfreie Animation erforderlich ist. (Fehler beim gemeinsamen Animieren von Frame / Bounds und ContentOffset, unterschiedliche Benutzeroberflächenausrichtungen, geteilte iPad-Tastatur, ...)
Lassen Sie mich meine Lösung teilen:
(vorausgesetzt, Sie haben eingerichtet UIKeyboardWill(Show|Hide)Notification)

// Called when UIKeyboardWillShowNotification is sent
- (void)keyboardWillShow:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = [notification userInfo];

    CGRect keyboardFrameInWindow;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];

    // the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
    CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil];

    CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView);
    UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);

    // this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    _scrollView.contentInset = newContentInsets;
    _scrollView.scrollIndicatorInsets = newContentInsets;

    /*
     * Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element
     * that should be visible, e.g. a purchase button below an amount text field
     * it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
     */
    if (_focusedControl) {
        CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
        controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.

        CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y;
        CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;

        // this is the visible part of the scroll view that is not hidden by the keyboard
        CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;

        if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
            // scroll up until the control is in place
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);

            // make sure we don't set an impossible offset caused by the "nice visual offset"
            // if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
            newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight);

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        } else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) {
            // if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y = controlFrameInScrollView.origin.y;

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        }
    }

    [UIView commitAnimations];
}


// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillHide:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = notification.userInfo;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    // undo all that keyboardWillShow-magic
    // the scroll view will adjust its contentOffset apropriately
    _scrollView.contentInset = UIEdgeInsetsZero;
    _scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;

    [UIView commitAnimations];
}
Martin Ullrich
quelle
Große Verbesserungen der @ Shiun-Antwort. Aber nachdem die Tastatur weg ist, kehrt die Ansicht nicht in die 1. Position zurück. Es ist immer noch eine großartige Arbeit :)
Lucien
2
Vielen Dank, dies ist die beste Lösung für mich im Jahr 2017. Beachten Sie, dass Sie das fokussierte Steuerelement nicht selbst verfolgen müssen, das können Sie mit bestimmen UIApplication.shared.sendAction(...). Hier ist die Swift 3-Version Ihrer Antwort (minus willHide-Teil) mit der sendActionimplementierten: gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72d
xaphod
@xaphod In meinem Fall musste ich mehr Steuerelemente fokussieren - z. B. eine Schaltfläche unter einem Eingabefeld. Aber ja, dieser Code ist jetzt 4 Jahre alt und kann von Verbesserungen profitieren.
Martin Ullrich
Dies ist wahrscheinlich die richtige Lösung. Die Tastaturbenachrichtigung enthält Animationsdaten. Die Delegationen der Textfelder wissen nichts über die Animationsdauer. Es wäre nur eine Vermutung.
XY
62

Shiun sagte: "Wie sich herausstellte, bringt UIScrollView das aktuell bearbeitete UITextField implizit implizit in das sichtbare Fenster." Dies scheint für iOS 3.1.3 zu gelten, nicht jedoch für 3.2, 4.0 oder 4.1. Ich musste ein explizites scrollRectToVisible hinzufügen, um das UITextField unter iOS> = 3.2 sichtbar zu machen.

Verzweigung
quelle
Es ist nicht die UIScrollView, die implizit das bearbeitete UITextField in die Ansicht scrollt, sondern das UITextField, das eine private [UITextField scrollTextFieldToVisibleIfNecessary]Methode aufruft , die wiederum aufruft, [UIScrollView scrollRectToVisible]wenn sie [UITextField becomeFirstResponder]aufgerufen wird. Siehe github.com/leopatras/ios_textfields_on_scrollview . Wenn Einschränkungen und Ansichtscontroller ordnungsgemäß eingerichtet sind, muss nicht scrollRectToVisibleexplizit aufgerufen werden (zumindest seit IOS 11).
Leo
48

Eine Sache zu überlegen ist, ob Sie jemals eine UITextFieldfür sich allein verwenden möchten . Ich habe keine gut gestalteten iPhone-Apps gefunden, die tatsächlich UITextFieldsaußerhalb von verwendet werden UITableViewCells.

Es wird einige zusätzliche Arbeit sein, aber ich empfehle Ihnen, alle Dateneingabeansichten und Tabellenansichten zu implementieren. Fügen Sie ein UITextViewzu Ihrem hinzu UITableViewCells.

Jonathan Sterling
quelle
1
Eine meiner Apps muss es Benutzern ermöglichen, Freiformnotizen hinzuzufügen. Ja, manchmal ist es nützlich, ein UITextField zu verwenden.
Peter Johnson
1
Ich stimme dieser Methode zu. Keine Arbeit oder Code auf diese Weise. Selbst wenn Sie eine Freiformnotiz benötigen, können Sie mit einer Tabellenzelle
RJH
UITableViewist leider der einzige Weg zu gehen. Tastaturbenachrichtigungen sind spröde und haben sich im Laufe der Zeit geändert. Beispielcode für Stapelüberlauf
SwiftArchitect
Diese Antwort ist etwa fünf Jahre veraltet. Die einzige moderne Lösung ist so etwas ... stackoverflow.com/a/41808338/294884
Fattie
47

Dieses Dokument enthält eine Lösung für dieses Problem. Sehen Sie sich den Quellcode unter "Verschieben von Inhalten, die sich unter der Tastatur befinden" an. Es ist ziemlich einfach.

EDIT: Es wurde festgestellt, dass das Beispiel einen kleinen Fehler enthält. Sie werden wahrscheinlich hören wollen UIKeyboardWillHideNotificationstatt UIKeyboardDidHideNotification. Andernfalls wird die Bildlaufansicht hinter der Tastatur für die Dauer der Animation zum Schließen der Tastatur abgeschnitten.

Mihai Damian
quelle
32

Einfachste Lösung gefunden

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}


- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = 80; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}
Jawad Zeb
quelle
Der Bildschirm bewegt sich nach oben, auch wenn er sich nicht unten befindet. Wenn sich das Textfeld oben befindet, wird es nicht mehr auf dem Bildschirm angezeigt. Wie kann man diesen Fall kontrollieren?
MELWIN
@MELWIN Fügen Sie einfach nach dieser Zeile hinzu: int movement = (up ? -movementDistance : movementDistance); if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 }Bitte nicht, dass die keyboardVariable der CGRect der Tastatur ist, die angezeigt wird, wenn Sie let keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
CapturedTree
31

Kleine Korrektur, die für viele UITextFields funktioniert

#pragma mark UIKeyboard handling

#define kMin 150

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
   if (currTextField) {
      [currTextField release];
   }
   currTextField = [sender retain];
   //move the main view, so that the keyboard does not hide it.
   if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
        [self setViewMovedUp:YES]; 
   }
}



//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
   [UIView beginAnimations:nil context:NULL];
   [UIView setAnimationDuration:0.3]; // if you want to slide up the view

   CGRect rect = self.view.frame;
   if (movedUp)
   {
      // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
      // 2. increase the size of the view so that the area behind the keyboard is covered up.
      rect.origin.y = kMin - currTextField.frame.origin.y ;
   }
   else
   {
      // revert back to the normal state.
      rect.origin.y = 0;
   }
   self.view.frame = rect;

   [UIView commitAnimations];
}


- (void)keyboardWillShow:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately

   if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
   {
      [self setViewMovedUp:YES];
   }
   else if (![currTextField isFirstResponder] && currTextField.frame.origin.y  + self.view.frame.origin.y < kMin)
   {
      [self setViewMovedUp:NO];
   }
}

- (void)keyboardWillHide:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately
   if (self.view.frame.origin.y < 0 ) {
      [self setViewMovedUp:NO];
   }

}


- (void)viewWillAppear:(BOOL)animated
{
   // register for keyboard notifications
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) 
                                                name:UIKeyboardWillShowNotification object:self.view.window]; 
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) 
                                                name:UIKeyboardWillHideNotification object:self.view.window]; 
}

- (void)viewWillDisappear:(BOOL)animated
{
   // unregister for keyboard notifications while not visible.
   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 
}
tt.Kilew
quelle
rect.origin.y=+currTextField.frame.origin.yfunktioniert gut danke
u.gen
30

Der RPDP-Code verschiebt das Textfeld erfolgreich aus dem Weg der Tastatur. Wenn Sie jedoch nach dem Verwenden und Schließen der Tastatur nach oben scrollen, wurde die Oberseite aus der Ansicht nach oben gescrollt. Dies gilt für den Simulator und das Gerät. Um den Inhalt oben in dieser Ansicht zu lesen, muss die Ansicht neu geladen werden.

Soll sein folgender Code nicht die Ansicht wieder beeinträchtigen?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
Steve
quelle
23

Ich bin mir nicht sicher, ob das Verschieben der Ansicht der richtige Ansatz ist. Ich habe es anders gemacht und die Größe der UIScrollView geändert. Ich habe es in einem kleinen Artikel ausführlich erklärt

Jose Muanis
quelle
Der Link zum Artikel ist tot.
Teo
22

Fügen Sie Folgendes hinzu, um zum ursprünglichen Ansichtsstatus zurückzukehren:

-(void)textFieldDidEndEditing:(UITextField *)sender

{
    //move the main view, so that the keyboard does not hide it.
    if  (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}
Nicolas Marchand
quelle
20

Versuchen Sie diesen kurzen Trick.

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}
Sourabh Sharma
quelle
19

Es gibt so viele Lösungen, aber ich habe einige Stunden damit verbracht, bevor es funktioniert. Also habe ich diesen Code hier eingefügt (einfach in das Projekt einfügen, Änderungen sind nicht erforderlich):

@interface RegistrationViewController : UIViewController <UITextFieldDelegate>{
    UITextField* activeField;
    UIScrollView *scrollView;
}
@end

- (void)viewDidLoad
{
    [super viewDidLoad];

    scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];

    //scrool view must be under main view - swap it
    UIView* natView = self.view;
    [self setView:scrollView];
    [self.view addSubview:natView];

    CGSize scrollViewContentSize = self.view.frame.size;
    [scrollView setContentSize:scrollViewContentSize];

    [self registerForKeyboardNotifications];
}

- (void)viewDidUnload {
    activeField = nil;
    scrollView = nil;
    [self unregisterForKeyboardNotifications];
    [super viewDidUnload];
}

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShown:)
                                                 name:UIKeyboardWillShowNotification object:nil];

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

}

-(void)unregisterForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillShowNotification
                                                  object:nil];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillHideNotification
                                                  object:nil];
}

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

    CGRect frame = self.view.frame;
    frame.size.height -= kbSize.height;
    CGPoint fOrigin = activeField.frame.origin;
    fOrigin.y -= scrollView.contentOffset.y;
    fOrigin.y += activeField.frame.size.height;
    if (!CGRectContainsPoint(frame, fOrigin) ) {
        CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height);
        [scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
     [scrollView setContentOffset:CGPointZero animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    activeField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeField = nil;
}

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

PS: Ich hoffe, der Code hilft jemandem, schnell den gewünschten Effekt zu erzielen. (Xcode 4.5)

HotJard
quelle
Hallo Hotjard, ich erhalte EXE_BAD_ACCESS in der [self.view addSubview: natView];
Bala
18

@ user271753

Um Ihre Ansicht wieder auf das Original zu bringen, fügen Sie Folgendes hinzu:

-(BOOL)textFieldShouldReturn:(UITextField *)textField{
   [textField resignFirstResponder];
   [self setViewMovedUp:NO];
   return YES;
}
436179
quelle
16

Es ist keine Bildlaufansicht erforderlich, um den Ansichtsrahmen verschieben zu können. Sie können den Rahmen einer viewcontroller'sAnsicht so ändern , dass die gesamte Ansicht gerade so weit nach oben verschoben wird, dass das Erstantwort-Textfeld über der Tastatur angezeigt wird. Als ich auf dieses Problem stieß, erstellte ich eine Unterklasse vonUIViewController , die dies tut. Es wird beobachtet, dass für die Tastatur eine Benachrichtigung angezeigt wird, und es wird die Unteransicht des Ersthelfer gefunden. (Falls erforderlich) wird die Hauptansicht gerade so weit nach oben animiert, dass sich der Ersthelfer über der Tastatur befindet. Wenn sich die Tastatur ausblendet, wird die Ansicht wieder dort animiert, wo sie war.

Um diese Unterklasse zu verwenden, machen Sie Ihren benutzerdefinierten Ansichts-Controller zu einer Unterklasse von GMKeyboardVC und erbt diese Funktion (stellen Sie nur sicher, dass Sie sie implementieren viewWillAppearund viewWillDisappearsie müssen super aufrufen). Die Klasse ist auf Github .

Progrmr
quelle
Welche Lizenz? Einige Ihrer Dateien dort haben eine Open Source-Lizenz, andere nicht.
Jaime
Warnung: Dieser Code ist für ARC-Projekte nicht geeignet.
Almo
Sie fügen einfach die Build-Option hinzu, um anzugeben, dass es sich um Nicht-ARC-Dateien handelt, oder Sie können sie in ARC konvertieren und eine Pull-Anforderung senden.
Programm
14

Swift 4 .

Sie können leicht auf und ab UITextFieldoder UIViewmit UIKeyBoardmit bewegenAnimation Geben Sie hier die Bildbeschreibung ein

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var textField: UITextField!
    @IBOutlet var chatView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.resignFirstResponder()
    }

    @objc func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y
        print("deltaY",deltaY)

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.chatView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
        },completion: nil)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }

}
ZAFAR007
quelle
2
Fast perfekt. Auf dem iPhone X entsteht jedoch eine merkwürdige Lücke zwischen Tastatur und Textfeld.
Houman
12

Hier ist die Hack-Lösung, die ich für ein bestimmtes Layout entwickelt habe. Diese Lösung ähnelt der Matt Gallagher-Lösung, indem ein Abschnitt in die Ansicht gescrollt wird. Ich bin noch neu in der iPhone-Entwicklung und kenne die Funktionsweise der Layouts nicht. Also dieser Hack.

Meine Implementierung musste das Scrollen beim Klicken in ein Feld und das Scrollen unterstützen, wenn der Benutzer als Nächstes auf der Tastatur auswählt.

Ich hatte ein UIView mit einer Höhe von 775. Die Steuerelemente sind grundsätzlich in Dreiergruppen über einen großen Raum verteilt. Am Ende hatte ich das folgende IB-Layout.

UIView -> UIScrollView -> [UI Components]

Hier kommt der Hack

Ich habe die UIScrollView-Höhe auf 500 Einheiten eingestellt, die größer sind als das tatsächliche Layout (1250). Ich habe dann ein Array mit den absoluten Positionen erstellt, zu denen ich scrollen muss, und eine einfache Funktion, um sie basierend auf der IB-Tag-Nummer zu erhalten.

static NSInteger stepRange[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};

NSInteger getScrollPos(NSInteger i) {
    if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
        return 0 ;
    return stepRange[i] ;
}

Jetzt müssen Sie nur noch die folgenden zwei Codezeilen in textFieldDidBeginEditing und textFieldShouldReturn verwenden (letztere, wenn Sie eine nächste Feldnavigation erstellen).

CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;

Ein Beispiel.

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
    [self.scrollView setContentOffset:point animated:YES] ;
}


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

    NSInteger nextTag = textField.tag + 1;
    UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];

    if (nextResponder) {
        [nextResponder becomeFirstResponder];
        CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ;
        [self.scrollView setContentOffset:point animated:YES] ;
    }
    else{
        [textField resignFirstResponder];
    }

    return YES ;
}

Diese Methode wird nicht wie andere Methoden zurückgescrollt. Dies war keine Voraussetzung. Wieder war dies für ein ziemlich "großes" UIView, und ich hatte keine Tage, um die internen Layout-Engines zu lernen.

Steve McFarlin
quelle
12

Gemäß der Dokumentation , wie von iOS 3.0, die die UITableViewControllerGröße der Klasse automatisch und positioniert seine Tabellenansicht , wenn in-line ist die Bearbeitung von Textfeldern. Ich denke, es reicht nicht aus, das Textfeld in ein zu setzenUITableViewCell wie einige angegeben haben.

Aus den Dokumenten :

Ein Tabellenansichts-Controller unterstützt die Inline-Bearbeitung von Tabellenansichtszeilen. Wenn beispielsweise Zeilen im Bearbeitungsmodus eingebettete Textfelder haben, wird die zu bearbeitende Zeile über die angezeigte virtuelle Tastatur gescrollt.

Dheeraj VS
quelle
Ich habe den gleichen Kommentar gefunden. Ja, das ist wahr. Das Seltsame ist, dass es in einem UITabelViewController funktioniert und in einem zweiten nicht. Aber ich konnte keine Unterschiede in meiner Implementierung feststellen.
Morpheus78
11

Hier fand ich die einfachste Lösung für die Tastatur.

Sie müssen nur den Beispielcode kopieren und einfügen und Ihr Textfeld oder eine Ansicht ändern, die Sie nach oben verschieben möchten.

Schritt 1

Kopieren Sie einfach die folgenden zwei Methoden in Ihren Controller

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];
}

- (void)deregisterFromKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Schritt 2

Registrieren und Abmelden von Tastaturbenachrichtigungen in den Methoden viewWillAppear und viewWillDisappear .

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self registerForKeyboardNotifications];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [self deregisterFromKeyboardNotifications];
    [super viewWillDisappear:animated];
}

Schritt 3

Hier kommt der Seelenteil: Ersetzen Sie einfach Ihr Textfeld und ändern Sie die Höhe, um wie viel Sie sich nach oben bewegen möchten.

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

    //you need replace your textfield instance here
    CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin;
    CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height;

    CGRect visibleRect = self.view.frame;
    visibleRect.size.height -= currentKeyboardSize.height;

    if (!CGRectContainsPoint(visibleRect, textFieldOrigin))
    {
        //you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below

        CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height  + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height
        [self.scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification *)notification
{
    [self.scrollView setContentOffset:CGPointZero animated:YES];
}

Hinweis : Nun, bitte schätzen Sie diesen Kerl Nun, , der diese schöne, saubere Lösung mit Code-Snip geteilt hat.

Hoffe das wäre sicherlich hilfreich jemand da draußen.

swiftBoy
quelle
Ich denke nicht, dass dies das Beste ist. Ithink @Dheeraj VS ist richtig: Es kann einfach und automatisch durchgeführt werden, wenn sich dieses Textfeld in der Zelle einer Tabelle befindet (auch wenn table.scrollable = NO ist). HINWEIS : Die Position und Größe des Tisches muss angemessen sein. Beispiel: - Wenn die y-Position der Tabelle von unten in der Ansicht 100 gezählt wird, überlappt die Tastatur mit 300 Höhen die gesamte Tabelle. - Wenn die Höhe der Tabelle = 10 ist und das Textfeld darin um 100 nach oben gescrollt werden muss, wenn die Tastatur angezeigt wird, um sichtbar zu sein, befindet sich dieses Textfeld außerhalb der Tabellengrenze.
Samthui7
@ samthui7 Die Antwort von Dheeraj funktioniert nur, wenn Sie einen TableViewController verwenden, nicht nur eine Tabellenansicht. Dies macht es zu einer Einschränkung, die manchmal nicht geeignet ist.
Ben G
10

Sie haben nach einem guten Tutorial für Anfänger zu diesem Thema gesucht und hier das beste Tutorial gefunden .

Stellen Sie im MIScrollView.hBeispiel unten im Tutorial ein Leerzeichen ein

@property (nonatomic, retain) id backgroundTapDelegate;

wie du siehst.

Savagenoob
quelle
Hallo savagenoob, danke für den Link und willkommen bei stackoverflow. Bitte versuchen Sie bei der Beantwortung (zukünftiger) Fragen so viele Informationen wie möglich bereitzustellen - einfache Links sind etwas spröde. Das heißt, wenn die Antwort ein Link zu einem guten Tutorial ist, der möglicherweise übersehen wird.
Maarten Bodewes
10

Wann UITextFieldsich ein UITableViewCellBildlauf befindet, sollte automatisch eingerichtet werden.

Wenn dies nicht der Fall ist, liegt dies wahrscheinlich an falschem Code / Setup der Tabellenansicht.

Zum Beispiel, als ich meinen langen Tisch UITextFieldwie folgt mit einem unten neu geladen habe :

-(void) viewWillAppear:(BOOL)animated
{
   [self.tableview reloadData];
}

Dann wurde mein Textfeld unten durch die Tastatur verdeckt, die beim Klicken in das Textfeld angezeigt wurde.

Um dies zu beheben, musste ich dies tun -

-(void) viewWillAppear:(BOOL)animated
{
    //add the following line to fix issue
    [super viewWillAppear:animated];
    [self.tableview reloadData];
}
lakersgonna3peat
quelle
Ich bin verwirrt, wofür dieser Code ist. Wenn die Tastatur angezeigt wird, viewWillAppearwird sie nicht aufgerufen. Und reloadDatamacht verdeckte Zeilen nicht sichtbar.
Adam Johns
10

Verwenden Sie diesen Drittanbieter, Sie müssen nicht einmal eine Zeile schreiben

https://github.com/hackiftekhar/IQKeyboardManager

Laden Sie das Projekt herunter und ziehen Sie es per Drag & Drop IQKeyboardManagerin Ihr Projekt. Wenn Sie ein Problem finden, lesen Sie bitte das READMEDokument.

Jungs wirklich seine Kopfschmerzen entfernen, um die Tastatur zu verwalten.

Arvind Kumar
quelle
8

Hinweis : Bei dieser Antwort wird davon ausgegangen, dass sich Ihr Textfeld in einer Bildlaufansicht befindet.

Ich bevorzuge es, mit scrollContentInset und scrollContentOffset damit umzugehen, anstatt mit den Frames meiner Ansicht herumzuspielen.

Lassen Sie uns zuerst auf die Tastaturbenachrichtigungen warten

//call this from viewWillAppear
-(void)addKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}
//call this from viewWillDisappear
-(void)removeKeyboardNotifications{
    [[NSNotificationCenter default
    Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Der nächste Schritt besteht darin, eine Eigenschaft beizubehalten, die den aktuellen Ersthelfer darstellt (UITextfield / UITextVIew, das derzeit über die Tastatur verfügt).

Wir verwenden die Delegate-Methoden, um diese Eigenschaft festzulegen. Wenn Sie eine andere Komponente verwenden, benötigen Sie etwas Ähnliches.

Beachten Sie, dass wir es für das Textfeld in didBeginEditing und für textView in shouldBeginEditing festlegen. Dies liegt daran, dass textViewDidBeginEditing aus irgendeinem Grund nach UIKeyboardWillShowNotification aufgerufen wird.

-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{
    self.currentFirstResponder = textView;
    return YES;
}

-(void)textFieldDidBeginEditing:(UITextField *)textField{
    self.currentFirstResponder = textField;
}

Schließlich ist hier die Magie

- (void)keyboardWillShow:(NSNotification*)aNotification{
    NSDictionary* info = [aNotification userInfo];
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];


    /*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/
    if(self.currentFirstResponder){

        //keyboard origin in currentFirstResponderFrame
        CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil];

        float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y);

        //only scroll the scrollview if keyboard overlays the first responder
        if(spaceBetweenFirstResponderAndKeyboard>0){
            //if i call setContentOffset:animate:YES it behaves differently, not sure why
            [UIView animateWithDuration:0.25 animations:^{
                [self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)];
            }];
        }
    }

    //set bottom inset to the keyboard height so you can still scroll the whole content

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0);
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;

}

- (void)keyboardWillHide:(NSNotification*)aNotification{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;
}
Juraj Petrik
quelle
8

Dies ist die Lösung mit Swift.

import UIKit

class ExampleViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var scrollView: UIScrollView!

    @IBOutlet var textField1: UITextField!
    @IBOutlet var textField2: UITextField!
    @IBOutlet var textField3: UITextField!
    @IBOutlet var textField4: UITextField!
    @IBOutlet var textField5: UITextField!

    var activeTextField: UITextField!

    // MARK: - View
    override func viewDidLoad() {
        super.viewDidLoad()
        self.textField1.delegate = self
        self.textField2.delegate = self
        self.textField3.delegate = self
        self.textField4.delegate = self
        self.textField5.delegate = self
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.registerForKeyboardNotifications()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        self.unregisterFromKeyboardNotifications()
    }

    // MARK: - Keyboard

    // Call this method somewhere in your view controller setup code.
    func registerForKeyboardNotifications() {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
        center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterFromKeyboardNotifications () {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil)
        center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }

    // Called when the UIKeyboardDidShowNotification is sent.
    func keyboardWasShown (notification: NSNotification) {
        let info : NSDictionary = notification.userInfo!
        let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size

        let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;

        // If active text field is hidden by keyboard, scroll it so it's visible
        // Your app might not need or want this behavior.
        var aRect = self.view.frame
        aRect.size.height -= kbSize.height;
        if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
            self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true)
        }
    }

    // Called when the UIKeyboardWillHideNotification is sent
    func keyboardWillBeHidden (notification: NSNotification) {
        let contentInsets = UIEdgeInsetsZero;
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    // MARK: -  Text Field

    func textFieldDidBeginEditing(textField: UITextField) {
        self.activeTextField = textField
    }

    func textFieldDidEndEditing(textField: UITextField) {
        self.activeTextField = nil
    }

}
Homam
quelle
Richtige Antwort, aber ich habe kein Problem, wenn ich sowohl TextField als auch TextView verwende. Irgendeine Hilfe?
Thiha Aung
@Thiha Aung, Sind Ihre IBOutlet-Variablen in Ihrem Quellcode mit dem IB verbunden?
Homam
Ja, sie sind auch verbunden. Sie haben nur diesen Fehler, wenn Sie UITextView in dieser Zeile verwenden: if (! CGRectContainsPoint (aRect, self.activeTextField.frame.origin)) {
Thiha Aung
Bedeutet self.activeTextField ist null
Thiha Aung