How can I make a UITextField move up when the keyboard is present - on starting to edit?
С iOS SDK:
у меня есть UIView
с UITextField
с, которые воспитывают клавиатуру. Мне нужно, чтобы я мог:
Разрешить прокрутку содержимого
UIScrollView
чтобы увидеть другие текстовые поля, как только клавиатураАвтоматически "прыгать" (путем прокрутки вверх) или сокращения
Я знаю что мне нужна UIScrollView
, Я пытался изменить класс моего UIView
к 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
}
Тем не менее, это не автоматически "двигает вверх" или центрирует нижние текстовые поля в видимой области, что мне бы очень хотелось.
101 ответ
Вам понадобится только
ScrollView
если содержимое, которое у вас сейчас есть, не помещается на экране iPhone. (Если вы добавляетеScrollView
как супервизор компонентов. просто чтобыTextField
прокрутите вверх, когда клавиатура появится, тогда это не нужно.)Для показа
textfields
стандартным способом, не будучи скрытым за клавиатурой, является перемещение вверх / вниз представления, имеющего текстовые поля, всякий раз, когда отображается клавиатура.
Вот пример кода:
#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];
}
У меня также было много проблем с UIScrollView
составление нескольких UITextFields
, из которых один или несколько из них будут скрыты клавиатурой при редактировании.
Вот некоторые вещи, которые следует учитывать, если ваш UIScrollView
неправильно прокручивает
1) Убедитесь, что ваш contentSize больше, чем UIScrollView
Размер рамки. Способ понять UIScrollViews
это то, что UIScrollView
это как окно просмотра контента, определенного в contentSize. Итак, когда для того, чтобы UIScrollview
для прокрутки в любом месте, contentSize должен быть больше, чем UIScrollView
, Иначе, прокрутка не требуется, так как все, что определено в contentSize, уже видно. Кстати, по умолчанию contentSize = CGSizeZero
,
2) Теперь, когда вы понимаете, что UIScrollView
действительно окно в ваш "контент", способ гарантировать, что клавиатура не заслоняет ваш UIScrollView's
просмотр "окна" будет изменять размер UIScrollView
так что когда клавиатура присутствует, у вас есть UIScrollView
окно размером только с оригинала UIScrollView
frame.size.height минус высота клавиатуры. Это гарантирует, что ваше окно будет только этой маленькой видимой областью.
3) Вот подвох: когда я впервые реализовал это, я подумал, что мне нужно получить CGRect
отредактированного текстового поля и вызова UIScrollView's
метод scrollRecToVisible. Я реализовал UITextFieldDelegate
метод textFieldDidBeginEditing
с призывом к scrollRecToVisible
метод. Это на самом деле работало со странным побочным эффектом, что прокрутка UITextField
в положение. Долгое время я не мог понять, что это было. Затем я закомментировал textFieldDidBeginEditing
Метод делегата и все это работает!!(???). Как оказалось, я считаю, 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;
}
- Зарегистрируйтесь для получения уведомлений клавиатуры
viewDidLoad
- Отменить регистрацию для клавиатурных сообщений на
viewDidUnload
- Убедитесь, что
contentSize
установлен и больше, чем вашUIScrollView
вviewDidLoad
- Сожмите
UIScrollView
когда клавиатура присутствует - Вернуть назад
UIScrollView
когда клавиатура уходит. - Используйте ivar, чтобы определить, отображается ли уже клавиатура на экране, так как уведомления клавиатуры отправляются каждый раз, когда
UITextField
вкладка, даже если клавиатура уже присутствует, чтобы избежать сокращенияUIScrollView
когда он уже сжался
Стоит отметить, что UIKeyboardWillShowNotification
будет срабатывать, даже если клавиатура уже на экране, когда вы нажимаете на другую UITextField
, Я позаботился об этом, используя ivar, чтобы избежать изменения размера UIScrollView
когда клавиатура уже на экране. Случайное изменение размера UIScrollView
когда клавиатура уже есть, это будет иметь катастрофические последствия!
Надеюсь, этот код избавит некоторых из вас от головной боли.
На самом деле лучше всего использовать реализацию 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)
В 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)
}
Просто используя TextFields:
1a) Использование Interface Builder
: Выберите все текстовые поля => редактировать => встроить => 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];
}
Для универсального решения, вот мой подход к реализации 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
который должен быть восстановлен в исходное положение.
Step7: - Я восстановил topMostViewController.view.frame
вниз по нарушенному расстоянию.
Шаг 8:- Я создал экземпляр одиночного экземпляра класса IQKeyboardManager при загрузке приложения, поэтому каждый UITextField
/ UITextView
в приложении будет корректироваться автоматически в зависимости от предполагаемого расстояния перемещения.
Это все, что IQKeyboardManager делает для вас без ЛИНЕЙКИ КОДА! нужно только перетащить связанный исходный файл в проект. IQKeyboardManager также поддерживает ориентацию устройства, автоматическое управление UIToolbar, KeybkeyboardDistanceFromTextField и многое другое, чем вы думаете.
Я собрал универсальный, вставной UIScrollView
, UITableView
и даже UICollectionView
подкласс, который заботится о перемещении всех текстовых полей в нем из клавиатуры.
Когда клавиатура должна появиться, подкласс найдет подпредставление, которое должно быть отредактировано, и отрегулирует его рамку и смещение содержимого, чтобы убедиться, что представление является видимым, с анимацией, соответствующей всплывающей клавиатуре. Когда клавиатура исчезает, она восстанавливает свой прежний размер.
Он должен работать в основном с любой настройкой, либо UITableView
интерфейс, или один, состоящий из представлений, размещенных вручную.
Вот это: решение для перемещения текстовых полей с клавиатуры
Для программистов 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()
}
Уже есть много ответов, но все же ни одно из вышеприведенных решений не имело всех причудливых средств позиционирования, необходимых для "идеальной" безошибочной, обратно совместимой и не мерцающей анимации. (ошибка при анимации 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];
}
Шиун сказал: "Как выяснилось, я считаю, что UIScrollView фактически неявно переносит редактируемый в настоящий момент UITextField в видимое окно". Это похоже на iOS 3.1.3, но не 3.2, 4.0 или 4.1. Мне пришлось добавить явный scrollRectToVisible, чтобы сделать UITextField видимым на iOS >= 3.2.
Одна вещь, чтобы рассмотреть, хотите ли вы когда-либо использовать UITextField
сам по себе. Я не сталкивался ни с какими хорошо разработанными приложениями для iPhone, которые действительно используют UITextFields
вне UITableViewCells
,
Это будет некоторая дополнительная работа, но я рекомендую вам реализовать все представления ввода данных в виде таблицы. Добавить UITextView
на ваш UITableViewCells
,
Этот документ подробно описывает решение этой проблемы. Посмотрите на исходный код в разделе "Перемещение содержимого, расположенного под клавиатурой". Это довольно просто.
РЕДАКТИРОВАТЬ: заметил, что в примере есть крошечный глюк. Вы, вероятно, захотите послушать UIKeyboardWillHideNotification
вместо UIKeyboardDidHideNotification
, В противном случае представление прокрутки позади клавиатуры будет обрезано на время анимации закрытия клавиатуры.
Самое простое решение найдено
- (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];
}
Небольшое исправление, которое работает для многих 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];
}
Код RPDP успешно перемещает текстовое поле с клавиатуры. Но когда вы прокручиваете до верха после использования и отклонения клавиатуры, верхняя часть прокручивается вверх из вида. Это верно для симулятора и устройства. Чтобы прочитать содержимое в верхней части этого представления, необходимо перезагрузить представление.
Разве его следующий код не должен вернуть представление вниз?
else
{
// revert back to the normal state.
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
Чтобы вернуть исходное состояние просмотра, добавьте:
-(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];
}
}
Я не уверен, что перемещение представления вверх является правильным подходом, я сделал это другим способом, изменив размер UIScrollView. Я объяснил это подробно в маленькой статье
Попробуйте этот короткий трюк...
- (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];
}
Удачного кодирования:)
Там так много решений, но я потратил несколько часов, прежде чем начать работать. Итак, я поместил этот код здесь (просто вставьте в проект, никаких изменений не нужно):
@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)
@user271753
Чтобы вернуться к исходному виду, добавьте:
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
[self setViewMovedUp:NO];
return YES;
}
Для перемещения рамки просмотра не требуется представление с прокруткой. Вы можете изменить кадр viewcontroller's
вид так, что весь вид перемещается вверх настолько, чтобы поместить текстовое поле первого респондента над клавиатурой. Когда я столкнулся с этой проблемой, я создал подкласс UIViewController
это делает это Он наблюдает за тем, чтобы на клавиатуре появлялось уведомление, и находил подпредставление первого респондента и (при необходимости) он анимировал основной вид вверх настолько, чтобы первый респондент находился над клавиатурой. Когда клавиатура прячется, она оживляет вид назад, где она была.
Чтобы использовать этот подкласс, сделайте свой пользовательский контроллер представления подклассом GMKeyboardVC, и он наследует эту функцию (просто убедитесь, что вы реализуете viewWillAppear
а также viewWillDisappear
они должны назвать супер). Класс на github.
Свифт 4.
Вы можете легко двигаться вверх и вниз UITextField
Или же UIView
С UIKeyBoard
С Animation
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
}
}
Согласно документации, начиная с iOS 3.0, UITableViewController
Класс автоматически изменяет размеры и перемещает свое табличное представление, когда есть встроенное редактирование текстовых полей. Я думаю, что недостаточно поместить текстовое поле внутри UITableViewCell
как указали некоторые.
Из документов:
Контроллер табличного представления поддерживает встроенное редактирование строк табличного представления; если, например, строки имеют встроенные текстовые поля в режиме редактирования, он прокручивает редактируемую строку над отображаемой виртуальной клавиатурой.
Вот хакерское решение, которое я придумал для конкретного макета. Это решение похоже на решение Мэтта Галлахера в том смысле, что оно отображает раздел. Я все еще новичок в разработке для 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, и у меня не было дней, чтобы изучить механизмы внутреннего макета.
Здесь я нашел самое простое решение для обработки клавиатуры.
Вам нужно просто скопировать и вставить ниже пример кода и изменить текстовое поле или любое представление, которое вы хотите переместить вверх.
Шаг 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];
}
Справка: ну, пожалуйста, оцените этого парня, который поделился этим красивым фрагментом кода, чистым решением.
Надеюсь, это кому-то очень поможет.
Используйте это третье лицо, вам не нужно писать даже одну строку
https://github.com/hackiftekhar/IQKeyboardManager
загрузите проект и перетащите IQKeyboardManager в свой проект. Если вы обнаружите какие-либо проблемы, пожалуйста, прочитайте README документ.
Ребята действительно убрать головную боль, чтобы управлять клавиатурой..
Спасибо и удачи!
В поисках хорошего учебника для начинающих по этому вопросу, нашел лучший учебник здесь.
в MIScrollView.h
Например, в нижней части учебника обязательно поставьте пробел в
@property (nonatomic, retain) id backgroundTapDelegate;
как вы видите.
Когда 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];
}
Примечание: этот ответ предполагает, что ваш 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;
}
Это решение с использованием 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
}
}