[英]How can I make a UITextField move up when the keyboard is present - on starting to edit?
使用 iOS SDK:
我有一个带有UITextField
的UIView
可以调出键盘。 我需要它能够:
启动键盘后,允许滚动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
}
但是,这不会自动“向上移动”或将可见区域中的下部文本字段居中,这是我真正想要的。
如果您现在拥有的内容不适合 iPhone 屏幕,您将只需要一个ScrollView
。 (如果您将ScrollView
添加为组件的超级视图,只是为了在键盘启动时使TextField
向上滚动,则不需要。)
防止TextField
被键盘覆盖的标准方法是在显示键盘时向上/向下移动视图。
这是一些示例代码:
#define kOFFSET_FOR_KEYBOARD 80.0
-(void)keyboardWillShow {
// Animate the current view out of the way
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
else if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
-(void)keyboardWillHide {
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
else if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
if ([sender isEqual:mailTf])
{
//move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
}
}
//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3]; // if you want to slide up the view
CGRect rect = self.view.frame;
if (movedUp)
{
// 1. move the view's origin up so that the text field that will be hidden come above the keyboard
// 2. increase the size of the view so that the area behind the keyboard is covered up.
rect.origin.y -= kOFFSET_FOR_KEYBOARD;
rect.size.height += kOFFSET_FOR_KEYBOARD;
}
else
{
// revert back to the normal state.
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
self.view.frame = rect;
[UIView commitAnimations];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
我对由多个UITextFields
组成的UIScrollView
也有很多问题,其中一个或多个在编辑时会被键盘遮挡。
如果您的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 方法。 我通过调用scrollRecToVisible
方法实现了UITextFieldDelegate
方法textFieldDidBeginEditing
。 这实际上产生了一个奇怪的副作用,即滚动会将UITextField
捕捉到位。 很长一段时间我都不知道那是什么。 然后我注释掉了textFieldDidBeginEditing
Delegate 方法,一切正常!!(???)。 事实证明,我相信UIScrollView
实际上隐式地将当前编辑的UITextField
隐式地带入了可视窗口。 我对UITextFieldDelegate
方法的实现和随后对scrollRecToVisible
的调用是多余的,并且是导致奇怪副作用的原因。
因此,当键盘出现时,以下是正确滚动UIScrollView
中的UITextField
到位的步骤。
// 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
已设置且大于viewDidLoad
处的UIScrollView
UIScrollView
UIScrollView
。UITextField
被标签时都会发送键盘通知,即使键盘已经存在,以避免在UIScrollView
已经缩小时缩小它需要注意的一点是,当您在另一个UITextField
上进行选项卡时,即使键盘已经在屏幕上, UIKeyboardWillShowNotification
也会触发。 我通过使用 ivar 来解决这个问题,以避免在键盘已经在屏幕上时调整UIScrollView
的大小。 当键盘已经存在时无意中调整UIScrollView
的大小将是灾难性的!
希望这段代码可以为你们中的一些人省去很多麻烦。
实际上最好只使用 Apple 的实现,如docs中提供的那样。 但是,他们提供的代码是错误的。 将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];
}
苹果代码的问题是:(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];
}
我希望这段代码对你有帮助。
斯威夫特 5
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.animate(withDuration: movementDuration, delay: 0, options: [.beginFromCurrentState]) {
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
animateTextField(textField: textField, up: true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
animateTextField(textField: textField, up: false)
}
仅使用文本字段:
1a)使用Interface Builder
:选择所有文本字段=>编辑=>嵌入=>滚动视图
1b) 在 UIScrollView 中手动嵌入 TextFields 称为 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的方法。
Step1:-我在单例类中添加了UITextField
、 UITextView
和UIKeyboard
的全局通知。 我称之为IQKeyboardManager 。
Step2:-如果找到UIKeyboardWillShowNotification
、 UITextFieldTextDidBeginEditingNotification
或UITextViewTextDidBeginEditingNotification
通知,我尝试从UIWindow.rootViewController
层次结构中获取topMostViewController
实例。 为了正确发现UITextField
/ UITextView
,需要调整topMostViewController.view
的框架。
Step3:-我计算了topMostViewController.view
相对于第一个响应的UITextField
/ UITextView
的预期移动距离。
Step4:-我根据预期的移动距离向上/向下移动了topMostViewController.view.frame
。
Step5:-如果找到UIKeyboardWillHideNotification
、 UITextFieldTextDidEndEditingNotification
或UITextViewTextDidEndEditingNotification
通知,我再次尝试从UIWindow.rootViewController
层次结构中获取topMostViewController
实例。
Step6:-我计算了topMostViewController.view
的干扰距离,需要恢复到原来的位置。
Step7:-我根据干扰距离恢复了topMostViewController.view.frame
。
Step8:-我在应用程序加载时实例化了单例IQKeyboardManager类实例,因此应用程序中的每个UITextField
/ UITextView
都会根据预期的移动距离自动调整。
这就是IQKeyboardManager为您所做的一切,无需任何代码! 只需将相关的源文件拖放到项目中。 IQKeyboardManager还支持Device Orientation 、 Automatic UIToolbar Management 、 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()
}
对于 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 上可见。
要考虑的一件事是您是否想单独使用UITextField
。 我还没有遇到任何设计精良的 iPhone 应用程序实际上在UITableViewCells
之外使用UITextFields
。
这将是一些额外的工作,但我建议您将所有数据输入视图实现为表视图。 将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];
}
适用于许多 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 on 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 on 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
,它们必须调用 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
}
}
这是我为特定布局提出的 hack 解决方案。 此解决方案类似于 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步
分别在viewWillAppear和viewWillDisappear方法中注册和取消注册键盘通知。
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self registerForKeyboardNotifications];
}
- (void)viewWillDisappear:(BOOL)animated
{
[self deregisterFromKeyboardNotifications];
[super viewWillDisappear:animated];
}
第三步
灵魂部分来了,只需替换您的文本字段,并更改您想要向上移动的高度。
- (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
文档。
伙计们真的消除了管理键盘的麻烦。
当UITextField
在UITableViewCell
中时,应该自动设置滚动。
如果不是,可能是因为 tableview 的代码/设置不正确。
例如,当我重新加载我的长表时,底部有一个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];
}
一直在为该主题的初学者寻找一个很好的教程,在这里找到了最好的教程。
在教程底部的MIScrollView.h
示例中,请务必在
@property (nonatomic, retain) id backgroundTapDelegate;
正如你看到的。
注意:此答案假定您的 textField 位于滚动视图中。
我更喜欢使用 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)。
我们使用委托方法来设置这个属性。 如果您正在使用另一个组件,您将需要类似的东西。
请注意,对于 textfield,我们将其设置在 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
}
}
尝试这个:
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
if ([sender isEqual:self.m_Sp_Contact])
{
[self.m_Scroller setContentOffset:CGPointMake(0, 105)animated:YES];
}
}
斯威夫特 2.0:
添加一个 UIScrollView 并在其顶部添加 textFields。 将故事板引用到 VC。
@IBOutlet weak var username: UITextField!
@IBOutlet weak var password: UITextField!
@IBOutlet weak var scrollView: UIScrollView!
添加这些方法:UITextFieldDelegate & UIScrollViewDelegate。
//MARK:- TEXTFIELD METHODS
func textFieldShouldReturn(textField: UITextField) -> Bool {
if(username.returnKeyType == UIReturnKeyType.Default) {
password.becomeFirstResponder()
}
textField.resignFirstResponder()
return true
}
func textFieldDidBeginEditing(textField: UITextField) {
dispatch_async(dispatch_get_main_queue()) {
let scrollPoint:CGPoint = CGPointMake(0,textField.frame.origin.y/4)
self.scrollView!.setContentOffset(scrollPoint, animated: true);
}
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
dispatch_async(dispatch_get_main_queue()) {
UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true) })
}
return true
}
override func touchesBegan(touches: Set<UITouch>,
withEvent event: UIEvent?) {
self.view.endEditing(true)
}
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
self.scrollView.scrollEnabled = true
dispatch_async(dispatch_get_main_queue()) {
UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true)
})
}
}
您需要以编程方式添加具有特定帧大小的滚动视图。 您必须在 .h 文件中添加UIScrollViewDelegate
。 您必须启用滚动视图,您需要在 viewDidLoad() 中编写以下内容。
scrollview.scrollEnabled=YES;
scrollview.delegate=self;
scrollview.frame = CGRectMake(x,y,width,height);
//---set the content size of the scroll view---
[scrollview setContentSize:CGSizeMake(height,width)];
这样,您可以添加 x、y、宽度和高度值。
这是一个免费的键盘处理库Keyboard-Handling-in-iPhone-Applications 。 你只需要写一行代码:
[AutoScroller addAutoScrollTo:scrollView];
处理表单中的键盘真是太棒了
我认为如果您使用 Swift,最好的方法是使用面向协议的编程。
首先,您必须创建一个KeyboardCapable
协议,该协议赋予任何符合它的 UIViewController 注册和注销键盘观察者的能力:
import Foundation
import UIKit
protocol KeyboardCapable: KeyboardAnimatable {
func keyboardWillShow(notification: NSNotification)
func keyboardWillHide(notification: NSNotification)
}
extension KeyboardCapable where Self: UIViewController {
func registerKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil)
}
func unregisterKeyboardNotifications() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
}
您已经注意到上面这段代码中无关紧要的KeyboardAnimatable
关键字。 它只是我们需要创建的下一个协议的名称:
import Foundation
import UIKit
protocol KeyboardAnimatable {
}
extension KeyboardAnimatable where Self: UIViewController {
func performKeyboardShowFullViewAnimation(withKeyboardHeight height: CGFloat, andDuration duration: NSTimeInterval) {
UIView.animateWithDuration(duration, animations: { () -> Void in
self.view.frame = CGRectMake(view.frame.origin.x, -height, view.bounds.width, view.bounds.height)
}, completion: nil)
}
func performKeyboardHideFullViewAnimation(withDuration duration: NSTimeInterval) {
UIView.animateWithDuration(duration, animations: { () -> Void in
self.view.frame = CGRectMake(view.frame.origin.x, 0.0, view.bounds.width, view.bounds.height)
}, completion: nil)
}
}
这个KeyboardAnimatable
协议,为所有符合它的 UIViewController 提供了两个方法,分别将动画整个视图向上和向下。
好的,如果KeyboardCapable
符合KeyboardAnimatable
,那么所有符合KeyboardCapable
的 UIViewController 也符合KeyboardAnimatable
。 这很酷。
让我们看看符合KeyboardCapable
的UIViewController
,并对键盘事件做出反应:
import Foundation
import UIKit
class TransferConfirmViewController: UIViewController, KeyboardCapable {
//MARK: - LIFE CYCLE
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
registerKeyboardNotifications()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
unregisterKeyboardNotifications()
}
//MARK: - NOTIFICATIONS
//MARK: Keyboard
func keyboardWillShow(notification: NSNotification) {
let keyboardHeight = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
let animationDuration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
performKeyboardShowFullViewAnimation(withKeyboardHeight: keyboardHeight, andDuration: animationDuration)
}
func keyboardWillHide(notification: NSNotification) {
let animationDuration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
performKeyboardHideFullViewAnimation(withDuration: animationDuration)
}
}
现在您的UIViewController
将响应键盘事件并因此产生动画。
注意:如果您想要自定义动画而不是推或拉视图,则必须在KeyboardAnimatable
协议上定义自定义方法或在KeyboardCapable
函数上执行它们。 由你决定。
只需将此添加到您的 pod 文件 -> pod 'IQKeyboardManager'
就是这样,处理所有的键盘、滚动视图和一切!
您不需要编写任何代码,找不到更好的解决方案!
它有一个扩展,如果有多个文本字段,它可以处理文本字段显示、屏幕移动、下一个和上一个箭头。
它还有一个自定义的完成按钮,可以删除。
这是我使用自动布局的版本:
这个想法只是将包含文本字段/文本视图的视图嵌入到 UIScrollView 中,从底部设置一个约束到它的超级视图,制作一个插座并使用通知根据键盘高度更新它的常量。 这是基于此处的 Apple 示例,以及此处的关于使用 AutoLayout 的 UIScrollView 的 Apple 技术说明。
1) 将 View V 嵌入 UIScrollView S :如果您已经设置了常量和子视图,您可以将视图和子视图复制/粘贴到 ViewController 的视图中,然后使用 Editor -> embed 菜单将其嵌入,最后删除复制的视图.)
2)设置以下约束:
S 落后于 superview : 0
V top space to superview : 0
V 引导空间到超级视图:0
V 等于 S 的宽度
最新的底部 V 子视图到超级视图:20
3) 创建一个从最新约束到视图控制器的出口
4)使用以下代码:
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomSpaceToContentView;
// ...
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// ...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Handle keyboard
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
self.bottomSpaceToContentView.constant = kBottomMargin + kbSize.height;
[self.view layoutIfNeeded];
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
self.bottomSpaceToContentView.constant = kBottomMargin;
[self.view layoutIfNeeded];
}
和tadaaaaa,它有效!
这很简单,只需将以下代码放入您的类中并根据需要进行自定义。
-(void)textFieldDidBeginEditing:(UITextField *)textField {
//Show Keyboard
self.view.frame = CGRectMake(self.view.frame.origin.x,
self.view.frame.origin.y-50,
self.view.frame.size.width,
self.view.frame.size.height);
}
-(void)textFieldDidEndEditing:(UITextField *)textField {
// Hide keyboard
self.view.frame = CGRectMake(self.view.frame.origin.x,
self.view.frame.origin.y+50,
self.view.frame.size.width,
self.view.frame.size.height);
}
一个更优雅的解决方案是使用UIView
子类(尽管这并不总是合适的)并在父级的帧更改时重新计算所有子视图(并且要聪明:只有在新的帧大小发生更改时才重新计算它们,即使用CGRectEqualToRect
在您覆盖setFrame
和调用[super setFrame:frame_]
之前比较新框架)。 唯一需要注意的是,您打算使用的UIViewController
可能应该监听键盘事件(或者,您可以在UIView
本身中执行此操作,以便于封装)。 但只有UIKeyboardWillShowNotification
和UIKeyboardWillHideNotification
。 这只是为了让它看起来很流畅(如果你等待 CG 调用它,你会得到片刻的波涛汹涌)。
无论如何,这具有构建一个做正确事情的UIView
子类的优势。
天真的实现将是覆盖drawRect:
(不要),更好的方法是只使用layoutSubviews
(然后在UIViewController
中,或者你可以调用 [view setNeedsLayout
] 在为任一显示调用的 SINGLE 方法中或隐藏)。
该解决方案无需对键盘偏移进行硬编码(如果它们未拆分,则会更改等),并且还意味着您的视图可能是许多其他视图的子视图,并且仍然可以正确响应。
除非没有其他解决方案,否则不要对类似的东西进行硬编码。 如果您做对了,操作系统会为您提供足够的信息,您只需要智能地重绘(基于您的新frame
大小)。 do things.这更清洁,也是你做的事情。 (不过,可能有更好的方法。)
干杯。
您也可以使用文本字段委托方法。 检查下面的代码。 将文本字段放在滚动视图上时,它对我有用。
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if(textField == answer)
{
CGPoint cPoint = textField.frame.origin;
[scrollView setContentOffset:CGPointMake(0, cPoint.y - 100) animated:YES];
}
}
注意:您必须根据您的视图更改 cPoint.y - 100 值。
这是我创建的 UITextfield (和其他类似字段)类别,它将使文本字段避免使用键盘,您应该能够将其按原样放在视图控制器中并且它应该可以工作。 它将整个屏幕向上移动,因此当前文本字段位于带有动画的键盘上方
#import "UIView+avoidKeyboard.h"
#import "AppDelegate.h"
@implementation UIView (avoidKeyboard)
- (void) becomeFirstResponder {
if(self.isFirstResponder)
return;
[super becomeFirstResponder];
if ([self isKindOfClass:[UISearchBar class]] ||
[self isKindOfClass:[UITextField class]] ||
[self isKindOfClass:[UITextView class]])
{
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
CGRect screenBounds = appDelegate.window.frame;
CGFloat keyboardHeight;
CGFloat keyboardY;
CGFloat viewsLowestY;
CGPoint origin = [self.superview convertPoint:self.frame.origin toView:appDelegate.window]; //get this views origin in terms of the main screens bounds
if(UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])){ //the window.frame doesnt take its orientation into account so if its sideways we must use the x value of the origin instead of the y
keyboardHeight = 216;
keyboardY = screenBounds.size.height - keyboardHeight; //find the keyboards y coord relative to how much the main window has moved up
viewsLowestY = origin.y + self.frame.size.height; //find the lowest point of this view
}
else {
keyboardHeight = 162;
keyboardY = screenBounds.size.width - keyboardHeight;
viewsLowestY = origin.x + self.frame.size.height;
}
CGFloat difference = viewsLowestY - keyboardY + 20; //find if this view overlaps with the keyboard with some padding
if (difference > 0){ //move screen up if there is an overlap
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
CGRect frame = appDelegate.window.frame;
if(UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])){
frame.origin.y -= difference;
}
else {
frame.origin.x -= difference;
}
appDelegate.window.frame = frame;
}
completion:nil];
}
}
}
//look at appDelegate to see when the keyboard is hidden
@end
在您的 appDelegate 添加此功能
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardHides:) name:UIKeyboardWillHideNotification object:nil]; //add in didFinishLaunchingWithOptions
...
- (void)keyboardHides:(NSNotification *)notification
{
[UIView animateWithDuration:0.3 animations:^{
[window setFrame: CGRectMake(0, 0, window.frame.size.width, window.frame.size.height)];
} completion:nil];
}
有很多答案可以说明这种方法。 我采取了相同的方法,但实施并不好。
这是基本的想法。 我对keyboardWasShown 方法进行了修改。
{
// Obtain keyboard Info
NSDictionary* info = [notification userInfo];
CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
// Obtain ScrollView Info w.r.t. top View
CGRect scrollViewRect = [self.view convertRect:self.scrollView.frame fromView:nil];
// Depending upon your screen Ui, Scroll View's bottom edge might be at some offset from screen's bottom
// Calculate the exact offset
int scrollViewBottomOffset = self.view.frame.size.height - (scrollViewRect.origin.y + scrollViewRect.size.height);
int heightToBeAdjusted = keyboardRect.size.height - scrollViewBottomOffset;
// We may also need to consider the Insets if already present with ScrollView. Let's keep it simple for now
// But we should store these, so that we can restore the Insets when Keyboard is gone
// origInsets = self.scrollView.contentInset;
// Set the new Insets for ScrollView
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, heightToBeAdjusted, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// Visible frame (not overlapped by Keyboard)
CGRect visibleFrame = self.view.frame;
visibleFrame.size.height -= keyboardRect.size.height;
// Get the Rect for Textfield w.r.t self.view
CGRect activeFieldFrame = self.activeField.frame;
activeFieldFrame = [self.view convertRect:activeFieldFrame fromView:self.scrollView];
// Check if the TextField is Visible or not
if (!CGRectContainsRect(visibleFrame, activeFieldFrame) ) {
// Scroll to make it visible but for scrolling use the activeField frame w.r.t. to scroll View
[self.scrollView scrollRectToVisible:self.activeField.frame animated:YES];
}
}
并添加这个方法来初始化activeField
- (IBAction)textFieldDidBeginEditing:(UITextField *)sender
{
self.activeField = sender;
}
https://github.com/michaeltyson/TPKeyboardAvoiding下载此文件并添加自定义类,因为它会在您的表格视图中管理所有事情,您不需要做任何事情。 它有很多选项,您也可以查看其他选项,这就是您避免使用键盘所需要的全部
这是独立于设备的偏移计算。 获取键盘和文本字段之间的重叠高度:
func keyboardShown(notification: NSNotification) {
let info = notification.userInfo!
let value: AnyObject = info[UIKeyboardFrameEndUserInfoKey]!
let rawFrame = value.CGRectValue
let keyboardFrame = view.convertRect(rawFrame, fromView: nil)
let screenHeight = UIScreen.mainScreen().bounds.size.height;
let Ylimit = screenHeight - keyboardFrame.size.height
let textboxOriginInSuperview:CGPoint = self.view.convertPoint(CGPointZero, fromCoordinateSpace: lastTextField!)
self.keyboardHeight = (textboxOriginInSuperview.y+self.lastTextField!.frame.size.height) - Ylimit
if(self.keyboardHeight>0){
self.animateViewMoving(true, moveValue: keyboardHeight!)
}else{
keyboardHeight=0
}
}
keyBoardHeight 是偏移量。
一种更简单但更通用的方法,就像苹果所做的那样,将键盘的高度考虑在内,这在我们在键盘顶部使用自定义工具栏时非常有用。 尽管苹果在这里的做法几乎没有问题。
这是我的方法(稍微修改苹果的方式) -
// UIKeyboardDidShowNotification
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
// UIKeyboardWillHideNotification
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
请按照以下步骤操作。
1) 在 .h 文件中声明以下变量。
{
CGFloat animatedDistance;
}
2) 在 .m 文件中声明以下常量。
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 162;
3)使用 UITextField 委托向上/向下移动键盘。
-(void) textFieldDidBeginEditing:(UITextField *)textField
{
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
CGRect textFieldRect = [self.view.window convertRect:textField.bounds fromView:textField];
CGRect viewRect = [self.view.window convertRect:self.view.bounds fromView:self.view];
CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator =
midline - viewRect.origin.y
- MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator =
(MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION)
* viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
if (heightFraction < 0.0)
{
heightFraction = 0.0;
}
else if (heightFraction > 1.0)
{
heightFraction = 1.0;
}
UIInterfaceOrientation orientation =
[[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait)
{
animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}
else
{
animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}
CGRect viewFrame = self.view.frame;
viewFrame.origin.y -= animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
}
-(void) textFieldDidEndEditing:(UITextField *)textField
{
if(UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone)
{
CGRect viewFrame = self.view.frame;
viewFrame.origin.y += animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
}
我最近在开发消息传递应用程序时发现自己处于类似的情况。 我创建了一个自定义 UIView,它贴在键盘顶部并自动完成您需要的大部分操作
(来源: thegameengine.org )
这个项目背后的想法是创建类似于 iMessage 组合视图 AKA 的功能:
为了调整/重新配置您的 UIScrollView,您需要使用以下可选委托方法:
- (void)messageComposerFrameDidChange:(CGRect)frame withAnimationDuration:(float)duration;
每当帧发生变化(调整大小、重新定位、旋转)时都会调用它,并且还会提供动画持续时间。 您可以根据需要使用此信息来调整 UIScrollView 的框架和内容插图的大小。
我发现这是最好的解决方案,请遵循以下代码:
将以下内容附加到您的Vertical Space - Bottom Layout Guide - TextField
约束。
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewBottomConst;
其次为键盘通知添加观察者。
- (void)observeKeyboard {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
将此添加到您的viewDidLoad
[self observeKeyboard];
最后是处理键盘变化的方法。
- (void)keyboardWillShow:(NSNotification *)notification {
//THIS WILL MAKE SURE KEYBOARD DOESNT JUMP WHEN OPENING QUICKTYPE/EMOJI OR OTHER KEYBOARDS.
kbHeight = 0;
height = 0;
self.textViewBottomConst.constant = height;
self.btnViewBottomConst.constant = height;
NSDictionary *info = [notification userInfo];
NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect keyboardFrame = [kbFrame CGRectValue];
CGRect finalKeyboardFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
int kbHeight = finalKeyboardFrame.size.height;
int height = kbHeight + self.textViewBottomConst.constant;
self.textViewBottomConst.constant = height;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
self.textViewBottomConst.constant = 10;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
我把所有东西都包在一个班级里。 只需在加载视图控制器时调用这些代码行:
- (void)viewDidLoad {
[super viewDidLoad];
KeyboardInsetScrollView *injectView = [[KeyboardInsetScrollView alloc] init];
[injectView injectToView:self.view withRootView:self.view];
}
这是示例项目的链接:
https://github.com/caohuuloc/KeyboardInsetScrollView
在视图中设置滚动视图
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGPoint point;
if(textField == txtEmail){
// -90 is for my you can change as per your postion
point = CGPointMake(0, textField.frame.origin.y - 90);
}
else if (textField == txtContact){
point = CGPointMake(0, textField.frame.origin.y - 90);
}
[scrollV setContentOffset:point animated:YES];
}
TextField
struct ContentView: View {
@ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 3)
@State private var name = Array<String>.init(repeating: "", count: 3)
var body: some View {
VStack {
Group {
Text("Some filler text").font(.largeTitle)
Text("Some filler text").font(.largeTitle)
}
TextField("text #1", text: $name[0], onEditingChanged: { if $0 { self.kGuardian.showField = 0 } })
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[0]))
TextField("text #2", text: $name[1], onEditingChanged: { if $0 { self.kGuardian.showField = 1 } })
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[1]))
TextField("text #3", text: $name[2], onEditingChanged: { if $0 { self.kGuardian.showField = 2 } })
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[2]))
}.offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.25))
}
}
TextField
s 如果其中任何一个出现键盘,这会将所有textField 向上移动。 但仅在需要时。 如果键盘不隐藏文本字段,它们将不会移动。
struct ContentView: View {
@ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 1)
@State private var name = Array<String>.init(repeating: "", count: 3)
var body: some View {
VStack {
Group {
Text("Some filler text").font(.largeTitle)
Text("Some filler text").font(.largeTitle)
}
TextField("enter text #1", text: $name[0])
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("enter text #2", text: $name[1])
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("enter text #3", text: $name[2])
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[0]))
}.offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.25))
}
}
这两个示例都使用相同的通用代码: GeometryGetter和KeyboardGuardian Inspired by @kontiki
这是一个吸收其父视图的大小和位置的视图。 在这里封装描述为了实现这一点,它在 .background 修饰符内部被调用。 这是一个非常强大的修饰符,不仅仅是装饰视图背景的一种方式。 将视图传递给 .background(MyView()) 时,MyView 将修改后的视图作为父视图。 使用 GeometryReader 可以让视图知道父对象的几何形状。
例如: Text("hello").background(GeometryGetter(rect: $bounds))
将填充变量边界,使用文本视图的大小和位置,并使用全局坐标空间。
struct GeometryGetter: View {
@Binding var rect: CGRect
var body: some View {
GeometryReader { geometry in
Group { () -> AnyView in
DispatchQueue.main.async {
self.rect = geometry.frame(in: .global)
}
return AnyView(Color.clear)
}
}
}
}
请注意, DispatchQueue.main.async
是为了避免在呈现视图时修改视图状态的可能性。
KeyboardGuardian 的目的是跟踪键盘显示/隐藏事件并计算视图需要移动多少空间。
请注意,当用户从一个字段切换到另一个字段时,它会刷新幻灯片*
import SwiftUI
import Combine
final class KeyboardGuardian: ObservableObject {
public var rects: Array<CGRect>
public var keyboardRect: CGRect = CGRect()
// keyboardWillShow notification may be posted repeatedly,
// this flag makes sure we only act once per keyboard appearance
public var keyboardIsHidden = true
@Published var slide: CGFloat = 0
var showField: Int = 0 {
didSet {
updateSlide()
}
}
init(textFieldCount: Int) {
self.rects = Array<CGRect>(repeating: CGRect(), count: textFieldCount)
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func keyBoardWillShow(notification: Notification) {
if keyboardIsHidden {
keyboardIsHidden = false
if let rect = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect {
keyboardRect = rect
updateSlide()
}
}
}
@objc func keyBoardDidHide(notification: Notification) {
keyboardIsHidden = true
updateSlide()
}
func updateSlide() {
if keyboardIsHidden {
slide = 0
} else {
let tfRect = self.rects[self.showField]
let diff = keyboardRect.minY - tfRect.maxY
if diff > 0 {
slide += diff
} else {
slide += min(diff, 0)
}
}
}
}
这里有很多答案,但这有效并且比大多数答案短得多:
- (void)textFieldDidBeginEditing:(UITextField *)sender
{
UIScrollView *scrollView = (UIScrollView *)self.view; // assuming this method is pasted into the UIScrollView's controller
const double dontHardcodeTheKeyboardHeight = 162;
double textY = [sender convertPoint:CGPointMake(0, 0) toView:scrollView].y;
if (textY - scrollView.contentOffset.y + sender.frame.size.height > self.view.frame.size.height - dontHardcodeTheKeyboardHeight)
[scrollView setContentOffset:CGPointMake(0.0, textY - 10) animated:YES];
}
刚刚找到这个类:
https://github.com/OliverLetterer/SLScrollViewKeyboardSupport
到目前为止,它在 iPhone 上运行良好,包括动画和正确的偏移。
要使用它,只需添加到viewDidLoad
:
self.support = [[SLScrollViewKeyboardSupport alloc] initWithScrollView:self.scrollView];
我们应该计算隐藏的精确交叉区域(键盘的框架和文本框的框架),然后我们应该改变视图的框架。
这里我给出一个完整的例子。
声明 3 个变量
#define 填充 10
@interface PKViewController () @property (nonatomic, assign) CGRect originalViewFrame; //original view's frame @property (nonatomic, strong) UITextField *activeTextField; // current text field @property (nonatomic, assign) CGRect keyBoardRect; // covered area by keaboard @end
存储原始框架
- (void)viewDidLoad {
[super viewDidLoad];
_originalViewFrame = self.view.frame;
}
将您的视图控制器添加为键盘通知的观察者
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
移除观察者
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
存储键盘出现时覆盖的区域,消失时设置为CGRectZero
- (void)keyboardWasShown:(NSNotification *)notification{
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
_keyBoardRect = CGRectMake(0, _originalViewFrame.size.height - keyboardSize.height, keyboardSize.width, keyboardSize.height);
[self moveTextFieldUP];
}
- (void) keyboardWillHide:(NSNotification *)notification{
_keyBoardRect = CGRectZero;
[self setDefaultFrame];
}
存储活动文本字段
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
_activeTextField = textField;
//When keyboard is already present but the textfield is hidden. Case:When return key of keyboard makes the next textfield as first responder
if (!CGRectIsEmpty(_keyBoardRect)) {
[self moveTextFieldUP];
}
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
现在我们应该改变视图的框架
- (void)moveTextFieldUP{
CGRect virtualTextFieldRect = CGRectMake(0, self.view.frame.origin.y, _activeTextField.frame.size.width, _activeTextField.frame.origin.y+_activeTextField.frame.size.height);
if (CGRectIntersectsRect(_keyBoardRect, virtualTextFieldRect)) {
CGRect intersectRect = CGRectIntersection(_keyBoardRect, virtualTextFieldRect);
CGFloat newY = _originalViewFrame.origin.y - intersectRect.size.height;
CGFloat newHeight = _originalViewFrame.size.height + intersectRect.size.height;
CGRect newFrame = CGRectMake(0, newY-PADDING, _originalViewFrame.size.width, newHeight+PADDING);
[UIView animateWithDuration:0.3 animations:^{
[self.view setFrame:newFrame];
}];
NSLog(@"Intersect");
}
}
- (void)setDefaultFrame {
[UIView animateWithDuration:0.3 animations:^{
[self.view setFrame:_originalViewFrame];
}];
}
-(BOOL) textFieldShouldBeginEditing:(UITextField *)textField {
[self slideUp];
return YES;
}
-(BOOL) textFieldShouldEndEditing:(UITextField *)textField {
[self slideDown];
return YES;
}
#pragma mark - Slide Up and Down animation
- (void) slideUp {
[UIView beginAnimations:nil context:nil];
layoutView.frame = CGRectMake(0.0, -70.0, layoutView.frame.size.width, layoutView.frame.size.height);
[UIView commitAnimations];
}
- (void) slideDown {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelay: 0.01];
layoutView.frame = CGRectMake(0.0, 0.0, layoutView.frame.size.width, layoutView.frame.size.height);
[UIView commitAnimations];
}
简单的解决方案和最新的动画 api。 将 origin.y 更改为 215,您可以将其自定义为适合您的任何值。
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if (self.view.frame.origin.y >= 0) {
[UIView animateWithDuration:0.5 animations:^{
self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y-215, self.view.frame.size.width, self.view.frame.size.height);
}];
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (self.view.frame.origin.y < 0) {
[UIView animateWithDuration:0.5 animations:^{
self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y+215, self.view.frame.size.width, self.view.frame.size.height);
}];
}
}
请在文本字段委托方法中添加这些行以在 iPad 中向上滚动。
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeTextfield = textField;
CGPoint pt;
CGRect rc = [textField bounds];
rc = [textField convertRect:rc toView:scrlView];
pt = rc.origin;
pt.x = 0;
pt.y -= 100;
[scrlView setContentOffset:pt animated:YES];
scrlView.contentSize = CGSizeMake(scrlView.frame.size.width, button.frame.origin.y+button.frame.size.height + 8 + 370);
}
如果该文本字段位于表格的单元格中(即使 table.scrollable = NO),它可以轻松自动地完成。
非常轻量级的解决方案可能是使用KeyboardAnimator 。
项目获得了示例实现,文档仍在进行中...
适当的用法 ::它对 UITextField 和 UITextView 有一个特定的实现
限制::它完全基于objective-c,很快就会有swift版本。
这将完美地工作。滚动视图通过文本字段位置自动调整。我相信你会感觉很好
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.25;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 162;
@interface LoginVC ()
{
CGFloat animatedDistance;
CGRect viewFrameKey;
}
//In ViewDidLoad
viewFrameKey=self.view.frame;
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGRect textFieldRect =
[self.view.window convertRect:textField.bounds fromView:textField];
CGRect viewRect =
[self.view.window convertRect:self.view.bounds fromView:self.view];
CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator =
midline - viewRect.origin.y
- MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator =
(MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION)
* viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
if (heightFraction < 0.0)
{
heightFraction = 0.0;
}
else if (heightFraction > 1.0)
{
heightFraction = 1.0;
}
UIInterfaceOrientation orientation =
[[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait ||
orientation == UIInterfaceOrientationPortraitUpsideDown)
{
animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}
else
{
animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}
CGRect viewFrame = self.view.frame;
viewFrame.origin.y -= animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrameKey];
[UIView commitAnimations];
}
我想扩展@sumanthkodi 的答案。
正如一些人所说,他的方法在较新的实现中不起作用,因为当您使用时 UIView 无法移动constraints 。
我将代码编辑如下(并移植到 Swift 2.0),希望它对某些人有所帮助:
1) 参考您想要向上移动的视图的垂直约束:
@IBOutlet var viewConstraint: NSLayoutConstraint!
确保在情节提要中使用约束引用此 var。
2)添加委托并实现监听器。 这是与以前相同的实现:
class YourViewController: UIViewController, UITextFieldDelegate {
...
func textFieldDidBeginEditing(textField: UITextField) {
animateTextField(textField, up: true)
}
func textFieldDidEndEditing(textField: UITextField) {
animateTextField(textField, up: false)
}
...
}
3) 将动画方法animateTextField
添加到YourViewController
类。 根据需要设置临时约束值。
func animateTextField(textfield: UITextField, up: Bool) {
let originalConstraint = 50
let temporaryConstraint = 0
let movementDuration = 0.3
let constraint = CGFloat(up ? temporaryConstraint : originalConstraint)
containerViewConstraint.constant = constraint
UIView.animateWithDuration(movementDuration) {
self.view.layoutIfNeeded()
}
}
这可以通过以下代码行使用约束简单地实现
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
[self adjustTextViewByKeyboardState:YES keyboardInfo:[notification userInfo]];
}
- (void)keyboardWillHide:(NSNotification *)notification {
[self adjustTextViewByKeyboardState:NO keyboardInfo:[notification userInfo]];
}
- (void)viewDidDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super viewDidDisappear:animated];
}
- (void)adjustTextViewByKeyboardState:(BOOL)showKeyboard keyboardInfo:(NSDictionary *)info {
CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat height = keyboardFrame.size.height;
self.constraintToAdjust.constant = height; UIViewAnimationCurve animationCurve = [info[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
UIViewAnimationOptions animationOptions = UIViewAnimationOptionBeginFromCurrentState;
if (animationCurve == UIViewAnimationCurveEaseIn) {
animationOptions |= UIViewAnimationOptionCurveEaseIn;
}
else if (animationCurve == UIViewAnimationCurveEaseInOut) {
animationOptions |= UIViewAnimationOptionCurveEaseInOut;
}
else if (animationCurve == UIViewAnimationCurveEaseOut) {
animationOptions |= UIViewAnimationOptionCurveEaseOut;
}
else if (animationCurve == UIViewAnimationCurveLinear) {
animationOptions |= UIViewAnimationOptionCurveLinear;
}
[UIView animateWithDuration:[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue] delay:0 options:animationOptions animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
虽然这个线程已经得到了足够的答案,但我想建议一种更简单但更通用的方法,就像苹果所做的那样,考虑到键盘的高度,这在我们使用键盘顶部的自定义工具栏时非常有用。 尽管苹果在这里的做法几乎没有问题。
这是我的方法(稍微修改苹果的方式) -
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
在您的视图控制器上:
@IBOutlet weak var usernameTextfield: UITextField!
@IBOutlet weak var passwordTextfield: UITextField!
@IBOutlet weak var loginScrollView: UIScrollView!
override func viewWillAppear(animated: Bool) {
loginScrollView.scrollEnabled = false
}
添加文本字段代表。
//MARK:- TEXTFIELD METHODS
func textFieldShouldReturn(textField: UITextField) -> Bool
{
if (usernameTextfield.resignFirstResponder())
{
passwordTextfield.becomeFirstResponder()
}
textField.resignFirstResponder();
loginScrollView!.setContentOffset(CGPoint.zero, animated: true);
loginScrollView.scrollEnabled = false
return true
}
func textFieldDidBeginEditing(textField: UITextField)
{
loginScrollView.scrollEnabled = true
if (textField.tag == 1 && (device == "iPhone" || device == "iPhone Simulator" || device == "iPod touch"))
{
let scrollPoint:CGPoint = CGPointMake(0, passwordTextfield.frame.origin.y/6.4);
loginScrollView!.setContentOffset(scrollPoint, animated: true);
}
else if (textField.tag == 2 && (device == "iPhone" || device == "iPhone Simulator" || device == "iPod touch"))
{
let scrollPoint:CGPoint = CGPointMake(0, passwordTextfield.frame.origin.y/6.0);
loginScrollView!.setContentOffset(scrollPoint, animated: true);
}
}
func textFieldDidEndEditing(textField: UITextField)
{
loginScrollView!.setContentOffset(CGPointZero,animated: true);
}
这段代码将根据键盘高度和文本字段的深度计算需要向上移动多少。 请记住在您的标题中添加委托并继承 UITextFieldDelegate。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[_tbxUsername resignFirstResponder];
[_tbxPassword resignFirstResponder];
}
- (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
{
int animatedDistance;
int moveUpValue = textField.frame.origin.y+ textField.frame.size.height;
UIInterfaceOrientation orientation =
[[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait ||
orientation == UIInterfaceOrientationPortraitUpsideDown)
{
animatedDistance = 236-(460-moveUpValue-5);
}
else
{
animatedDistance = 182-(320-moveUpValue-5);
}
if(animatedDistance>0)
{
const int movementDistance = animatedDistance;
const float movementDuration = 0.3f;
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: nil context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
}
在 ViewDidLoad 处添加的委托
_tbxUsername.delegate = self;
_tbxPassword.delegate = self;
试试 IQKeyboard 库。
这将自动向上移动文本字段。
Swift 3.0 版本的苹果键盘管理代码在这里: 下面代码中使用的FloatingTF是 iOS 中基于 Material design 的文本字段。
import UIKit
class SignupViewController: UIViewController, UITextFieldDelegate {
//MARK: - IBOutlet:
@IBOutlet weak var emailTF: FloatingTF!
@IBOutlet weak var passwordTF: FloatingTF!
@IBOutlet weak var dobTF: FloatingTF!
@IBOutlet weak var scrollView: UIScrollView!
//MARK: - Variable:
var activeTextField: UITextField!
//MARK: - ViewController Lifecycle:
override func viewDidLoad() {
super.viewDidLoad()
emailTF.delegate = self
passwordTF.delegate = self
dobTF.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
registerKeyboardNotifications()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
deRegisterKeyboardNotifications()
}
//MARK: - Keyboard notification observer Methods
fileprivate func registerKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(SignupViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(SignupViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
fileprivate func deRegisterKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidHide, object: self.view.window)
}
func keyboardWillShow(notification: NSNotification) {
let info: NSDictionary = notification.userInfo! as NSDictionary
let value: NSValue = info.value(forKey: UIKeyboardFrameBeginUserInfoKey) as! NSValue
let keyboardSize: CGSize = value.cgRectValue.size
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.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: CGRect = self.view.frame
aRect.size.height -= keyboardSize.height
let activeTextFieldRect: CGRect? = activeTextField?.frame
let activeTextFieldOrigin: CGPoint? = activeTextFieldRect?.origin
if (!aRect.contains(activeTextFieldOrigin!)) {
scrollView.scrollRectToVisible(activeTextFieldRect!, animated:true)
} }
func keyboardWillHide(notification: NSNotification) {
let contentInsets: UIEdgeInsets = .zero
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
}
//MARK: - UITextField Delegate Methods
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == emailTF {
passwordTF.becomeFirstResponder()
}
else if textField == passwordTF {
dobTF.becomeFirstResponder()
}
else {
self.view.endEditing(true)
}
return true
}
func textFieldDidBeginEditing(_ textField: UITextField) {
activeTextField = textField
scrollView.isScrollEnabled = true
}
func textFieldDidEndEditing(_ textField: UITextField) {
activeTextField = nil
scrollView.isScrollEnabled = false
}
}
下面是带有 TextFields 的 Scrollview 的简单解决方案,不需要任何约束或活动文本字段等...
override func viewWillAppear(_ animated: Bool){
super.viewWillAppear(animated)
registerForKeyboardNotifications();
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
deregisterFromKeyboardNotifications();
}
//MARK:- KEYBOARD DELEGATE METHODS
func registerForKeyboardNotifications(){
//Adding notifies on keyboard appearing
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func deregisterFromKeyboardNotifications(){
//Removing notifies on keyboard appearing
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWasShown(notification: NSNotification){
var info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
var contentInset:UIEdgeInsets = self.scrRegister.contentInset
contentInset.bottom = (keyboardSize?.height)!
scrRegister.contentInset = contentInset
}
func keyboardWillBeHidden(notification: NSNotification)
{
var contentInset:UIEdgeInsets = self.scrRegister.contentInset
contentInset.bottom = 0
scrRegister.contentInset = contentInset
}
使用 IQKeyboardManager,当键盘出现时 UITextField 和 UITextView 会自动滚动。 Git 链接: https ://github.com/hackiftekhar/IQKeyboardManager。
pod: pod 'IQKeyboardManager' #iOS8 及更高版本
吊舱“IQKeyboardManager”,“3.3.7”#iOS7
斯威夫特 5
在 viewDidLoad 或 viewDidAppear 添加addKeyboardObservers方法。
fileprivate func addKeyboardObservers(){
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
fileprivate func removeKeyboardObservers(){
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
}
@objc fileprivate func keyboardWillHide(_ notification: Notification){
if (window == nil) {return}
guard let duration = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double) else {return}
scrollView.contentInset.bottom = .zero
}
@objc fileprivate func keyboardWillShow(_ notification: Notification){
if (window == nil) {return}
if UIApplication.shared.applicationState != .active { return }
// keyboard height
guard let height = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height else {return}
// keyboard present animation duration
guard let duration = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double) else {return}
scrollView.contentInset.bottom = height
}
不要忘记在 deinit 上删除观察者或消失
self.removeKeyboardObservers()
请按照以下步骤操作,它可能会有所帮助。 放置一个视图,然后将您的文本字段放在该视图上,并在您的键盘启动时通过委托检测事件,当时立即为视图设置动画(您也可以为该视图分配一些位置),然后您的视图将向上到那个位置。做同样的事情来动画视图。
谢谢
这很简单:-
在 TextFieldDidBeginEditing 中:-
self.view.frame=CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y-150, self.view.frame.size.width, self.view.frame.size.height);
在 TextFieldShouldEndEditing 中:-
self.view.frame=CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y+150, self.view.frame.size.width, self.view.frame.size.height);
我迟到了一点。 您应该在 viewController 上添加 scrollView。
您必须执行以下 2 种方法。
TextField 委托方法。
- (void)textFieldDidBeginEditing:(UIView *)textField {
[self scrollViewForTextField:reEnterPINTextField];
}
然后在委托方法中调用下面的方法。
- (void)scrollViewForTextField:(UIView *)textField {
NSInteger keyboardHeight = KEYBOARD_HEIGHT;
if ([textField UITextField.class]) {
keyboardHeight += ((UITextField *)textField).keyboardControl.activeField.inputAccessoryView.frame.size.height;
}
CGRect screenFrame = [UIScreen mainScreen].bounds;
CGRect aRect = (CGRect){0, 0, screenFrame.size.width, screenFrame.size.height - ([UIApplication sharedApplication].statusBarHidden ? 0 : [UIApplication sharedApplication].statusBarFrame.size.height)};
aRect.size.height -= keyboardHeight;
CGPoint relativeOrigin = [UIView getOriginRelativeToScreenBounds:textField];
CGPoint bottomPointOfTextField = CGPointMake(relativeOrigin.x, relativeOrigin.y + textField.frame.size.height);
if (!CGRectContainsPoint(aRect, bottomPointOfTextField) ) {
CGPoint scrollPoint = CGPointMake(0.0, bottomPointOfTextField.y -aRect.size.height);
[contentSlidingView setContentOffset:scrollPoint animated:YES];
}
}
我已经根据自己的需要开发了一个框架来更好地解决这个问题,并现在将其公开。 它不仅适用于 UITextField 和 UITextView,它适用于任何采用 UITextInput 协议的自定义 UIView,如 UITextField 和 UITextView,并提供许多有用的功能。 你可以通过 Carthage、CocoaPods 或 Swift Package Manager 安装它。
ODScrollView 只是一个 UIScrollView,它可以根据键盘可见性自动垂直移动 UITextField 和 UITextView 等可编辑文本区域,以提供更好的用户体验。
可以为每个 UITextInput 单独应用调整边距,用于 .Top 和 .Bottom 调整方向设置。 默认为 20 CGFloat。
可以单独为每个 UITextInput 启用/禁用调整。 默认情况下为真。
调整方向 - .Top、.Center、.Bottom - 可以分别应用于每个 UITextInput。 .底部默认。 例子
1 -您需要做的第一件事是正确设置 ODScrollView 及其内容视图。 由于 ODScrollView 只是一个 UIScrollView,因此您可以以与 UIScrollView 相同的方式实现 ODScrollView。 您可以使用情节提要或以编程方式创建 ODScrollView。
如果您以编程方式创建 ODScrollView,则可以从步骤 4 继续。
在 Storyboard 中创建 UIScrollView 的建议方法
- If you are using Content Layout Guide and Frame Layout Guide:
1.1 - scrollView: Place UIScrollView anywhere you want to use.
1.2 - contentView: Place UIView inside scrollView.
1.3 - Set contentView's top, bottom, leading and trailing constraints to Content Layout Guide's constraints.
1.4 - Set contentView's width equal to Frame Layout Guide's width.
1.5 - Set contentView's height equal to Frame Layout Guide's height or set static height which is larger than scrollView's height.
1.6 - Build your UI inside contentView.
- If you are NOT using Content Layout Guide and Frame Layout Guide:
1.1 - scrollView: Place UIScrollView anywhere you want to use.
1.2 - contentView: Place UIView inside scrollView.
1.3 - Set contentView's top, bottom, leading and trailing constraints to 0.
1.4 - Set contentView's width equal to scrollView's width.
1.5 - Set contentView's height equal to scrollView's superview's height or set static height which is larger than scrollView's height.
1.6 - Build your UI inside contentView.
2 -在 Storyboard 的身份检查器中将滚动视图的类从 UIScrollView 更改为 ODScrollView。
3 -在 ViewController 上为 scrollView 和 contentView 创建 IBOutlets。
4 -在 ViewController 上的 ViewDidLoad() 中调用以下方法:
override func viewDidLoad() {
super.viewDidLoad()
//ODScrollView setup
scrollView.registerContentView(contentView)
scrollView.odScrollViewDelegate = self
}
5 -可选:您仍然可以使用 UIScrollView 的功能:
override func viewDidLoad() {
super.viewDidLoad()
//ODScrollView setup
scrollView.registerContentView(contentView)
scrollView.odScrollViewDelegate = self
// UIScrollView setup
scrollView.delegate = self // UIScrollView Delegate
scrollView.keyboardDismissMode = .onDrag // UIScrollView keyboardDismissMode. Default is .none.
UITextView_inside_contentView.delegate = self
}
6 -采用 ViewController 中的 ODScrollViewDelegate 并决定 ODScrollView 选项:
extension ViewController: ODScrollViewDelegate {
// MARK:- State Notifiers: are responsible for notifiying ViewController about what is going on while adjusting. You don't have to do anything if you don't need them.
// #Optional
// Notifies when the keyboard showed.
func keyboardDidShow(by scrollView: ODScrollView) {}
// #Optional
// Notifies before the UIScrollView adjustment.
func scrollAdjustmentWillBegin(by scrollView: ODScrollView) {}
// #Optional
// Notifies after the UIScrollView adjustment.
func scrollAdjustmentDidEnd(by scrollView: ODScrollView) {}
// #Optional
// Notifies when the keyboard hid.
func keyboardDidHide(by scrollView: ODScrollView) {}
// MARK:- Adjustment Settings
// #Optional
// Specifies the margin between UITextInput and ODScrollView's top or bottom constraint depending on AdjustmentDirection
func adjustmentMargin(for textInput: UITextInput, inside scrollView: ODScrollView) -> CGFloat {
if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
return 20
} else {
return 40
}
}
// #Optional
// Specifies that whether adjustment is enabled or not for each UITextInput seperately.
func adjustmentEnabled(for textInput: UITextInput, inside scrollView: ODScrollView) -> Bool {
if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
return true
} else {
return false
}
}
// Specifies adjustment direction for each UITextInput. It means that some of UITextInputs inside ODScrollView can be adjusted to the bottom, while others can be adjusted to center or top.
func adjustmentDirection(selected textInput: UITextInput, inside scrollView: ODScrollView) -> AdjustmentDirection {
if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
return .bottom
} else {
return .center
}
}
/**
- Always : ODScrollView always adjusts the UITextInput which is placed anywhere in the ODScrollView.
- IfNeeded : ODScrollView only adjusts the UITextInput if it overlaps with the shown keyboard.
*/
func adjustmentOption(for scrollView: ODScrollView) -> AdjustmentOption {
.Always
}
// MARK: - Hiding Keyboard Settings
/**
#Optional
Provides a view for tap gesture that hides keyboard.
By default, keyboard can be dismissed by keyboardDismissMode of UIScrollView.
keyboardDismissMode = .none
keyboardDismissMode = .onDrag
keyboardDismissMode = .interactive
Beside above settings:
- Returning UIView from this, lets you to hide the keyboard by tapping the UIView you provide, and also be able to use isResettingAdjustmentEnabled(for scrollView: ODScrollView) setting.
- If you return nil instead of UIView object, It means that hiding the keyboard by tapping is disabled.
*/
func hideKeyboardByTappingToView(for scrollView: ODScrollView) -> UIView? {
self.view
}
/**
#Optional
Resets the scroll view offset - which is adjusted before - to beginning its position after keyboard hid by tapping to the provided UIView via hideKeyboardByTappingToView.
## IMPORTANT:
This feature requires a UIView that is provided by hideKeyboardByTappingToView().
*/
func isResettingAdjustmentEnabled(for scrollView: ODScrollView) -> Bool {
true
}
}
7 -可选:在输入多行 UITextInput 时,当光标与键盘重叠时,您可以调整 ODScrollView。 trackTextInputCursor(for UITextInput) 必须由键入时触发的 UITextInput 函数调用。
/**
## IMPORTANT:
This feature is not going to work unless textView is subView of _ODScrollView
*/
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
_ODScrollView.trackTextInputCursor(for textView)
return true
}
你不需要这么多的代码来完成一个简单的任务。 有一个名为“IQKeyboardManager”的 CocoaPod 将为您完成任务:
pod 'IQKeyboardManager'
然后在从didFinishLaunchingWithOptions
返回之前在AppDelegate
中使用此代码:
IQKeyboardManager.shared().isEnabled = true
IQKeyboardManager.shared().shouldResignOnTouchOutside = true
IQKeyboardManager.shared().isEnableAutoToolbar = false
从Dune Buggy的回答中得到参考。 根据TextField
的Position向上滚动视图。 因为现有的答案是滚动屏幕的整个视图,所以我需要根据文本字段的框架滚动视图。
@objc func keyboardWillShow(notification: NSNotification) {
guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
// if keyboard size is not available for some reason, don't do anything
return
}
let keyboardFrame = keyboardSize
let maxheightScreen = self.view.frame
if (self.txtEmail.frame.origin.y + ((self.txtEmail.superview)!.frame.maxY) + keyboardSize.height) >= maxheightScreen.size.height{
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= (keyboardFrame.height - (self.txtEmail.frame.maxY + 120)) // Here i added 120 additional height for my additional view space
}
}
}
@objc func keyboardWillHide(notification: NSNotification) {
guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
// if keyboard size is not available for some reason, don't do anything
return
}
let keyboardFrame = keyboardSize
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y += (keyboardFrame.height - (self.txtEmail.frame.maxY + 120))
}
}
我在这里没有看到这种可能性,所以我添加了它,因为我尝试了答案中的方法,但几个小时后发现在 iOS6/7 的 XCode 5 中有一种更简单的方法:使用 NSLayoutConstraints。
请参阅: 自动布局约束 - 键盘
这是我的代码:
.m 文件:
// Called when the UIKeyboardWillShowNotification is sent.
- (void)keyboardWillBeShown:(NSNotification*)aNotification
{
NSLog(@"keyboardWillBeShown:");
[self.PhoneNumberLabelOutlet setHidden:TRUE];
CGFloat heightOfLabel = self.PhoneNumberLabelOutlet.frame.size.height;
for( NSLayoutConstraint* thisConstraint in self.topElementsVerticalDistanceFromTopLayoutConstraint ) {
thisConstraint.constant -= heightOfLabel;
}
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGFloat oldConstant = [self.SignInYConstraint constant];
self.SignInYConstraint.constant = oldConstant + kbSize.height;
[self.view setNeedsUpdateConstraints];
NSTimeInterval duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:duration animations:^{
[self.view layoutIfNeeded];
}];
}
.h 文件:
#import <UIKit/UIKit.h>
@interface SignInViewController : UIViewController {
UITextField* _activeField;
}
- (void)signInCallback:(NSObject*)object;
@property (weak, nonatomic) IBOutlet UILabel *PhoneNumberLabelOutlet;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *ActivityIndicatorOutlet;
@property (weak, nonatomic) IBOutlet UITextField *UserIDTextfieldOutlet;
@property (weak, nonatomic) IBOutlet UITextField *PasswordTextfieldOutlet;
@property (weak, nonatomic) IBOutlet UIButton *SignInButton;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *SignInYConstraint;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *topElementsVerticalDistanceFromTopLayoutConstraint;
@end
我发现@DK_ 是我开始使用的解决方案。 但是,假设 scrollView 覆盖了整个视图。 对我来说不是这样。 我只想要一个滚动视图,以防键盘覆盖我登录屏幕上的下部文本字段。 所以我的内容视图与我的滚动视图大小相同,比主视图小。
它也没有考虑风景,这是我开始遇到麻烦的地方。 在玩了几天之后,这是我的keyboardWasShown:
方法。
- (void)keyboardWasShown:(NSNotification*)aNotification
{
// A lot of the inspiration for this code came from http://stackoverflow.com/a/4837510/594602
CGFloat height = 0;
NSDictionary* info = [aNotification userInfo];
CGRect kbFrameRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect kbBoundsRect = [self.view convertRect:kbFrameRect fromView:nil]; // Convert frame from window to view coordinates.
CGRect scrollRect = scrollView.frame;
CGRect intersect = CGRectIntersection(kbBoundsRect, scrollRect);
if (!CGRectIsNull(intersect))
{
height = intersect.size.height;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
// Figure out what the view rectangle is for the scrollView
CGPoint contentOffset = scrollView.contentOffset;
CGRect visibleRect = CGRectOffset(scrollRect, contentOffset.x, contentOffset.y); // I'm not 100% sure if this is needed/right. My scrollView was always at the top in testing.
visibleRect.size.height -= height;
CGRect activeRect = activeField.frame;
if (!CGRectContainsRect(visibleRect, activeRect))
{
[self.scrollView scrollRectToVisible:activeField.frame animated:YES];
}
}
我在使用自动布局时也遇到了一些困难。 如果我没有正确完成布局,我不会得到预期的滚动。 让生活变得更轻松的一件事是将所有要滚动的项目放在一个视图中,并将其作为唯一项目放置在滚动视图中。 我将那个单一视图称为“内容视图”。
我认为关键部分是内容视图具有设定的宽度和高度。 这允许滚动视图准确地知道它必须处理多少内容。 这与通常的布局有点倒退。 通常情况下,视图会尽可能多地占据空间。 使用滚动视图的内容,您试图让视图尽可能地限制自己。 内容视图允许您停止这种情况。 所以我给我的高度为 248,并使用标准屏幕宽度 320 作为我的宽度。
最终对我有用的布局是:
Horizontal Space - View - Scroll View
(0)Vertical Space - View - Scroll View
(0)Horizontal Space - Scroll View - View
(0)Height - (248) - Scroll View
Vertical Space - View - Scroll View
(0)Vertical Space - Scroll View - View
(0)Horizontal Space - View - Scroll View
(0)Horizontal Space - Scroll View - View
(0)Height - (248) - View
Width - (320) - View
在 (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
if (textField.frame.origin.y > self.view.frame.size.height - 216)
{
if (screenHeight>500)
scrollView.contentSize = CGSizeMake(0.0, scrollView.contentSize.height + 100);
else
scrollView.contentSize = CGSizeMake(0.0, scrollView.contentSize.height + 216);
CGPoint scrollPoint = CGPointMake(0.0,(textField.frame.origin.y - (self.view.frame.size.height - 216 - textField.frame.size.height - 20)));
[scrollView setContentOffset:scrollPoint animated:YES];
}
[scrollView setScrollEnabled:YES];
辞职键盘时,您需要编写以下代码
scrollView.contentSize = CGSizeMake(0.0, 640);
CGPoint scrollPoint = CGPointMake(0.0,0.0);
[scrollView setContentOffset:scrollPoint animated:YES];
我使用 Swift 和自动布局(但不能评论之前的 Swift 答案); 这是我在没有滚动视图的情况下如何做到的:
我在 IB 中布局我的表单,并在字段之间使用垂直约束来分隔它们。 我从最顶层字段向容器视图添加一个垂直约束,并为其创建一个出口(下面代码中的 topSpaceForFormConstraint)。 所需要的只是更新这个约束,我在一个动画块中做了一个很好的软动作。 高度检查当然是可选的,在这种情况下,我只需要针对最小的屏幕尺寸进行检查。
这可以使用任何常用的 textFieldDidBeginEditing 或 keyboardWillShow 方法来调用。
func setFormHeight(top: CGFloat)
{
let height = UIScreen.mainScreen().bounds.size.height
// restore text input fields for iPhone 4/4s
if (height < 568) {
UIView.animateWithDuration(0.2, delay: 0.0, options: nil, animations: {
self.topSpaceForFormConstraint.constant = top
self.view.layoutIfNeeded()
}, completion: nil)
}
}
这对我有用:
func setupKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWasShown:"), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillBeHidden:"), name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWasShown(aNotification:NSNotification) {
let info = aNotification.userInfo
let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as NSValue
let kbSize = infoNSValue.CGRectValue().size
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.3)
var rect : CGRect = self.view.frame
rect.size.height -= kbSize.height
self.view.frame = rect
UIView.commitAnimations()
}
func keyboardWillBeHidden(aNotification:NSNotification) {
let info = aNotification.userInfo
let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as NSValue
let kbSize = infoNSValue.CGRectValue().size
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.3)
var rect : CGRect = self.view.frame
rect.size.height += kbSize.height
self.view.frame = rect
UIView.commitAnimations()
}
在 iOS 中,向上移动键盘并收回应用程序中的文本字段有点令人困惑,需要为此实现一些方法。 您还需要委托文本字段并处理它。 它的代码将在存在文本字段的每个类中重复。
我更喜欢使用这个 Github 控件。
在其中我们不需要做任何事情。 -只需将拖放控件拖放到您的项目并构建。 -- 它将为您的应用做所有事情。
谢谢
可能这会有用。
我在更改文本字段或编辑其内容时重置为默认主视图时遇到问题(例如电话文本字段并添加“-”符号,并且视图返回覆盖文本字段)我最终通过使用自动布局和更改约束常量克服了这个问题,而不是通知委托函数中的帧大小或位置,如下所示:
PS我没有使用滚动视图只是简单的向上移动视图,但它应该类似
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if !keyboardIsShown{
self.infoViewTopConstraint.constant -= keyboardSize.height
self.infoViewBottomConstraint.constant += keyboardSize.height
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
keyboardIsShown = true
}
}
func keyboardWillHide(notification: NSNotification) {
if keyboardIsShown {
self.infoViewTopConstraint.constant += keyboardSize.height
self.infoViewBottomConstraint.constant -= keyboardSize.height
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
keyboardIsShown = false
}
对于 Swift Developer,使用 Swift 3,这里是 repo https://github.com/jamesrochabrun/KeyboardWillShow
import UIKit
class ViewController: UIViewController {
//1 Create a view that will hold your TEXTFIELD
let textField: UITextField = {
let tf = UITextField()
tf.translatesAutoresizingMaskIntoConstraints = false
tf.layer.borderColor = UIColor.darkGray.cgColor
tf.layer.borderWidth = 3.0
return tf
}()
//2 global variable that will hold the bottom constraint on changes
var textfieldBottomAnchor: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
//3 add the view to your controller
view.addSubview(textField)
textField.heightAnchor.constraint(equalToConstant: 80).isActive = true
textField.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
textField.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
textfieldBottomAnchor = textField.bottomAnchor.constraint(equalTo: view.bottomAnchor)
textfieldBottomAnchor?.isActive = true
setUpKeyBoardObservers()
}
//4 Use NSnotificationCenter to monitor the keyboard updates
func setUpKeyBoardObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
//5 toggle the bottom layout global variable based on the keyboard's height
func handleKeyboardWillShow(notification: NSNotification) {
let keyboardFrame = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? CGRect
if let keyboardFrame = keyboardFrame {
textfieldBottomAnchor?.constant = -keyboardFrame.height
}
let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
if let keyboardDuration = keyboardDuration {
UIView.animate(withDuration: keyboardDuration, animations: {
self.view.layoutIfNeeded()
})
}
}
func handleKeyboardWillHide(notification: NSNotification) {
textfieldBottomAnchor?.constant = 0
let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
if let keyboardDuration = keyboardDuration {
UIView.animate(withDuration: keyboardDuration, animations: {
self.view.layoutIfNeeded()
})
}
}
//6 remove the observers
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
}
您可以使用这个简单的 Git 存储库: https ://github.com/hackiftekhar/IQKeyboardManager
这是一个自动管理所有字段移动的库。
根据他们的自述文件,集成非常简单:
无需您输入任何代码,也无需额外设置。 要使用 IQKeyboardManager,您只需将源文件添加到您的项目中
虽然,这是一个很好的控制,但在某些情况下它会导致冲突,比如在带有滚动视图的视图控制器中。 它有时会改变内容大小。 不过,你可以去做,并根据你的要求尝试它,也许你可以做我错过的事情。
这个问题已经有很多答案了,有的说用scroll view,有的说用第三个lib。
但对我来说,想法解决方案应该是带有静态单元格的UITableViewController
。
您将您的 UI 分成多个部分,并将它们一个一个地放入 tableViewCells 中,而不是您不再需要担心键盘,tableViewController 会自动为您管理它。
计算填充、边距、单元格高度可能有点困难,但如果你的数学没问题,这很简单。
我们可以为 Swift 4.1 用户提供代码
let keyBoardSize = 80.0
func keyboardWillShow() {
if view.frame.origin.y >= 0 {
viewMovedUp = true
}
else if view.frame.origin.y < 0 {
viewMovedUp = false
}
}
func keyboardWillHide() {
if view.frame.origin.y >= 0 {
viewMovedUp = true
}
else if view.frame.origin.y < 0 {
viewMovedUp = false
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if sender.isEqual(mailTf) {
//move the main view, so that the keyboard does not hide it.
if view.frame.origin.y >= 0 {
viewMovedUp = true
}
}
}
func setViewMovedUp(_ movedUp: Bool) {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.3)
// if you want to slide up the view
let rect: CGRect = view.frame
if movedUp {
rect.origin.y -= keyBoardSize
rect.size.height += keyBoardSize
}
else {
// revert back to the normal state.
rect.origin.y += keyBoardSize
rect.size.height -= keyBoardSize
}
view.frame = rect
UIView.commitAnimations()
}
func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector:#selector(self.keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(self.keyboardWillHide), name: .UIKeyboardWillHide, object: nil)
}
func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
加我的 5 美分 :)
我总是喜欢将 tableView 用于 inputTextField 或 scrollView。 结合通知,您可以轻松管理此类行为。 (注意,如果您在 tableView 中使用静态单元格,则此类行为将自动为您管理。)
// MARK: - Notifications
fileprivate func registerNotificaitions() {
NotificationCenter.default.addObserver(self, selector: #selector(AddRemoteControlViewController.keyboardWillAppear(_:)),
name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(AddRemoteControlViewController.keyboardWillDisappear),
name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
fileprivate func unregisterNotifications() {
NotificationCenter.default.removeObserver(self)
}
@objc fileprivate func keyboardWillAppear(_ notification: Notification) {
if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
view.layoutIfNeeded()
UIView.animate(withDuration: 0.3, animations: {
let heightInset = keyboardHeight - self.addDeviceButton.frame.height
self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: heightInset, right: 0)
self.view.layoutIfNeeded()
}, completion: nil)
}
}
@objc fileprivate func keyboardWillDisappear() {
view.layoutIfNeeded()
UIView.animate(withDuration: 0.3, animations: {
self.tableView.contentInset = UIEdgeInsets.zero
self.view.layoutIfNeeded()
}, completion: nil)
}
如果文本字段应该位于屏幕底部,那么最神奇的解决方案是在您的视图控制器上进行以下覆盖:
override var inputAccessoryView: UIView? {
return <yourTextField>
}
这是我的“仅UITextView
扩展”解决方案,基于 Paul Hudson @twostraws 的解决方案(向他和此处类似答案的所有作者致意)。
import UIKit
extension UITextView {
func adjustableForKeyboard() {
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}
@objc private func adjustForKeyboard(notification: Notification) {
guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
return
}
let keyboardScreenEndFrame = keyboardValue.cgRectValue
let keyboardViewEndFrame = convert(keyboardScreenEndFrame, from: window)
if notification.name == UIResponder.keyboardWillHideNotification {
contentInset = .zero
} else {
contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardViewEndFrame.height - safeAreaInsets.bottom, right: 0)
}
scrollIndicatorInsets = contentInset
scrollRangeToVisible(selectedRange)
}
}
用法:
override func viewDidLoad() {
super.viewDidLoad()
textView.adjustableForKeyboard()
}
如果您希望 UIView 正确移动并且您的活动文本字段应准确定位到用户需要,以便他/她可以看到他们输入的任何内容。
为此,您必须使用 Scrollview 。 这假设是您的 UIView 层次结构。 ContainerView -> ScrollView -> ContentView -> 你的视图。
如果您按照上面讨论的层次结构进行了 UIView 设计,现在在您的控制器类中,您需要在 viewwillappear 中添加通知观察者并在 viewwilldissappear 中删除观察者。
但是这种方法需要在 UIView 需要转换的每个控制器上添加。 我一直在使用 'TPKeyboardAvoiding' pod 。 无论您是 Scrollview 、 TableView 还是 CollectionView ,它都能可靠且轻松地处理每种可能情况下的 UIView 转换。 您只需要将课程传递给您的“滚动视图”。
像下面
如果您将 tableview 更改为“TPKeyboardAvoidingTableView”,则可以更改此类。 您可以找到完整的运行项目项目链接
我一直遵循这种稳健的方法进行开发。 希望这可以帮助!
您可以使用ViewModifier
的 ViewModifier。 它要简单得多。
import SwiftUI
import Combine
struct KeyboardAwareModifier: ViewModifier {
@State private var keyboardHeight: CGFloat = 0
private var keyboardHeightPublisher: AnyPublisher<CGFloat, Never> {
Publishers.Merge(
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue }
.map { $0.cgRectValue.height },
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map { _ in CGFloat(0) }
).eraseToAnyPublisher()
}
func body(content: Content) -> some View {
content
.padding(.bottom, keyboardHeight)
.onReceive(keyboardHeightPublisher) { self.keyboardHeight = $0 }
}
}
extension View {
func KeyboardAwarePadding() -> some View {
ModifiedContent(content: self, modifier: KeyboardAwareModifier())
}
}
在你看来
struct SomeView: View {
@State private var someText: String = ""
var body: some View {
VStack {
Spacer()
TextField("some text", text: $someText)
}.KeyboardAwarePadding()
}
}
KeyboardAwarePadding()
将自动为您的视图添加填充。 它更优雅。
如果您仍在为此苦苦挣扎-请阅读我的帖子
我今天想出了一个解决方案。 我已经阅读了很多关于这个问题的帖子和“教程”,但没有一个在每种情况下都有效(其中大多数是彼此的复制粘贴)。 即使是苹果官方提出的“解决方案”也不起作用,而且在横向模式下完全不起作用。 为苹果没有给开发者解决这样一个常见的基本问题的手段而感到羞耻。 非常不专业。 如此惊人的框架(Cocoa)和如此令人讨厌的被低估的问题。
现在,我的解决方案:将 UIScrollView 设为您的根视图,然后将所有内容放入其中。 然后从这个 KeyboardAwareController 类继承你的视图控制器(你可能想要重新定义 scrollView 和 keyboardPadding 方法):
// // KeyboardAwareController.h // Sociopathy // // 由管理员于 13.01.14 创建。 // 版权所有 (c) 2014 kuchumovn。 版权所有。 //
#import <UIKit/UIKit.h>
@interface KeyboardAwareController : UIViewController <UITextFieldDelegate>
@end
// // KeyboardAwareController.m // 社会病 // // 由管理员于 13.01.14 创建。 // 版权所有 (c) 2014 kuchumovn。 版权所有。 //
#import "KeyboardAwareController.h"
@interface KeyboardAwareController ()
@end
@implementation KeyboardAwareController
{
CGPoint scrollPositionBeforeKeyboardAdjustments;
__weak UIScrollView* scrollView;
UITextField* activeField;
}
- (id) initWithCoder: (NSCoder*) decoder
{
if (self = [super initWithCoder:decoder])
{
scrollPositionBeforeKeyboardAdjustments = CGPointZero;
}
return self;
}
- (void) viewDidLoad
{
[super viewDidLoad];
}
- (UIScrollView*) scrollView
{
return (UIScrollView*) self.view;
}
- (CGFloat) keyboardPadding
{
return 5;
}
- (void) registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void) deregisterFromKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
- (void) viewWillAppear: (BOOL) animated
{
[super viewWillAppear:animated];
[self registerForKeyboardNotifications];
}
- (void) viewWillDisappear: (BOOL) animated
{
[self deregisterFromKeyboardNotifications];
[super viewWillDisappear:animated];
}
- (void) keyboardWillShow: (NSNotification*) notification
{
//NSLog(@"keyboardWillShow");
// force the animation from keyboardWillBeHidden: to end
scrollView.contentOffset = scrollPositionBeforeKeyboardAdjustments;
scrollPositionBeforeKeyboardAdjustments = CGPointZero;
}
// warning: i have no idea why this thing works and what does every line of this code mean
// (but it works and there is no other solution on the internets whatsoever)
// P.S. Shame on Apple for missing such a basic functionality from SDK (and many other basic features we have to hack and mess around with for days and nights)
- (void) keyboardDidShow: (NSNotification*) notification
{
//NSLog(@"keyboardDidShow");
UIWindow* window = [[[UIApplication sharedApplication] windows]objectAtIndex:0];
UIView* mainSubviewOfWindow = window.rootViewController.view;
CGRect keyboardFrameIncorrect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [mainSubviewOfWindow convertRect:keyboardFrameIncorrect fromView:window];
CGSize keyboardSize = keyboardFrame.size;
CGRect visibleFrame = CGRectMake(0, 0, 0, 0);
visibleFrame.origin = self.scrollView.contentOffset;
visibleFrame.size = self.scrollView.bounds.size;
CGFloat paddedKeyboardHeight = keyboardSize.height + self.keyboardPadding;
//NSLog(@"visibleFrame %@", NSStringFromCGRect(visibleFrame));
visibleFrame.size.height -= paddedKeyboardHeight;
//NSLog(@"visibleFrame after keyboard height %@", NSStringFromCGRect(visibleFrame));
if (CGRectContainsPoint(visibleFrame, activeField.frame.origin))
return;
scrollPositionBeforeKeyboardAdjustments = scrollView.contentOffset;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, activeField.frame.origin.y - visibleFrame.size.height + activeField.frame.size.height, 0);
contentInsets = UIEdgeInsetsMake(0.0, 0.0, paddedKeyboardHeight, 0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
CGSize scrollContentSize = self.scrollView.bounds.size;
scrollContentSize.height += paddedKeyboardHeight;
self.scrollView.contentSize = scrollContentSize;
//NSLog(@"scrollView %@", NSStringFromCGRect(scrollView.frame));
//NSLog(@"activeField %@", NSStringFromCGRect(activeField.frame));
//[scrollView scrollRectToVisible:activeField.frame animated:YES];
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y - visibleFrame.size.height + activeField.frame.size.height);
//NSLog(@"scrollPoint %@", NSStringFromCGPoint(scrollPoint));
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
- (void) keyboardWillBeHidden: (NSNotification*) notification
{
//NSLog(@"keyboardWillBeHidden");
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
// this doesn't work when changing orientation while the keyboard is visible
// because when keyboardDidShow: will be called right after this method the contentOffset will still be equal to the old value
//[scrollView setContentOffset:scrollPositionBeforeKeyboardAdjustments animated:YES];
[UIView animateWithDuration:.25 animations:^
{
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// replacement for setContentOffset:animated:
self.scrollView.contentOffset = scrollPositionBeforeKeyboardAdjustments;
}];
}
- (void) textFieldDidBeginEditing: (UITextField*) textField
{
activeField = textField;
}
- (void) textFieldDidEndEditing: (UITextField*) textField
{
activeField = nil;
}
@end
如果您有任何问题,我的项目托管在 github 上: https ://github.com/kuchumovn/sociopathy.ios
为了更好地解释,我还截取了截图:
参考以下
import UIKit
@available(tvOS, unavailable)
public class KeyboardLayoutConstraint: NSLayoutConstraint {
private var offset : CGFloat = 0
private var keyboardVisibleHeight : CGFloat = 0
@available(tvOS, unavailable)
override public func awakeFromNib() {
super.awakeFromNib()
offset = constant
NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillShowNotification(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillHideNotification(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: Notification
@objc func keyboardWillShowNotification(_ notification: Notification) {
if let userInfo = notification.userInfo {
if let frameValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue {
let frame = frameValue.cgRectValue
keyboardVisibleHeight = frame.size.height
}
self.updateConstant()
switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) {
case let (.some(duration), .some(curve)):
let options = UIViewAnimationOptions(rawValue: curve.uintValue)
UIView.animate(
withDuration: TimeInterval(duration.doubleValue),
delay: 0,
options: options,
animations: {
UIApplication.shared.keyWindow?.layoutIfNeeded()
return
}, completion: { finished in
})
default:
break
}
}
}
@objc func keyboardWillHideNotification(_ notification: NSNotification) {
keyboardVisibleHeight = 0
self.updateConstant()
if let userInfo = notification.userInfo {
switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) {
case let (.some(duration), .some(curve)):
let options = UIViewAnimationOptions(rawValue: curve.uintValue)
UIView.animate(
withDuration: TimeInterval(duration.doubleValue),
delay: 0,
options: options,
animations: {
UIApplication.shared.keyWindow?.layoutIfNeeded()
return
}, completion: { finished in
})
default:
break
}
}
}
func updateConstant() {
self.constant = offset + keyboardVisibleHeight
}
}
试试这个,它工作得很好:
if Firstnametxt.text == "" || Passwordtxt.text == "" || emailtxt.text == ""
{
if Firstnametxt.text == ""
{
Firstnametxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
Firstnametxt.becomeFirstResponder()
}
else if Passwordtxt.text == ""
{
Passwordtxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
Passwordtxt.becomeFirstResponder()
}
else if emailtxt.text == ""
{
emailtxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
emailtxt.becomeFirstResponder()
}
}
else
{
let isValidEmail:Bool = self.isValidEmail(emailtxt.text!)
if isValidEmail == true
{
}
else
{
emailtxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
emailtxt.becomeFirstResponder()
}
}
首先,我为你的页面推荐一个更好的设计,这样就不需要滚动你的视图。 如果你有很多文本字段,你仍然不必使用 ScrollView,它只会让事情变得复杂。 您可以在控制器的原始视图顶部添加一个容器 UIView,然后将这些文本字段放在该视图上。 当键盘显示或消失时,只需使用动画来移动这个容器视图。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.