Как я могу заставить UITextField двигаться вверх при наличии клавиатуры - при начале редактирования?

1692

С iOS SDK:

У меня есть UIViewс, UITextFieldкоторые поднимают клавиатуру. Мне нужно, чтобы я мог:

  1. Разрешить прокрутку содержимого, UIScrollViewчтобы увидеть другие текстовые поля, как только клавиатура

  2. Автоматически «прыгать» (путем прокрутки вверх) или сокращения

Я знаю, что мне нужно UIScrollView. Я пытался изменить класс своего класса UIViewна a, UIScrollViewно все еще не могу прокрутить текстовые поля вверх или вниз.

Нужно ли мне и а UIViewи а UIScrollView? Один входит в другой?

Что необходимо реализовать, чтобы автоматически прокручивать активное текстовое поле?

В идеале, максимально возможная настройка компонентов будет выполнена в Интерфейсном Разработчике. Я хотел бы написать код только для того, что нужно.

Примечание: UIView(или UIScrollView), с которым я работаю, вызывается вкладкой ( UITabBar), которая должна функционировать как обычно.


Изменить: я добавляю полосу прокрутки только для того, когда клавиатура появляется. Хотя это и не нужно, я чувствую, что он обеспечивает лучший интерфейс, потому что тогда пользователь может, например, прокручивать и изменять текстовые поля.

У меня это работает, когда я меняю размер кадра, UIScrollViewкогда клавиатура идет вверх и вниз. Я просто использую:

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

Тем не менее, это автоматически не «двигает вверх» и не центрирует нижние текстовые поля в видимой области, что мне бы очень хотелось.

philfreo
источник
6
Проверь это. Нет проблем для вас. TPKeyboardAvoiding
Аруна
21
Это задокументировано Apple, я думаю, что это лучший способ: developer.apple.com/library/ios/#documentation/StringsTextFonts/…
Maik639,
58
Используйте этот код. Вам просто нужна 1 строка в файле appdelegate.m, и она работает. github.com/hackiftekhar/IQKeyboardManager
Прадип Миттал
9
Лучший способ, который я нашел до сих пор, - это открытый исходный код TPKeyboardAvoiding
Зайди,
2
Другой способ - добавить текстовые поля с таким содержимым и все в TableViewController и позволить tableview справиться с этим.
Вики Дхас

Ответы:

1036
  1. Вам понадобится только ScrollViewесли содержимое, которое у вас сейчас есть, не помещается на экране iPhone. (Если вы добавляете ScrollViewкак суперпредставление компонентов просто для TextFieldпрокрутки вверх, когда клавиатура появляется, то это не нужно.)

  2. Стандартный способ предотвратить TextFieldналожение клавиатуры на клавиатуру - это перемещать изображение вверх / вниз, когда отображается клавиатура.

Вот пример кода:

#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
источник
3
Что значит _textField? Я скопировал его в свой код, он говорит, что _textField не объявлен.
Какао Dev
Это поле, которое вы используете, чтобы сказать «когда пользователь редактирует здесь, представление должно сдвинуться вверх» или что-то в этом роде ... Однако вы можете удалить это, если, если вам нужно больше полей.
Патрик
Разве это не бэттер для вызова - (void) setViewMovedUp: (BOOL) moveUp в событиях keyBoardWillSHow и KeyBoardWillHide !!
Абдулиам Рехманиус
4
Не особенно полезно, если вы поддерживаете повороты основного вида.
FractalDoctor
2
Чтобы сделать эту работу, мне пришлось закомментировать textFieldDidBeginEditingраздел.
Аванс
445

У меня также было много проблем с UIScrollViewсозданием нескольких UITextFields, из которых один или несколько из них были бы скрыты клавиатурой при редактировании.

Вот некоторые вещи, которые следует учитывать, если вы UIScrollViewне прокручиваете их должным образом.

1) Убедитесь, что ваш contentSize больше UIScrollViewразмера кадра. Способ понять UIScrollViews, что UIScrollViewэто похоже на окно просмотра контента, определенного в contentSize. Поэтому, когда для UIScrollviewпрокрутки в любом месте, contentSize должен быть больше, чем UIScrollView. Иначе, прокрутка не требуется, так как все, что определено в contentSize, уже видно. Кстати, по умолчанию contentSize = CGSizeZero.

2) Теперь, когда вы понимаете, что на UIScrollViewсамом деле окно в ваш «контент», способ гарантировать, что клавиатура не заслоняет ваше UIScrollView's«окно» просмотра, состоит в изменении размера UIScrollViewтак, чтобы при наличии клавиатуры у вас было UIScrollViewокно по размеру соответствует только оригинальному UIScrollViewframe.size.height минус высота клавиатуры. Это гарантирует, что ваше окно будет только этой маленькой видимой областью.

3) Вот подвох: когда я впервые реализовал это, я подумал, что мне нужно получить CGRectотредактированное текстовое поле и вызвать UIScrollView'sметод scrollRecToVisible. Я реализовал UITextFieldDelegateметод textFieldDidBeginEditingс помощью вызова scrollRecToVisibleметода. Это на самом деле работали со странным побочным эффектом , что скроллинг будет оснасткойUITextField в положение. Долгое время я не мог понять, что это было. Затем я прокомментировал textFieldDidBeginEditingметод Delegate, и все это работает !! (???). Как выяснилось, я считаю, что UIScrollViewфактически неявно переносит редактируемое UITextFieldв настоящее время в видимое окно неявно. Моя реализация UITextFieldDelegateметода и последующий вызов к нему scrollRecToVisibleбыли излишни и стали причиной странного побочного эффекта.

Так вот шаги , чтобы правильно прокручивает UITextFieldв UIScrollViewна место , когда появляется значок клавиатуры.

// 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. Зарегистрируйтесь для получения уведомлений клавиатуры viewDidLoad
  2. Отменить регистрацию для клавиатурных сообщений на viewDidUnload
  3. Убедитесь, что contentSizeустановлено и больше, чем у вас UIScrollViewвviewDidLoad
  4. ТермоусадочныеUIScrollView , когда клавиатура присутствует
  5. Откат назадUIScrollView когда клавиатура уходит.
  6. Используйте Ивар , чтобы обнаружить , если клавиатура уже отображается на экране , так как уведомления клавиатуры посылаются каждый раз , когда UITextFieldэто закладками , даже если клавиатура уже присутствует , чтобы избежать усадкиUIScrollView , когда он уже усадке

Стоит отметить, что при UIKeyboardWillShowNotificationсрабатывании другой клавиши она будет срабатывать даже тогда, когда клавиатура уже находится на экране UITextField. Я позаботился об этом, используя ivar, чтобы избежать изменения размера, UIScrollViewкогда клавиатура уже находится на экране. Случайное изменение размера, UIScrollViewкогда клавиатура уже есть, будет катастрофическим!

Надеюсь, этот код избавит некоторых из вас от головной боли.

Shiun
источник
3
Отлично, но две проблемы: 1. UIKeyboardBoundsUserInfoKeyустарела. 2. keyboardSize находится в «координатах экрана», поэтому ваши вычисления в viewFrame завершатся неудачно, если кадр будет повернут или масштабирован.
Мартин Уикман,
21
@Martin Wickman - Использование CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;вместо устаревшегоUIKeyboardBoundsUserInfoKey
sottenad
1
Привет, я сделал то же самое, но текстовое представление перемещается только вверх, когда пользователь начинает печатать? Это ожидаемое поведение или я что-то упустил?
3
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].sizeдолжно быть [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size. Отличное решение, хотя!
j7nn7k
1
Мне нравится ваше решение, но я думаю, что смогу сделать его еще проще: не беспокойтесь о компоненте Notification Observer; вместо этого вызовите правильные процедуры анимации внутри соответствующих методов делегата - для UITextView они textViewDidBeginEditing и textViewDidEndEditing.
AlexChaffee
270

На самом деле лучше всего использовать реализацию Apple, как указано в документации . Однако код, который они предоставляют, неисправен. Замените часть, найденную keyboardWasShown:чуть ниже комментариев, на следующую:

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

Проблемы с кодом Apple заключаются в следующем: (1) Они всегда вычисляют, находится ли точка в рамке представления, но это так ScrollView, поэтому она, возможно, уже прокрутилась, и вам необходимо учитывать это смещение:

origin.y -= scrollView.contentOffset.y

(2) Они смещают contentOffset на высоту клавиатуры, но мы хотим наоборот (мы хотим сместить contentOffsetвысоту, которая видна на экране, а не то, что нет):

activeField.frame.origin.y-(aRect.size.height)
DK_
источник
1
В ситуациях, когда представление прокрутки не заполняет экран, aRect должен быть установлен в рамку представления прокрутки
mblackwell8
2
Разве вы не хотите, чтобы CGPoint origin = activeField.frame.origin + activeField.frame.size.height? Потому что вы хотите, чтобы отображалось все текстовое поле, и если в нем всего несколько пикселей, то код не будет введен состояние.
Хтафоя
1
Это решение не работает в альбомной ориентации - текстовое поле вылетает из верхней части окна просмотра. iPad с iOS 7.1.
Андрей
4
Для лучшей поддержки iOS 8, я бы предложил использовать UIKeyboardFrameEndUserInfoKeyвместо UIKeyboardFrameBeginUserInfoKeyполучения размера клавиатуры, так как это подберет такие вещи, как пользовательские изменения клавиатуры и включение / выключение интеллектуального текста.
Endareth
1
@Egor: Ваше исправление делает его намного лучше - но последняя строка должна быть обратной:self.scrollView.contentOffset = self.currentSVoffset;
Мортен Холмгаард
244

В textFieldDidBeginEdittingи в textFieldDidEndEditingвызове функции [self animateTextField:textField up:YES]так:

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

Я надеюсь, что этот код поможет вам.

В Свифт 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)
    }
сумантккоди
источник
1
почему бы не использовать [UIView animateWithDuration: animations:^{ }];?
Андре Cytryn
2
это работает хорошо, хотя const int motionDistance = -130; // настройка по мере необходимости должна быть изменена на более гибкую
Hammer
7
Невероятно просто на небольших реализациях. Нет проблем с ScrollViews и неоднозначными проблемами автоматической разметки.
Джеймс Пери
4
Хм ... вы вообще не используете параметр textField. Зачем тогда это в качестве параметра функции? Кроме того, вы можете использовать троичный оператор также в Swift. Делает код менее разговорчивым.
СТК
1
Если цвет фона представления отличается от черного, убедитесь, что вы установили цвет окна так, чтобы он соответствовал вашему виду, чтобы пользователь не видел за ним. т.е. self.window.backgroundColor = [UIColor whiteColor];
bvmobileapps
134

Просто используя TextFields:

1a) Использование Interface Builder: Выбрать все TextFields => Edit => Embed In => ScrollView

1b) Внедрение TextFields в UIScrollView, называемое scrollView.

2) Установить UITextFieldDelegate

3) Установите каждый textField.delegate = self;(или установите соединения в Interface Builder)

4) Копировать / Вставить:

- (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
источник
8
Но он также перемещается вверх по виду, когда textFieldон уже виден.
TheTiger
1
Нужно поменять CGPointMake(0, textField.frame.origin.y);наCGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
Fury
@Egor Даже после вашего комментария это не работает. Как упоминалось в «TheTiger», он перемещается вверх по виду даже после того, как текстовое поле становится видимым.
rak appdev
Изменение для XCode 10: «Выбрать все
текстовые
116

Для универсального решения , вот мой подход к реализации IQKeyboardManager .

введите описание изображения здесь

Шаг1: - Я Добавленные глобальные уведомления о UITextField, UITextViewи UIKeyboardв классе одноплодного. Я называю это IQKeyboardManager .

Шаг2: - Если будет установлено UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationили UITextViewTextDidBeginEditingNotificationуведомления, я пытаюсь получить topMostViewControllerэкземпляр из UIWindow.rootViewControllerиерархии. Для того, чтобы правильно раскрыть UITextField/ UITextViewна нем, topMostViewController.viewнеобходимо скорректировать кадр.

Шаг 3: - Я рассчитал ожидаемое расстояние перемещения по topMostViewController.viewотношению к первому ответившему UITextField/ UITextView.

Шаг 4: - Я двигался topMostViewController.view.frameвверх / вниз в соответствии с ожидаемым расстоянием перемещения.

Шаг 5: - Если будет установлено UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationили UITextViewTextDidEndEditingNotificationуведомление, я снова попытаться получить topMostViewControllerэкземпляр из UIWindow.rootViewControllerиерархии.

Шаг 6: - Я рассчитал нарушенное расстояние, topMostViewController.viewкоторое необходимо восстановить в исходное положение.

Шаг 7: - Я восстановил topMostViewController.view.frameпо нарушенной дистанции.

Шаг 8: - Я создал экземпляр одноэлементного экземпляра класса IQKeyboardManager при загрузке приложения, поэтому каждое UITextField/ UITextViewв приложении будет автоматически корректироваться в соответствии с ожидаемым расстоянием перемещения.

Это все , что IQKeyboardManager сделать для вас НЕТ строка кода на самом деле !! нужно всего лишь перетащить связанный исходный файл в проект. IQKeyboardManager также поддерживает ориентацию устройства , автоматическое управление UIToolbar , KeybkeyboardDistanceFromTextField и многое другое, чем вы думаете.

Мохд Ифтехар Кураши
источник
Добавить каталог IQKeyBoardManagerSwift в мой проект и не работать. Не могу включить, потому что его не распознают в AppDelegate ...
user3722523
2
это похоже на фишинг, фактическое решение не показано, но вместо этого мы видим рекламу этого аккаунта на GitHub.
Брайан
101

Я соединил универсальное, дроп-ин UIScrollView, UITableViewи даже UICollectionViewподкласс , который заботится о перемещении всех текстовых полей внутри него в сторону клавиатуры.

Когда клавиатура должна появиться, подкласс найдет подпредставление, которое должно быть отредактировано, и отрегулирует его фрейм и смещение содержимого, чтобы убедиться, что представление является видимым, с анимацией, соответствующей всплывающему окну клавиатуры. Когда клавиатура исчезает, она восстанавливает свой прежний размер.

Он должен работать практически с любой настройкой, либо с UITableViewинтерфейсом на основе , либо с интерфейсом, состоящим из представлений, размещенных вручную.

Вот это: решение для перемещения текстовых полей с клавиатуры

Michael Tyson
источник
Это оно! Это лучшее, самое эффективное и идеальное решение! Это также обрабатывает вращения правильно для представлений прокрутки. При вращении убедитесь, что вы автоматически меняете размеры по вертикали, но не ставьте якорь внизу. Я добавил UITextView к представлению прокрутки в моем случае. Спасибо, связки!
Кристофер
Очень хорошая работа! Конечно, мне лень использовать ваше решение вместо DIY, но мой босс счастливее, так что да! Даже если кто-то хочет сделать это сам, мне нравится ваш подход к подклассам вместо добавления кода для каждого контроллера. Я был в шоке, iOS не делает этого по умолчанию, как это делал Android - опять же, я нахожу много недостатков в iOS и MacOS :(
eselk
Все ли странные вещи вроде моего прокрутки помещаются на экране, поэтому их нельзя прокручивать. После открытия и закрытия клавиатуры содержимое теперь стало больше (похоже, что-то невидимое было добавлено, а не удалено внизу страницы), и его можно прокручивать.
Almo
91

Для программистов Swift :

Это все сделает за вас, просто поместите их в ваш класс контроллера представления и реализуйте в UITextFieldDelegateвашем контроллере представления и установите делегата textField вself

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

Реализуйте методы обратного вызова делегата:

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

Для Swift 4, 4.2, 5: изменить

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

в

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

Последнее замечание об этой реализации: если вы поместите другой контроллер представления в стек, пока отображается клавиатура, это создаст ошибку, когда представление возвращается к его центральной рамке, но смещение клавиатуры не сбрасывается. Например, ваша клавиатура является первым респондентом для nameField, но затем вы нажимаете кнопку, которая помещает ваш Help View Controller в ваш стек. Чтобы исправить ошибку смещения, обязательно вызовите nameField.resignFirstResponder () перед выходом из контроллера представления, убедившись, что метод делегата textFieldDidEndEditing также вызывается. Я делаю это в методе viewWillDisappear.

Satnam Sync
источник
3
SwiftLint не понравилось, self.view.frame = CGRectOffset(self.view.frame, 0, movement)поэтому я изменил эту строку наself.view.frame.offsetInPlace(dx: 0, dy: movement)
levibostian
2
Swift 4 изменить self.view.frame = CGRectOffset (self.view.frame, 0, движение) на self.view.frame.offsetBy (dx: 0, dy: движение)
Asinox
К вашему сведению, чтобы это работало, вы должны поставить. self.view.frame = self.view.frame.offsetBy (dx: 0, dy: движение)
Джош Вольф
64

Уже есть много ответов, но все же ни одно из вышеприведенных решений не имело всех причудливых средств позиционирования, необходимых для «идеальной» безошибочной, обратно совместимой и не мерцающей анимации. (ошибка при анимации frame / bounds и contentOffset вместе, различные ориентации интерфейса, разделенная клавиатура iPad, ...)
Позвольте мне поделиться своим решением:
(при условии, что вы настроили 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];
}
Мартин Ульрих
источник
Большие улучшения ответа @Shiun. Но после того, как клавиатура пропала, вид не возвращается в 1-е положение. Это все еще отличная работа :)
Люсьен
2
Спасибо, это лучшее решение для меня в 2017 году. Заметьте, что вам не нужно отслеживать focusControl самостоятельно, вы можете определить это с помощью UIApplication.shared.sendAction(...). Вот версия вашего ответа Swift 3 (за исключением части willHide), с sendActionвнедренным: gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72d
xaphod
В моем случае @xaphod мне нужно было сфокусировать больше элементов управления - например, кнопку под полем ввода. но да, этому коду уже 4 года, и он может выиграть от улучшений.
Мартин Ульрих
Это, вероятно, правильное решение. Уведомление клавиатуры переносит данные анимации, делегаты текстового поля не знают о продолжительности анимации, это было бы просто предположением.
XY
62

Шиун сказал: «Как оказалось, я полагаю, что UIScrollView фактически неявно переносит редактируемый в настоящий момент UITextField в видимое окно». Это, похоже, верно для iOS 3.1.3, но не для 3.2, 4.0 или 4.1. Мне пришлось добавить явный scrollRectToVisible, чтобы сделать UITextField видимым на iOS> = 3.2.

cbranch
источник
Это не UIScrollView, который неявно прокручивает отредактированное UITextField в представление, это UITextField, который вызывает закрытый [UITextField scrollTextFieldToVisibleIfNecessary]метод, который в свою очередь вызывает [UIScrollView scrollRectToVisible]когда [UITextField becomeFirstResponder]вызывается. См. Github.com/leopatras/ios_textfields_on_scrollview . Если ограничения и контроллеры представления установлены правильно, фактически нет необходимости вызывать scrollRectToVisibleявно (по крайней мере, начиная с IOS 11).
Лев
48

Одна вещь, чтобы рассмотреть, хотите ли вы когда-либо использовать UITextFieldсамостоятельно. Я не сталкивался ни с какими хорошо разработанными приложениями для iPhone, которые действительно используют UITextFieldsза пределами UITableViewCells.

Это будет некоторая дополнительная работа, но я рекомендую вам реализовать все представления ввода данных в виде таблиц. Добавьте UITextViewк своему UITableViewCells.

Джонатан Стерлинг
источник
1
Одно из моих приложений должно позволять пользователям добавлять заметки произвольной формы, так что да, иногда полезно использовать UITextField.
Питер Джонсон
1
Я согласен с этим методом. Ноль работы или код таким образом. Даже если вам нужна записка свободной формы, вы все равно можете использовать ячейку таблицы
RJH
UITableViewк сожалению, единственный путь. Уведомления клавиатуры хрупкие и со временем изменились. Пример кода для переполнения стека: stackoverflow.com/a/32390936/218152
SwiftArchitect
Этот ответ устарел примерно на пять лет . Единственное современное решение - что-то вроде этого ... stackoverflow.com/a/41808338/294884
Толстяк
47

Этот документ подробно описывает решение этой проблемы. Посмотрите на исходный код в разделе «Перемещение содержимого, расположенного под клавиатурой». Это довольно просто.

РЕДАКТИРОВАТЬ: заметил, что в примере есть крошечный глюк. Вы, вероятно, захотите слушать UIKeyboardWillHideNotificationвместо UIKeyboardDidHideNotification. В противном случае представление прокрутки позади клавиатуры будет обрезано на время анимации закрытия клавиатуры.

Mihai Damian
источник
32

Самое простое решение найдено

- (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];
}
Джавад Зеб
источник
Экран движется вверх, даже если он не внизу. то есть, если текстовое поле находится сверху, оно выходит за пределы экрана. Как контролировать этот случай?
MELWIN
@MELWIN Просто добавьте после этой строки: int movement = (up ? -movementDistance : movementDistance); if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 }Пожалуйста, обратите внимание, что keyboardпеременная - это CGRect клавиатуры, которая появляется, когда вы получаете:let keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
CapturedTree
31

Небольшое исправление, которое работает для многих UITextFields

#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
источник
rect.origin.y=+currTextField.frame.origin.yработает хорошо, спасибо
u.gen
30

Код RPDP успешно перемещает текстовое поле с клавиатуры. Но когда вы прокручиваете до верха после использования и отклонения клавиатуры, верхняя часть прокручивается вверх из вида. Это верно для симулятора и устройства. Чтобы прочитать содержимое в верхней части этого представления, необходимо перезагрузить представление.

Разве его следующий код не должен вернуть представление вниз?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
Стив
источник
23

Я не уверен, что перемещение представления вверх является правильным подходом, я сделал это другим способом, изменив размер UIScrollView. Я объяснил это подробно в маленькой статье

Jose Muanis
источник
Ссылка на статью мертва.
Teo
22

Чтобы вернуть исходное состояние просмотра, добавьте:

-(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];
    }
}
Николас Маршан
источник
20

Попробуйте этот короткий трюк.

- (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
источник
19

Там так много решений, но я потратил несколько часов, прежде чем он заработал. Итак, я поместил этот код здесь (просто вставьте в проект, никаких изменений не нужно):

@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: я надеюсь, что код поможет кому-то быстро добиться желаемого эффекта. (Xcode 4.5)

HotJard
источник
Привет, Hotjard, я получаю EXE_BAD_ACCESS в [self.view addSubview: natView];
Бала
18

@ user271753

Чтобы вернуться к исходному виду, добавьте:

-(BOOL)textFieldShouldReturn:(UITextField *)textField{
   [textField resignFirstResponder];
   [self setViewMovedUp:NO];
   return YES;
}
user436179
источник
16

Для перемещения рамки просмотра не требуется представление прокрутки. Вы можете изменить рамку viewcontroller'sвида так, чтобы весь вид двигался вверх настолько, чтобы поместить текстовое поле первого респондента над клавиатурой. Когда я столкнулся с этой проблемой, я создал подклассUIViewController который делает это. Он наблюдает за тем, чтобы на клавиатуре появлялось уведомление, и находил подпредставление первого респондента и (при необходимости) он анимировал основной вид вверх настолько, чтобы первый респондент находился над клавиатурой. Когда клавиатура скрывается, она оживляет вид назад, где она была.

Чтобы использовать этот подкласс, сделайте свой собственный контроллер представления подклассом GMKeyboardVC, и он наследует эту функцию (просто убедитесь, что вы реализуете, viewWillAppearи viewWillDisappearони должны вызывать super). Класс на github .

прогрмр
источник
Какая лицензия? Некоторые из ваших файлов имеют лицензию с открытым исходным кодом, а некоторые нет.
Хайме
Предупреждение: этот код не подходит для проектов ARC.
Almo
Вы просто добавляете опцию сборки, чтобы указать, что это файлы не ARC, или можете преобразовать ее в ARC и отправить запрос на извлечение.
прогрмр
14

Свифт 4 .

Вы можете легко перемещаться вверх и вниз UITextFieldИли UIViewС UIKeyBoardWithAnimation введите описание изображения здесь

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
источник
2
Практически идеально. На iPhone X вы видите странный разрыв между клавиатурой и текстовым полем.
Хоуман
12

Вот хакерское решение, которое я придумал для конкретного макета. Это решение похоже на решение Мэтта Галлахера в том смысле, что оно отображает раздел. Я все еще новичок в разработке для iPhone, и не знаю, как работают макеты. Таким образом, это взломать.

Моя реализация должна была поддерживать прокрутку при нажатии на поле, а также прокрутку, когда пользователь выбирает следующий на клавиатуре.

У меня был UIView с высотой 775. Элементы управления распределены в основном группами по 3 на большом пространстве. Я закончил со следующим макетом IB.

UIView -> UIScrollView -> [UI Components]

Здесь приходит взломать

Я установил высоту UIScrollView на 500 единиц больше, чем фактический макет (1250). Затем я создал массив с абсолютными позициями, к которым нужно прокрутиться, и простой функцией для получения их на основе номера тега IB.

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

Теперь все, что вам нужно сделать, это использовать следующие две строки кода в textFieldDidBeginEditing и textFieldShouldReturn (последняя, ​​если вы создаете навигацию по следующему полю)

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

Пример.

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

Этот метод не «прокручивает назад», как другие методы. Это не было требованием. Опять же, это было для довольно «высокого» UIView, и у меня не было дней, чтобы изучить механизмы внутреннего макета.

Стив Макфарлин
источник
12

Согласно документам , начиная с iOS 3.0, UITableViewControllerкласс автоматически изменяет размеры и перемещает свое табличное представление, когда есть встроенное редактирование текстовых полей. Я думаю, что недостаточно поместить текстовое поле внутриUITableViewCell как указали некоторые.

Из документов :

Контроллер табличного представления поддерживает встроенное редактирование строк табличного представления; если, например, строки имеют встроенные текстовые поля в режиме редактирования, он прокручивает редактируемую строку над отображаемой виртуальной клавиатурой.

Дирай В.С.
источник
Я нашел тот же комментарий. Да, это правда. Странно то, что он работает в одном UITabelViewController, а во втором - нет. Но я не смог найти никаких различий в моей реализации.
Morpheus78
11

Здесь я нашел самое простое решение для обработки клавиатуры.

Вам нужно просто скопировать и вставить ниже пример кода и изменить текстовое поле или любое представление, которое вы хотите переместить вверх.

Шаг 1

Просто скопируйте и вставьте ниже два метода в свой контроллер

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

Шаг 2

зарегистрируйте и отмените регистрацию уведомлений клавиатуры в методах viewWillAppear и viewWillDisappear соответственно.

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

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

Шаг 3

Здесь идет часть души. Просто замените свое текстовое поле и измените высоту, насколько вы хотите двигаться вверх.

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

Ссылка : хорошо, пожалуйста, оцените этого парня , который поделился этим красивым фрагментом кода, чистым решением.

Надеюсь, это кому-то очень поможет.

swiftBoy
источник
Я не думаю, что это лучшее. Я думаю, @Dheeraj VS прав: это можно сделать легко и автоматически, если это текстовое поле находится в ячейке таблицы (даже если table.scrollable = NO). Обратите внимание : положение и размер стола должны быть разумными. Например: - если позиция y таблицы равна 100 от нижней части представления, тогда клавиатура высотой 300 будет перекрывать всю таблицу. - если высота таблицы = 10, а текстовое поле в ней должно быть прокручено на 100, когда клавиатура появляется, чтобы быть видимой, то это текстовое поле будет вне границы таблицы.
samthui7
@ samthui7 Ответ Dheeraj работает, только если вы используете TableViewController, а не просто просмотр таблицы. Это делает это ограничение, которое иногда не подходит.
Бен Г
10

В поисках хорошего учебника для начинающих по этому вопросу, нашел лучший учебник здесь .

В MIScrollView.hпримере внизу урока обязательно поставьте пробел в

@property (nonatomic, retain) id backgroundTapDelegate;

как видишь.

savagenoob
источник
Привет savagenoob, спасибо за предоставленную ссылку и добро пожаловать в stackoverflow. Пожалуйста, постарайтесь предоставить как можно больше информации при ответе на (будущие) вопросы - простые ссылки немного хрупкие. Тем не менее, если ответ является ссылкой на хороший учебник, который может быть пропущен.
Maarten Bodewes
10

Когда UITextFieldесть UITableViewCellпрокрутка должна быть настроена автоматически.

Если это не так, вероятно, из-за неправильного кода / настройки таблицы.

Например, когда я перезагрузил свою длинную таблицу с одной UITextFieldвнизу, как показано ниже,

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

затем мое текстовое поле внизу было скрыто клавиатурой, которая появилась, когда я щелкнул внутри текстового поля.

Чтобы исправить это, я должен был сделать это -

-(void) viewWillAppear:(BOOL)animated
{
    //add the following line to fix issue
    [super viewWillAppear:animated];
    [self.tableview reloadData];
}
lakersgonna3peat
источник
Я запутался, для чего этот код? Когда клавиатура показана, viewWillAppearне вызывается. И reloadDataне делает затемненные строки видимыми.
Адам Джонс
10

Используйте это третье лицо, вам не нужно писать даже одну строку

https://github.com/hackiftekhar/IQKeyboardManager

скачать проект и перетащить IQKeyboardManagerв свой проект. Если вы обнаружите какие-либо проблемы, пожалуйста, прочитайте READMEдокумент.

Ребята действительно убирают головную боль, чтобы управлять клавиатурой.

Арвинд Кумар
источник
8

Примечание : этот ответ предполагает, что ваш textField находится в scrollView.

Я предпочитаю иметь дело с этим, используя scrollContentInset и scrollContentOffset вместо того, чтобы возиться с кадрами моего представления.

Сначала давайте послушаем уведомления клавиатуры

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

Следующим шагом является сохранение свойства, которое представляет текущий первый респондент (UITextfield / UITextVIew, который в настоящее время имеет клавиатуру).

Мы используем методы делегата, чтобы установить это свойство. Если вы используете другой компонент, вам понадобится нечто подобное.

Обратите внимание, что для текстового поля мы устанавливаем его в didBeginEditing, а для textView - в mustBeginEditing. Это потому, что textViewDidBeginEditing вызывается после UIKeyboardWillShowNotification по какой-то причине.

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

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

Наконец, вот волшебство

- (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;
}
Юрай Петрик
источник
8

Это решение с использованием 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
    }

}
оборота Хомам
источник
Правильный ответ, но у меня проблема с нулем при использовании TextField и TextView. Любая помощь?
Тиха Аунг
@Thiha Aung, Ваши переменные IBOutlet в вашем исходном коде связаны с IB?
Хомам
Да, они также связаны. Просто с этой ошибкой при использовании UITextView в этой строке: if (! CGRectContainsPoint (aRect, self.activeTextField.frame.origin)) {
Тиха Аунг
Означает, что self.activeTextField равен нулю
Тиха Аунг