ios 当键盘存在时,如何使 UITextField 向上移动 - 在开始编辑时?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1126726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I make a UITextField move up when the keyboard is present - on starting to edit?
提问by philfreo
With the iOS SDK:
使用 iOS SDK:
I have a UIView
with UITextField
s that bring up a keyboard. I need it to be able to:
我有一个UIView
带UITextField
s 的键盘。我需要它能够:
Allow scrolling of the contents of the
UIScrollView
to see the other text fields once the keyboard is brought upAutomatically "jump" (by scrolling up) or shortening
UIScrollView
启动键盘后,允许滚动 的内容以查看其他文本字段自动“跳转”(通过向上滚动)或缩短
I know that I need a UIScrollView
. I've tried changing the class of my UIView
to a UIScrollView
but I'm still unable to scroll the textboxes up or down.
我知道我需要一个UIScrollView
. 我已经尝试将 my 的类更改UIView
为 aUIScrollView
但我仍然无法向上或向下滚动文本框。
Do I need both a UIView
and a UIScrollView
? Does one go inside the other?
我需要 aUIView
和 aUIScrollView
吗?一个会进入另一个吗?
What needs to be implemented in order to automatically scroll to the active text field?
需要实现什么才能自动滚动到活动文本字段?
Ideally as much of the setup of the components as possible will be done in Interface Builder. I'd like to only write code for what needs it.
理想情况下,尽可能多的组件设置将在 Interface Builder 中完成。我只想为需要的东西编写代码。
Note: the UIView
(or UIScrollView
) that I'm working with is brought up by a tabbar (UITabBar
), which needs to function as normal.
注意:我正在使用的UIView
(或UIScrollView
) 是由标签栏 ( UITabBar
) 显示的,它需要正常运行。
Edit: I am adding the scroll bar just for when the keyboard comes up. Even though it's not needed, I feel like it provides a better interface because then the user can scroll and change textboxes, for example.
编辑:我只是在键盘出现时添加滚动条。尽管不需要它,但我觉得它提供了一个更好的界面,因为这样用户就可以滚动和更改文本框,例如。
I've got it working where I change the frame size of the UIScrollView
when the keyboard goes up and down. I'm simply using:
我已经让它工作了,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
}
However, this doesn't automatically "move up" or center the lower text fields in the visible area, which is what I would really like.
但是,这不会自动“向上移动”或将可见区域的下部文本字段居中,这正是我真正想要的。
采纳答案by RPDP
You will only need a
ScrollView
if the contents you have now do not fit in the iPhone screen. (If you are adding theScrollView
as the superview of the components just to make theTextField
scroll up when keyboard comes up, then it's not needed.)The standard way to prevent the
TextField
s from being covered by the keyboard is to move the view up/down whenever the keyboard is shown.
ScrollView
如果您现在拥有的内容不适合 iPhone 屏幕,您将只需要一个。(如果您添加ScrollView
作为组件的超级视图只是为了TextField
在键盘出现时向上滚动,则不需要。)防止
TextField
s 被键盘覆盖的标准方法是在显示键盘时向上/向下移动视图。
Here is some sample code:
下面是一些示例代码:
#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];
}
回答by Shiun
I was also having a lot of issue with a UIScrollView
composing of multiple UITextFields
, of which, one or more of them would get obscured by the keyboard when they are being edited.
我在UIScrollView
组合多个时也遇到了很多问题UITextFields
,其中一个或多个在编辑时会被键盘遮挡。
Here are some things to consider if your UIScrollView
is not properly scrolling.
如果您UIScrollView
没有正确滚动,这里有一些事情需要考虑。
1) Ensure that your contentSize is greater than the UIScrollView
frame size. The way to understand UIScrollViews
is that the UIScrollView
is like a viewing window on the content defined in the contentSize. So when in order for the UIScrollview
to scroll anywhere, the contentSize must be greater than the UIScrollView
. Else, there is no scrolling required as everything defined in the contentSize is already visible. BTW, default contentSize = CGSizeZero
.
1) 确保您的 contentSize 大于UIScrollView
框架大小。理解的方法UIScrollViews
是,UIScrollView
就像在 contentSize 中定义的内容上的查看窗口。因此,当为了UIScrollview
滚动到任何地方时, contentSize 必须大于UIScrollView
. 否则,不需要滚动,因为 contentSize 中定义的所有内容都已经可见。顺便说一句,默认 contentSize = CGSizeZero
。
2) Now that you understand that the UIScrollView
is really a window into your "content", the way to ensure that the keyboard is not obscuring your UIScrollView's
viewing "window" would be to resize the UIScrollView
so that when the keyboard is present, you have the UIScrollView
window sized to just the original UIScrollView
frame.size.height minus the height of the keyboard. This will ensure that your window is only that small viewable area.
2) 既然您知道这UIScrollView
确实是一个进入“内容”的窗口,那么确保键盘不会遮挡您的UIScrollView's
查看“窗口”的方法是调整其大小,UIScrollView
以便当键盘存在时,您就有了UIScrollView
窗口大小为原始UIScrollView
frame.size.height 减去键盘的高度。这将确保您的窗口只是那个小的可视区域。
3) Here's the catch: When I first implemented this I figured I would have to get the CGRect
of the edited textfield and call UIScrollView's
scrollRecToVisible method. I implemented the UITextFieldDelegate
method textFieldDidBeginEditing
with the call to the scrollRecToVisible
method. This actually worked with a strange side effect that the scrolling would snapthe UITextField
into position. For the longest time I couldn't figure out what it was. Then I commented out the textFieldDidBeginEditing
Delegate method and it all work!!(???). As it turned out, I believe the UIScrollView
actually implicitly brings the currently edited UITextField
into the viewable window implicitly. My implementation of the UITextFieldDelegate
method and subsequent call to the scrollRecToVisible
was redundant and was the cause of the strange side effect.
3)这里有一个问题:当我第一次实现这个时,我想我必须获得CGRect
编辑过的文本字段的 并调用UIScrollView's
scrollRecToVisible 方法。我实现了UITextFieldDelegate
方法textFieldDidBeginEditing
与调用scrollRecToVisible
方法。这实际上与一个奇怪的副作用是滚动将工作卡的UITextField
到位。在很长一段时间里,我无法弄清楚它是什么。然后我注释掉了textFieldDidBeginEditing
Delegate 方法,一切正常!!(???)。事实证明,我相信UIScrollView
实际上隐式地将当前编辑的内容隐式地带UITextField
入了可视窗口。我对该UITextFieldDelegate
方法的实现和随后对 的调用scrollRecToVisible
是多余的,并且是造成奇怪副作用的原因。
So here are the steps to properly scroll your UITextField
in a UIScrollView
into place when the keyboard appears.
因此,这里是正常滚动的步骤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;
}
- Register for the keyboard notifications at
viewDidLoad
- Unregister for the keyboard nofitications at
viewDidUnload
- Ensure that the
contentSize
is set and greater than yourUIScrollView
atviewDidLoad
- Shrinkthe
UIScrollView
when the keyboard is present - Revert backthe
UIScrollView
when the keyboard goes away. - Use an ivar to detect if the keyboard is already shown on the screen since the keyboard notifications are sent each time a
UITextField
is tabbed even if the keyboard is already present to avoid shrinkingtheUIScrollView
when it's already shrunk
- 在以下位置注册键盘通知
viewDidLoad
- 取消注册键盘通知
viewDidUnload
- 确保
contentSize
已设置且大于您的UIScrollView
atviewDidLoad
- 收缩的
UIScrollView
当键盘存在 - 恢复的
UIScrollView
时候键盘消失。 - 使用伊娃如果键盘已经显示在屏幕上,以检测由于键盘通知每一次发送
UITextField
的标签即使键盘已经存在,以避免缩水的UIScrollView
时候它已经缩水
One thing to note is that the UIKeyboardWillShowNotification
will fire even when the keyboard is already on the screen when you tab on another UITextField
. I took care of this by using an ivar to avoid resizing the UIScrollView
when the keyboard is already on the screen. Inadvertently resizing the UIScrollView
when the keyboard is already there would be disastrous!
需要注意的一件事是,UIKeyboardWillShowNotification
即使当您点击另一个 时键盘已经在屏幕上, 也会触发UITextField
。我通过使用 ivar 来解决这个问题,以避免UIScrollView
在键盘已经在屏幕上时调整大小。UIScrollView
当键盘已经存在时,无意中调整大小将是灾难性的!
Hope this code saves some of you a lot of headache.
希望这段代码可以为你们中的一些人省去很多麻烦。
回答by DK_
It's actually best just to use Apple's implementation, as provided in the docs. However, the code they provide is faulty. Replace the portion found in keyboardWasShown:
just below the comments to the following:
实际上最好只使用 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];
}
The problems with Apple's code are these:
(1) They always calculate if the point is within the view's frame, but it's a ScrollView
, so it may already have scrolled and you need to account for that offset:
Apple 代码的问题如下: (1) 他们总是计算点是否在视图的框架内,但它是 a ScrollView
,所以它可能已经滚动,您需要考虑该偏移量:
origin.y -= scrollView.contentOffset.y
(2) They shift the contentOffset by the height of the keyboard, but we want the opposite (we want to shift the contentOffset
by the height that is visible on the screen, not what isn't):
(2) 他们将 contentOffset 移动键盘的高度,但我们想要相反的(我们想移动contentOffset
屏幕上可见的高度,而不是不可见的高度):
activeField.frame.origin.y-(aRect.size.height)
回答by sumanthkodi
In textFieldDidBeginEditting
and in textFieldDidEndEditing
call the function [self animateTextField:textField up:YES]
like so:
IntextFieldDidBeginEditting
和 intextFieldDidEndEditing
调用函数,[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];
}
I hope this code will help you.
我希望这段代码能帮到你。
In Swift 2
在斯威夫特 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
斯威夫特 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)
}
回答by DAS
Just using TextFields:
仅使用 TextFields:
1a) Using Interface Builder
: Select All TextFields => Edit => Embed In => ScrollView
1a) 使用Interface Builder
:选择所有文本字段 => 编辑 => 嵌入 => ScrollView
1b) Manually embed TextFields in UIScrollView called scrollView
1b) 在 UIScrollView 中手动嵌入 TextFields,称为 scrollView
2) Set UITextFieldDelegate
2) 设置 UITextFieldDelegate
3) Set each textField.delegate = self;
(or make connections in Interface Builder
)
3)设置每个textField.delegate = self;
(或在 中建立连接Interface Builder
)
4) Copy / Paste:
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];
}
回答by Mohd Iftekhar Qurashi
For Universal Solution, Here was my approach for implementing IQKeyboardManager.
对于通用解决方案,这是我实现IQKeyboardManager 的方法。
Step1:-I Added global notifications of UITextField
, UITextView
, and UIKeyboard
in a singleton class. I call it IQKeyboardManager.
第一步: -我加了全球的通知UITextField
,UITextView
和UIKeyboard
在一个单独的类。我称之为IQKeyboardManager。
Step2:-If found UIKeyboardWillShowNotification
, UITextFieldTextDidBeginEditingNotification
or UITextViewTextDidBeginEditingNotification
notifications, I try to get topMostViewController
instance from the UIWindow.rootViewController
hierarchy. In order to properly uncover UITextField
/UITextView
on it, topMostViewController.view
's frame needs to be adjusted.
第二步: -如果找到UIKeyboardWillShowNotification
,UITextFieldTextDidBeginEditingNotification
或UITextViewTextDidBeginEditingNotification
通知,我试图让topMostViewController
从实例UIWindow.rootViewController
层次结构。为了正确地揭开UITextField
/UITextView
在它上面,topMostViewController.view
需要调整 的框架。
Step3:-I calculated expected move distance of topMostViewController.view
with respect to first responded UITextField
/UITextView
.
第3步:-我计算topMostViewController.view
了相对于第一次响应的预期移动距离UITextField
/ UITextView
。
Step4:-I moved topMostViewController.view.frame
up/down according to the expected move distance.
Step4:-我topMostViewController.view.frame
根据预期的移动距离向上/向下移动。
Step5:-If found UIKeyboardWillHideNotification
, UITextFieldTextDidEndEditingNotification
or UITextViewTextDidEndEditingNotification
notification, I again try to get topMostViewController
instance from the UIWindow.rootViewController
hierarchy.
第五步: -如果找到UIKeyboardWillHideNotification
,UITextFieldTextDidEndEditingNotification
或UITextViewTextDidEndEditingNotification
通知,我再次试图让topMostViewController
从实例UIWindow.rootViewController
层次结构。
Step6:-I calculated disturbed distance of topMostViewController.view
which needs to be restored to it's original position.
Step6:-我计算了topMostViewController.view
需要恢复到原来位置的扰动距离。
Step7:-I restored topMostViewController.view.frame
down according to the disturbed distance.
Step7:-我topMostViewController.view.frame
根据扰动的距离还原下来。
Step8:-I instantiated singleton IQKeyboardManagerclass instance on app load, so every UITextField
/UITextView
in the app will adjust automatically according to the expected move distance.
步骤 8:-我在应用加载时实例化了单例IQKeyboardManager类实例,因此应用中的每个UITextField
/UITextView
都会根据预期的移动距离自动调整。
That's all IQKeyboardManagerdo for you with NO LINE OF CODEreally!! only need to drag and drop related source file to project. IQKeyboardManageralso support Device Orientation, Automatic UIToolbar Management, KeybkeyboardDistanceFromTextFieldand much more than you think.
这就是IQKeyboardManager为您所做的一切,真的没有代码行!!只需将相关的源文件拖放到项目中即可。IQKeyboardManager还支持Device Orientation、Automatic UIToolbar Management、KeybkeyboardDistanceFromTextField等等。
回答by Michael Tyson
I've put together a universal, drop-in UIScrollView
, UITableView
and even UICollectionView
subclass that takes care of moving all text fields within it out of the way of the keyboard.
我已经把一个普遍的,插入式UIScrollView
,UITableView
甚至UICollectionView
子类,拍摄运动中的所有文本字段出了键盘的方式照顾。
When the keyboard is about to appear, the subclass will find the subview that's about to be edited, and adjust its frame and content offset to make sure that view is visible, with an animation to match the keyboard pop-up. When the keyboard disappears, it restores its prior size.
当键盘即将出现时,子类会找到即将编辑的子视图,并调整其框架和内容偏移以确保该视图可见,并带有动画以匹配键盘弹出。当键盘消失时,它会恢复原来的大小。
It should work with basically any setup, either a UITableView
-based interface, or one consisting of views placed manually.
它应该适用于基本上任何设置,无论是UITableView
基于界面的界面,还是由手动放置的视图组成的界面。
Here' tis: solution for moving text fields out of the way of the keyboard
回答by Codetard
For SwiftProgrammers :
对于Swift程序员:
This will do everything for you, just put these in your view controller class and implement the UITextFieldDelegate
to your view controller & set the textField's delegate to self
这将为您做所有事情,只需将这些放在您的视图控制器类中并实现UITextFieldDelegate
到您的视图控制器并将 textField 的委托设置为self
textField.delegate = self // Setting delegate of your UITextField to self
Implement the delegate callback methods:
实现委托回调方法:
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()
}
For Swift 4, 4.2, 5: Change
对于 Swift 4、4.2、5:更改
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
to
到
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
Last note about this implementation: If you push another view controller onto the stack while the keyboard is shown, this will create an error where the view is returned back to its center frame but keyboard offset is not reset. For example, your keyboard is the first responder for nameField, but then you push a button that pushes your Help View Controller onto your stack. To fix the offset error, make sure to call nameField.resignFirstResponder() before leaving the view controller, ensuring that the textFieldDidEndEditing delegate method is called as well. I do this in the viewWillDisappear method.
关于此实现的最后一个注意事项:如果在显示键盘时将另一个视图控制器推入堆栈,这将导致视图返回其中心框架但未重置键盘偏移量的错误。例如,您的键盘是 nameField 的第一响应者,但随后您按下一个按钮,将您的帮助视图控制器推送到您的堆栈中。要修复偏移错误,请确保在离开视图控制器之前调用 nameField.resignFirstResponder(),确保同时调用 textFieldDidEndEditing 委托方法。我在 viewWillDisappear 方法中执行此操作。
回答by Martin Ullrich
There are already a lot of answers, but still none of the solutions above had all the fancy positioning stuff required for a "perfect" bug-free, backwards compatible and flicker-free animation. (bug when animating frame/bounds and contentOffset together, different interface orientations, iPad split keyboard, ...)
Let me share my solution:
(assuming you have set up UIKeyboardWill(Show|Hide)Notification
)
已经有很多答案,但仍然没有上述解决方案具有“完美”无错误、向后兼容和无闪烁动画所需的所有花哨定位内容。(将 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];
}
回答by cbranch
Shiun said "As it turned out, I believe the UIScrollView actually implicitly brings the currently edited UITextField into the viewable window implicitly" This seems to be true for iOS 3.1.3, but not 3.2, 4.0, or 4.1. I had to add an explicit scrollRectToVisible in order to make the UITextField visible on iOS >= 3.2.
Shiun 说:“事实证明,我相信 UIScrollView 实际上隐式地将当前编辑的 UITextField 隐式地带入了可视窗口中”这对于 iOS 3.1.3 似乎是正确的,但对于 3.2、4.0 或 4.1 来说则不然。我必须添加一个显式的 scrollRectToVisible 才能使 UITextField 在 iOS >= 3.2 上可见。