当键盘存在时,如何使 UITextField 向上移动 - 开始编辑?
-
13-09-2019 - |
题
使用 iOS SDK:
我有一个 UIView
和 UITextField
调出键盘。我需要它能够:
允许滚动内容
UIScrollView
键盘打开后即可查看其他文本字段自动“跳跃”(通过向上滚动)或缩短
我知道我需要一个 UIScrollView
. 。我尝试过改变我的班级 UIView
到一个 UIScrollView
但我仍然无法向上或向下滚动文本框。
我需要两个吗 UIView
和一个 UIScrollView
?一个会进入另一个的内部吗?
需要实现什么才能自动滚动到活动文本字段?
理想情况下,尽可能多的组件设置将在 Interface Builder 中完成。我只想为需要的地方编写代码。
笔记:这 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
}
但是,这不会自动“向上移动”或将下部文本字段置于可见区域的中心,而这正是我真正想要的。
解决方案
您只需要一个
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 中定义的所有内容都已经可见。顺便说一句,默认内容大小= CGSizeZero
.
2) 现在你明白了 UIScrollView
实际上是了解“内容”的一个窗口,确保键盘不会遮挡您的内容 UIScrollView's
查看“窗口”将调整大小 UIScrollView
这样当键盘存在时,您就可以 UIScrollView
窗口大小与原始大小相同 UIScrollView
frame.size.height 减去键盘的高度。这将确保您的窗口只有那么小的可视区域。
3)这里有一个问题:当我第一次实现这个时,我想我必须得到 CGRect
编辑的文本字段并调用 UIScrollView's
滚动RecToVisible 方法。我实施了 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)
}
环球银行金融电信协会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)
}
仅使用文本字段:
1a) 使用 Interface Builder
:选择所有文本字段 => 编辑 => 嵌入 => ScrollView
1b) 在称为scrollView的UIScrollView中手动嵌入TextFields
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];
}
为了 通用解决方案, ,这是我的实施方法 IQ键盘管理器.
步骤1:- 我添加了全局通知 UITextField
, UITextView
, , 和 UIKeyboard
在单例类中。我称之为 IQ键盘管理器.
第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:- 我实例化了单例 IQ键盘管理器 应用程序加载时的类实例,因此每个 UITextField
/UITextView
应用程序中会根据预期的移动距离自动调整。
就这样 IQ键盘管理器 为你做 无需一行代码 真的吗!!只需要将相关源文件拖放到项目中即可。 IQ键盘管理器 也支持 设备方向, 自动 UI 工具栏管理, Keybkeyboard距离TextField 比你想象的要多得多。
我已经组装了一个通用的、插入式的 UIScrollView
, UITableView
乃至 UICollectionView
子类,负责将其中的所有文本字段移出键盘。
当键盘即将出现时,子类将找到即将编辑的子视图,并调整其框架和内容偏移量以确保该视图可见,并带有与键盘弹出窗口相匹配的动画。当键盘消失时,它会恢复原来的大小。
它基本上应该适用于任何设置,无论是 UITableView
基于 的界面,或由手动放置的视图组成的界面。
这是: 将文本字段移出键盘的解决方案
为了 迅速 程序员:
这将为您做所有事情,只需将它们放入您的视图控制器类中并实现 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 的第一响应者,但随后您按下一个按钮,将帮助视图控制器推送到堆栈上。要修复偏移错误,请确保在离开视图控制器之前调用 nameField.resignFirstResponder(),并确保同时调用 textFieldDidEndEditing 委托方法。我在 viewWillDisappear 方法中执行此操作。
已经有很多答案,但上述解决方案仍然没有一个具有“完美”无错误、向后兼容和无闪烁动画所需的所有精美定位内容。(同时对框架/边界和 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 说,“事实证明,我相信 UIScrollView 实际上隐式地将当前编辑的 UITextField 隐式地带入可视窗口” 这对于 iOS 3.1.3 似乎是正确的,但对于 3.2、4.0 或 4.1 则不然。我必须添加显式的scrollRectToVisible 才能使UITextField 在iOS >= 3.2 上可见。
这 文档详细介绍了该问题的解决方案。查看“移动位于键盘下的内容”下的源代码。这非常简单。
编辑:注意到示例中有一个小故障。您可能想听 UIKeyboardWillHideNotification
代替 UIKeyboardDidHideNotification
. 。否则,键盘后面的滚动视图将在键盘关闭动画期间被剪切。
需要考虑的一件事是您是否想要使用 UITextField
在其自己的。我还没有遇到过任何设计精良、实际使用的 iPhone 应用程序 UITextFields
在外面 UITableViewCells
.
这将是一些额外的工作,但我建议您将所有数据输入视图实现为表视图。添加一个 UITextView
给你的 UITableViewCells
.
找到最简单的解决方案
- (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];
}
适用于许多 UITextField 的小修复
#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;
}
我不确定向上移动视图是否是正确的方法,我以不同的方式做到了,调整 UIScrollView 的大小。我详细解释了一点 文章
要恢复到原始视图状态,请添加:
-(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];
}
}
试试这个小技巧。
- (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;
}
附:我希望代码可以帮助人们快速达到预期的效果。(Xcode 4.5)
@用户271753
要将您的视图恢复到原始添加:
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
[self setViewMovedUp:NO];
return YES;
}
它不需要滚动视图就可以移动视图框架。您可以更改框架 viewcontroller's
视图,以便整个视图向上移动到足以将第一响应者文本字段放在键盘上方。当我遇到这个问题时,我创建了一个子类 UIViewController
就是这样做的。它观察键盘将出现的通知并找到第一响应者子视图,并且(如果需要)它会将主视图向上动画化,以便第一响应者位于键盘上方。当键盘隐藏时,它会将视图动画恢复到原来的位置。
要使用此子类,请使您的自定义视图控制器成为以下子类 GMKeyboardVC 并且它继承了这个功能(只要确定你是否实现了 viewWillAppear
和 viewWillDisappear
他们必须调用 super)。上课了 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
}
}
这是我针对特定布局提出的黑客解决方案。该解决方案与 Matt Gallagher 解决方案类似,即将一个部分滚动到视图中。我对 iPhone 开发还是个新手,不熟悉布局的工作原理。因此,这个黑客。
我的实现需要支持在单击字段时滚动,以及当用户在键盘上选择下一个时支持滚动。
我有一个高度为 775 的 UIView。控制器基本上以 3 个为一组分布在一个很大的空间内。我最终得到了以下 IB 布局。
UIView -> UIScrollView -> [UI Components]
黑客来了
我将 UIScrollView 高度设置为比实际布局 (1250) 大 500 个单位。然后,我创建了一个包含需要滚动到的绝对位置的数组,以及一个根据 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步
注册和取消注册键盘通知 视图将出现 和 视图将消失 分别方法。
- (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];
}
参考:出色地, 请欣赏这个人, ,谁分享了这个漂亮的代码片段,干净的解决方案。
希望这对那里的人有帮助。
一直在为该主题的初学者寻找一个好的教程,找到了最好的教程 这里.
在里面 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];
}
使用这个第三方你甚至不需要写一行
https://github.com/hackiftekhar/IQKeyboardManager
下载项目并将 IQKeyboardManager 拖放到您的项目中。如果您发现任何问题,请阅读 README 文档。
伙计们,真的可以消除管理键盘的头痛..
感谢和好运!
笔记:这个答案假设您的文本字段位于滚动视图中。
我更喜欢使用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,我们在 shouldBeginEditing 中设置它。这是因为由于某种原因,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
}
}