UITextField: przesuń widok po pojawieniu się klawiatury

Obecnie pracuję nad aplikacją na iPhone ' a z jednym widokiem, która ma wiele pól UITextFields do wprowadzania. Gdy pojawi się klawiatura, nakłada się na dolne pola tekstowe. Dodałem więc odpowiednią metodę textFieldDidBeginEditing:, aby przesunąć widok w górę, która działa świetnie:

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

Ta metoda sprawdza, czy źródłem wiadomości jest jedno z pól tekstowych, które są widoczne na klawiaturze, a jeśli nie, przesuwa widok w górę.

Dodałem również metodę textFieldDidEndEnditing:, która przesuwa widok w dół ponownie (i aktualizuje niektóre obiekty modelu zgodnie ze zmienionym wejściem):

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

Jednak To rozwiązanie ma prostą wadę: kiedy skończę edytować jedno z "ukrytych" pól tekstowych i dotknę innego pola tekstowego, klawiatura znika, widok przesuwa się w dół, widok przesuwa się ponownie w górę i pojawia się klawiatura.

Czy jest jakakolwiek możliwość, aby klawiatura nie znikała i nie pojawiała się ponownie pomiędzy dwoma edycjami ("ukrytych" pól tekstowych - tak aby Widok poruszał się tylko wtedy, gdy zaznaczone textfield zmienia się z takiego, który byłby ukryty przez klawiaturę na taki, który nie byłby Ukryty)?

Author: ComSubVie, 2009-11-21

6 answers

To rozwiązanie opiera się na ComSubVie ' S one.

Zalety:

    Dzięki temu, że urządzenie jest w pełni kompatybilne z innymi urządzeniami]}
  • Nie koduje na twardo wartości czasu trwania animacji i krzywej, odczytuje je z powiadomienia klawiatury; {21]} W przeciwieństwie do tego, że nie jest on w pełni kompatybilny z klawiaturą, nie jest on w pełni kompatybilny z klawiaturą.]}
  • nie używa przestarzałego UIKeyboardBoundsUserInfoKey;
  • obsługuje zmianę rozmiaru klawiatury z powodu naciśnięcia Międzynarodowego klucz;
  • [[20]}Naprawiono wyciek pamięci przez niezarejestrowanie zdarzeń klawiatury;
  • cały kod obsługi klawiatury jest zamknięty w osobnej klasie - KBKeyboardHandler;
  • Klasa może być łatwo rozszerzona / zmodyfikowana w celu lepszego dopasowania do konkretnych potrzeb;]}

Ograniczenia:

    [20]} Działa na iOS 4 i nowsze, wymaga niewielkich modyfikacji, aby obsługiwać starsze wersje; [21]}
  • Działa dla aplikacji z pojedynczym UIWindow. Jeśli używasz wielu UIWindows, możesz potrzebować aby zmodyfikować metodę retrieveFrameFromNotification:.

Użycie:

Include KBKeyboardHandler.h, KBKeyboardHandler.m i KBKeyboardHandlerDelegate.h w Twoim projekcie. Zaimplementuj protokół KBKeyboardHandlerDelegate w kontrolerze widoku-składa się on z jednej metody, która zostanie wywołana po wyświetleniu, ukryciu klawiatury lub zmianie jej rozmiaru. Tworzy instancję KBKeyboardHandler i ustawia jej delegata (zazwyczaj self). Zobacz przykład MyViewController poniżej.

KBKeyboardHandler.h :

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@protocol KBKeyboardHandlerDelegate;

@interface KBKeyboardHandler : NSObject

- (id)init;

// Put 'weak' instead of 'assign' if you use ARC
@property(nonatomic, assign) id<KBKeyboardHandlerDelegate> delegate; 
@property(nonatomic) CGRect frame;

@end

KBKeyboardHandler.m :

#import "KBKeyboardHandler.h"
#import "KBKeyboardHandlerDelegate.h"

@implementation KBKeyboardHandler

- (id)init
{
    self = [super init];
    if (self)
    {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillShow:)
                                                     name:UIKeyboardWillShowNotification
                                                   object:nil];

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

    return self;
}

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

@synthesize delegate;
@synthesize frame;

- (void)keyboardWillShow:(NSNotification *)notification
{
    CGRect oldFrame = self.frame;    
    [self retrieveFrameFromNotification:notification];

    if (oldFrame.size.height != self.frame.size.height)
    {
        CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width,
                                  self.frame.size.height - oldFrame.size.height);
        if (self.delegate)
            [self notifySizeChanged:delta notification:notification];
    }
}

- (void)keyboardWillHide:(NSNotification *)notification
{
    if (self.frame.size.height > 0.0)
    {
        [self retrieveFrameFromNotification:notification];
        CGSize delta = CGSizeMake(-self.frame.size.width, -self.frame.size.height);

        if (self.delegate)
            [self notifySizeChanged:delta notification:notification];
    }

    self.frame = CGRectZero;
}

- (void)retrieveFrameFromNotification:(NSNotification *)notification
{
    CGRect keyboardRect;
    [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
    self.frame = [[UIApplication sharedApplication].keyWindow.rootViewController.view convertRect:keyboardRect fromView:nil];
}

- (void)notifySizeChanged:(CGSize)delta notification:(NSNotification *)notification
{
    NSDictionary *info = [notification userInfo];

    UIViewAnimationOptions curve;
    [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve];

    NSTimeInterval duration;
    [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration];

    void (^action)(void) = ^{
        [self.delegate keyboardSizeChanged:delta];
    };

    [UIView animateWithDuration:duration
                          delay:0.0
                        options:curve
                     animations:action
                     completion:nil];    
}

@end

KBKeyboardHandlerDelegate.h :

@protocol KBKeyboardHandlerDelegate

- (void)keyboardSizeChanged:(CGSize)delta;

@end

Sample MyViewController.h :

@interface MyViewController : UIViewController<KBKeyboardHandlerDelegate>
...
@end

Sample MyViewController.m :

@implementation MyViewController
{
    KBKeyboardHandler *keyboard;
}

- (void)dealloc
{
    keyboard.delegate = nil;
    [keyboard release];
    [super dealloc];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    keyboard = [[KBKeyboardHandler alloc] init];
    keyboard.delegate = self;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    keyboard.delegate = nil;
    [keyboard release];
    keyboard = nil;
}

- (void)keyboardSizeChanged:(CGSize)delta
{
    // Resize / reposition your views here. All actions performed here 
    // will appear animated.
    // delta is the difference between the previous size of the keyboard 
    // and the new one.
    // For instance when the keyboard is shown, 
    // delta may has width=768, height=264,
    // when the keyboard is hidden: width=-768, height=-264.
    // Use keyboard.frame.size to get the real keyboard size.

    // Sample:
    CGRect frame = self.view.frame;
    frame.size.height -= delta.height;
    self.view.frame = frame;
}

Aktualizacja: Naprawiono Ostrzeżenie iOS 7, dzięki @ weienv.

 47
Author: Vladimir Grigorov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-25 20:39:02

Właśnie rozwiązałem ten problem. Rozwiązanie jest kombinacją obserwatora UIKeyboardDidShowNotification i UIKeyboardDidHideNotification z powyższymi metodami textFieldDidBeginEditing: i textFieldDidEndEditing:.

Potrzebne są trzy dodatkowe zmienne, jedna do przechowywania bieżącego wybranego pola UITextField( które nazwałem activeField), jedna do wskazania, czy bieżący widok został przeniesiony, a druga do wskazania, czy klawiatura jest wyświetlana.

Tak wyglądają teraz dwie metody delegowania UITextField:

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

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

Po załadowaniu widoku, następujące dwa tworzy się obserwatorów:

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

I odpowiednie metody są zaimplementowane w następujący sposób:

- (void)keyboardWasShown:(NSNotification *)aNotification {
    if ( keyboardShown )
        return;

    if ( ( activeField != inputAmount ) && ( activeField != inputAge ) ) {
        NSDictionary *info = [aNotification userInfo];
        NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
        CGSize keyboardSize = [aValue CGRectValue].size;

        NSTimeInterval animationDuration = 0.300000011920929;
        CGRect frame = self.view.frame;
        frame.origin.y -= keyboardSize.height-44;
        frame.size.height += keyboardSize.height-44;
        [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
        [UIView setAnimationDuration:animationDuration];
        self.view.frame = frame;
        [UIView commitAnimations];

        viewMoved = YES;
    }

    keyboardShown = YES;
}

- (void)keyboardWasHidden:(NSNotification *)aNotification {
    if ( viewMoved ) {
        NSDictionary *info = [aNotification userInfo];
        NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
        CGSize keyboardSize = [aValue CGRectValue].size;

        NSTimeInterval animationDuration = 0.300000011920929;
        CGRect frame = self.view.frame;
        frame.origin.y += keyboardSize.height-44;
        frame.size.height -= keyboardSize.height-44;
        [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
        [UIView setAnimationDuration:animationDuration];
        self.view.frame = frame;
        [UIView commitAnimations];

        viewMoved = NO;
    }

    keyboardShown = NO;
}

Ten kod działa teraz zgodnie z oczekiwaniami. Klawiatura jest wyłączana tylko po naciśnięciu przycisku Gotowe, w przeciwnym razie pozostaje widoczna i widok nie jest przesuwany.

Jako dodatkowa uwaga, myślę, że można uzyskać animationDuration dynamicznie pytając obiekt NSNotification, ponieważ grałem już z podobnym rozwiązaniem, ale nie dostałem go do pracy (co robi teraz).

 30
Author: ComSubVie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-21 16:22:20

Ten kontroler widoku musi być delegatem UITextView i musisz ustawić self.textview.delegate = self w viewdidload

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

-(void) textViewDidEndEditing:(UITextView *)textView
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.25f];
    CGRect frame = self.view.frame;
    frame.origin.y = frame.origin.y + 204;
    [self.view setFrame:frame];
    [UIView commitAnimations];
}
 2
Author: coolcool1994,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-24 18:44:22

I got your Problem just do simple thing wystarczy dać outlet do UIScrollview. Ustaw unikalną właściwość znacznika dla każdego pola tekstowego w widoku.

-(void)textFieldDidBeginEditing:(UITextField *)textField
    {   
        switch (textField.tag)
        {
            case 2:    //can be your textfiled tag
              { CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-150); 
                                           //set figure y-150 as per your comfirt
                  [scrollview setContentOffset:scrollPoint animated:YES]; 
             }break;

              case 3:   
              { CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-180); 
                                           //set figure y-180 as per your comfirt
                  [scrollview setContentOffset:scrollPoint animated:YES]; 
             }break;

             ...

         }
    }

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

        if(textField.tag==3){
            [scrollview setContentOffset:CGPointZero animated:YES];
                }
         //set the last textfield when you want to disappear keyboard.
    }
 0
Author: utkal patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-04 05:45:34
Write below code in your view controller. tbl is your table view. 

-(void)viewWillAppear:(BOOL)animated{


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

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

-(void) viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}



#pragma mark - Keyboard Methods
-(void)keyboardWillShow:(NSNotification *)notification
{
//    375 × 667  ( 750 × 1334 ) iPhone 6
    //414 × 736
    CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height;
    [UIView animateWithDuration:0.5 animations:^{
        tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right);
    }];
}

-(void)keyboardWillChangeFrame:(NSNotification *)notification
{
    CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
//    int H = IS_IPHONE_5?504-keyboardRect.size.height:416-keyboardRect.size.height;
     int H = [[UIScreen mainScreen] bounds].size.height - 64- 20  -keyboardRect.size.height;
    [UIView animateWithDuration:0.5 animations:^{
        //        scroll.frame = rect;
        tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right);
    }];
}


-(void)keyboardWillHide:(NSNotification *)notification
{

    [UIView animateWithDuration:0.3 animations:^{
        //        scroll.frame = rect;
        tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, 0, tbl.contentInset.right);
    }];
}
 0
Author: Vibhooti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-08 05:43:04

Dość łatwe rozwiązanie, działa ze wszystkimi rozmiarami ekranu

Najpierw musisz osadzić UITextFields w UIScrollView. W moim przypadku miałem kilka UITextFields i UITextView.

Tutaj wpisz opis obrazka

Następnie musisz dziedziczyć z UITextFieldDelegate, Uitextviewdelegate.

class SettingsVC: UIViewController, UITextFieldDelegate, UITextViewDelegate

Przypisanie delegatów textfield i textview do self.

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

Następnie użyj tego kodu:

var editingTextInput: UIView!


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

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(self.keyboardShown(notification:)),
                                           name: NSNotification.Name.UIKeyboardDidShow,
                                           object: nil)
    

  }
  
    override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)
  }
  
  func keyboardShown(notification: NSNotification) {
    
    if let infoKey  = notification.userInfo?[UIKeyboardFrameEndUserInfoKey],
      let rawFrame = (infoKey as AnyObject).cgRectValue {
      
      let keyboardFrame = view.convert(rawFrame, to: view)
      let editingTextInputFrame = self.editingTextInput.convert(self.editingTextInput.frame, to: view)

      
      if editingTextInputFrame.maxY > keyboardFrame.minY{
        
        let diff = keyboardFrame.minY - editingTextInputFrame.maxY
        containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true)
        
      }
    }
  }
  
  func textFieldDidBeginEditing(_ textField: UITextField) {
    self.editingTextInput = textField
  }
  
  func textViewDidBeginEditing(_ textView: UITextView) {
    self.editingTextInput = textView
  }
  
  func textFieldDidEndEditing(_ textField: UITextField) {
    containerScrollView.setContentOffset(CGPoint.zero, animated: true)
  }
  
  func textViewDidEndEditing(_ textView: UITextView) {
    containerScrollView.setContentOffset(CGPoint.zero, animated: true)
  }

Krótko mówiąc, subskrybujesz Powiadomienie UIKeyboardDidShow. Po dotknięciu textField lub textView zostanie wyświetlona klawiatura i chwycisz ramkę klawiatury i ramkę elementu wejściowego, na którym stuknąłeś. Konwertuj je na układ współrzędnych viewcontrollera i porównaj najniższy punkt elementu wejściowego z najwyższym punktem klawiatury. Jeśli dolna część elementu jest niższa od najwyższej klawiatury, Ustaw przesunięcie containerScrollView na różnicę między nimi.

if editingTextInputFrame.maxY > keyboardFrame.minY{
            
            let diff = keyboardFrame.minY - editingTextInputFrame.maxY
            containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true)
            
          }

res1 res2

 0
Author: Alexey Savchenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-05 10:03:26