当文本字段被选中时,滚动UITableView

经过大量的试验和错误,我放弃了,并提出了这个问题。我见过很多人有类似的问题,但不能得到所有的答案工作正确。

我有一个UITableView,它由自定义单元格组成。单元格由相邻的5个文本字段组成(有点像网格)。

当我试图滚动和编辑UITableView底部的单元格时,我不能设法让我的单元格正确地位于键盘上方。

我看到过很多关于改变视图大小的回答……但到目前为止,没有一种效果很好。

谁能用一个具体的代码示例来阐明这样做的“正确”方式?

209564 次浏览

因为你在一个表格中有文本字段,最好的方法是调整表格的大小——你需要设置tableView.frame的高度比键盘的大小小(我认为大约165像素),然后当键盘被关闭时再次展开它。

如果你不想让用户滚动,你也可以选择在那个时候禁用tableView的用户交互。

我想我已经想出了与苹果应用程序行为相匹配的解决方案。

首先,在viewWillAppear:订阅键盘通知,这样你就知道什么时候键盘会显示和隐藏,系统会告诉你键盘的大小,但不要忘记在viewWillDisappear:中取消注册。

[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];

实现类似于下面的方法,以便在键盘显示时调整tableView的大小以匹配可见区域。这里我单独跟踪键盘的状态,所以我可以自己选择何时将tableView设置为全高度,因为你在每个字段更改时都会收到这些通知。不要忘记实现keyboardWillHide:并选择适当的地方来修复你的tableView大小。

-(void) keyboardWillShow:(NSNotification *)note
{
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardBounds];
keyboardHeight = keyboardBounds.size.height;
if (keyboardIsShowing == NO)
{
keyboardIsShowing = YES;
CGRect frame = self.view.frame;
frame.size.height -= keyboardHeight;


[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3f];
self.view.frame = frame;
[UIView commitAnimations];
}
}

现在这是滚动位,我们先计算出一些大小,然后我们看到我们在可见区域中的位置,并设置我们想要滚动的矩形,根据它在视图中的位置,将它设置为文本字段中间上方或下方的半视图。在本例中,我们有一个UITextFields数组和一个用于跟踪它们的enum,因此用rowHeight乘以行号就得到了这个外部视图中帧的实际偏移量。

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
CGRect frame = textField.frame;
CGFloat rowHeight = self.tableView.rowHeight;
if (textField == textFields[CELL_FIELD_ONE])
{
frame.origin.y += rowHeight * CELL_FIELD_ONE;
}
else if (textField == textFields[CELL_FIELD_TWO])
{
frame.origin.y += rowHeight * CELL_FIELD_TWO;
}
else if (textField == textFields[CELL_FIELD_THREE])
{
frame.origin.y += rowHeight * CELL_FIELD_THREE;
}
else if (textField == textFields[CELL_FIELD_FOUR])
{
frame.origin.y += rowHeight * CELL_FIELD_FOUR;
}
CGFloat viewHeight = self.tableView.frame.size.height;
CGFloat halfHeight = viewHeight / 2;
CGFloat midpoint = frame.origin.y + (textField.frame.size.height / 2);
if (midpoint < halfHeight)
{
frame.origin.y = 0;
frame.size.height = midpoint;
}
else
{
frame.origin.y = midpoint;
frame.size.height = midpoint;
}
[self.tableView scrollRectToVisible:frame animated:YES];
}

这似乎工作得很好。

执行滚动的函数可以简单得多:

- (void) textFieldDidBeginEditing:(UITextField *)textField {
UITableViewCell *cell;


if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
// Load resources for iOS 6.1 or earlier
cell = (UITableViewCell *) textField.superview.superview;


} else {
// Load resources for iOS 7 or later
cell = (UITableViewCell *) textField.superview.superview.superview;
// TextField -> UITableVieCellContentView -> (in iOS 7!)ScrollView -> Cell!
}
[tView scrollToRowAtIndexPath:[tView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

就是这样。根本不需要计算。

如果你使用uitableview来放置你的textfields (杰夫·拉马彻),你可以像这样使用delegate方法滚动tableview。

(注意:我的文本字段存储在一个数组中,索引与表视图中的行相同)

- (void) textFieldDidBeginEditing:(UITextField *)textField
{


int index;
for(UITextField *aField in textFields){


if (textField == aField){
index = [textFields indexOfObject:aField]-1;
}
}


if(index >= 0)
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];


[super textFieldDidBeginEditing:textField];
}

键盘通知可以工作,但苹果的示例代码假设滚动视图是窗口的根视图。通常情况并非如此。您必须补偿标签栏等,以获得正确的偏移量。

这比听起来容易。下面是我在UITableViewController中使用的代码。它有两个实例变量,hiddenRect和keyboardshow。

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification {
if (keyboardShown)
return;


NSDictionary* info = [aNotification userInfo];


// Get the frame of the keyboard.
NSValue *centerValue = [info objectForKey:UIKeyboardCenterEndUserInfoKey];
NSValue *boundsValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGPoint keyboardCenter = [centerValue CGPointValue];
CGRect keyboardBounds = [boundsValue CGRectValue];
CGPoint keyboardOrigin = CGPointMake(keyboardCenter.x - keyboardBounds.size.width / 2.0,
keyboardCenter.y - keyboardBounds.size.height / 2.0);
CGRect keyboardScreenFrame = { keyboardOrigin, keyboardBounds.size };




// Resize the scroll view.
UIScrollView *scrollView = (UIScrollView *) self.tableView;
CGRect viewFrame = scrollView.frame;
CGRect keyboardFrame = [scrollView.superview convertRect:keyboardScreenFrame fromView:nil];
hiddenRect = CGRectIntersection(viewFrame, keyboardFrame);


CGRect remainder, slice;
CGRectDivide(viewFrame, &slice, &remainder, CGRectGetHeight(hiddenRect), CGRectMaxYEdge);
scrollView.frame = remainder;


// Scroll the active text field into view.
CGRect textFieldRect = [/* selected cell */ frame];
[scrollView scrollRectToVisible:textFieldRect animated:YES];


keyboardShown = YES;
}




// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
// Reset the height of the scroll view to its original value
UIScrollView *scrollView = (UIScrollView *) self.tableView;
CGRect viewFrame = [scrollView frame];
scrollView.frame = CGRectUnion(viewFrame, hiddenRect);


keyboardShown = NO;
}

一个更加流线型的解决方案。它滑入UITextField委托方法,所以它不需要搞砸/ UIKeyboard通知。

实现注意事项:

kSettingsRowHeight——UITableViewCell的高度。

offsetTarget和offsetThreshold基于kSettingsRowHeight。如果使用不同的行高,则将这些值设置为point的y属性。[alt:以不同的方式计算行偏移量。]

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
CGFloat offsetTarget    = 113.0f; // 3rd row
CGFloat offsetThreshold = 248.0f; // 6th row (i.e. 2nd-to-last row)


CGPoint point = [self.tableView convertPoint:CGPointZero fromView:textField];


[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];


CGRect frame = self.tableView.frame;
if (point.y > offsetThreshold) {
self.tableView.frame = CGRectMake(0.0f,
offsetTarget - point.y + kSettingsRowHeight,
frame.size.width,
frame.size.height);
} else if (point.y > offsetTarget) {
self.tableView.frame = CGRectMake(0.0f,
offsetTarget - point.y,
frame.size.width,
frame.size.height);
} else {
self.tableView.frame = CGRectMake(0.0f,
0.0f,
frame.size.width,
frame.size.height);
}


[UIView commitAnimations];


return YES;

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];


[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];


CGRect frame = self.tableView.frame;
self.tableView.frame = CGRectMake(0.0f,
0.0f,
frame.size.width,
frame.size.height);


[UIView commitAnimations];


return YES;

我在做一些非常相似的事情,它是通用的,不需要为你的代码计算特定的东西。 只需检查代码上的注释:

在MyUIViewController.h

@interface MyUIViewController: UIViewController <UITableViewDelegate, UITableViewDataSource>
{
UITableView *myTableView;
UITextField *actifText;
}


@property (nonatomic, retain) IBOutlet UITableView *myTableView;
@property (nonatomic, retain) IBOutlet UITextField *actifText;


- (IBAction)textFieldDidBeginEditing:(UITextField *)textField;
- (IBAction)textFieldDidEndEditing:(UITextField *)textField;


-(void) keyboardWillHide:(NSNotification *)note;
-(void) keyboardWillShow:(NSNotification *)note;


@end

在MyUIViewController.m

@implementation MyUIViewController


@synthesize myTableView;
@synthesize actifText;


- (void)viewDidLoad
{
// Register notification when the keyboard will be show
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];


// Register notification when the keyboard will be hide
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}


// To be link with your TextField event "Editing Did Begin"
//  memoryze the current TextField
- (IBAction)textFieldDidBeginEditing:(UITextField *)textField
{
self.actifText = textField;
}


// To be link with your TextField event "Editing Did End"
//  release current TextField
- (IBAction)textFieldDidEndEditing:(UITextField *)textField
{
self.actifText = nil;
}


-(void) keyboardWillShow:(NSNotification *)note
{
// Get the keyboard size
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];


// Detect orientation
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGRect frame = self.myTableView.frame;


// Start animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3f];


// Reduce size of the Table view
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
frame.size.height -= keyboardBounds.size.height;
else
frame.size.height -= keyboardBounds.size.width;


// Apply new size of table view
self.myTableView.frame = frame;


// Scroll the table view to see the TextField just above the keyboard
if (self.actifText)
{
CGRect textFieldRect = [self.myTableView convertRect:self.actifText.bounds fromView:self.actifText];
[self.myTableView scrollRectToVisible:textFieldRect animated:NO];
}


[UIView commitAnimations];
}


-(void) keyboardWillHide:(NSNotification *)note
{
// Get the keyboard size
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];


// Detect orientation
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGRect frame = self.myTableView.frame;


[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3f];


// Increase size of the Table view
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
frame.size.height += keyboardBounds.size.height;
else
frame.size.height += keyboardBounds.size.width;


// Apply new size of table view
self.myTableView.frame = frame;


[UIView commitAnimations];
}


@end

Swift 1.2+版本:

class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var activeText: UITextField!
@IBOutlet weak var tableView: UITableView!


override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self,
selector: Selector("keyboardWillShow:"),
name: UIKeyboardWillShowNotification,
object: nil)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: Selector("keyboardWillHide:"),
name: UIKeyboardWillHideNotification,
object: nil)
}


func textFieldDidBeginEditing(textField: UITextField) {
activeText = textField
}


func textFieldDidEndEditing(textField: UITextField) {
activeText = nil
}


func keyboardWillShow(note: NSNotification) {
if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
var frame = tableView.frame
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.3)
frame.size.height -= keyboardSize.height
tableView.frame = frame
if activeText != nil {
let rect = tableView.convertRect(activeText.bounds, fromView: activeText)
tableView.scrollRectToVisible(rect, animated: false)
}
UIView.commitAnimations()
}
}


func keyboardWillHide(note: NSNotification) {
if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
var frame = tableView.frame
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.3)
frame.size.height += keyboardSize.height
tableView.frame = frame
UIView.commitAnimations()
}
}
}

如果你使用UITableViewController而不是UIViewController,它会自动这样做。

如果使用Three20,则使用autoresizesForKeyboard属性。只要在你的视图控制器的-initWithNibName:bundle方法中设置

self.autoresizesForKeyboard = YES

这涉及到:

  1. 监听键盘通知并调整表视图的框架
  2. 滚动到第一个响应器

搞定了。

这非常有效,在iPad上也是如此。

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{


if(textField == textfield1){
[accountName1TextField becomeFirstResponder];
}else if(textField == textfield2){
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[textfield3 becomeFirstResponder];


}else if(textField == textfield3){
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[textfield4 becomeFirstResponder];


}else if(textField == textfield4){
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[textfield5 becomeFirstResponder];


}else if(textField == textfield5){
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[textfield6 becomeFirstResponder];


}else if(textField == textfield6){
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:4 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[textfield7 becomeFirstResponder];


}else if(textField == textfield7){
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:5 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[textfield8 becomeFirstResponder];


}else if(textField == textfield8){
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:6 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[textfield9 becomeFirstResponder];


}else if(textField == textfield9){
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:7 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[textField resignFirstResponder];
}

如果你可以使用UITableViewController,你就可以免费得到这个功能。然而,有时这不是一个选项,特别是如果你需要多个视图,而不仅仅是UITableView

这里介绍的一些解决方案在iOS≥4上不起作用,一些在iPad或横屏模式下不起作用,一些不适用于蓝牙键盘(我们不希望有任何滚动),一些在多个文本字段之间切换时不起作用。因此,如果您选择任何解决方案,请确保测试这些情况。这是我们在InAppSettingsKit使用 使用的解决方案:

- (void)_keyboardWillShow:(NSNotification*)notification {
if (self.navigationController.topViewController == self) {
NSDictionary* userInfo = [notification userInfo];


// we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0
NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
if (!keyboardFrameValue) {
keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"];
}


// Reduce the tableView height by the part of the keyboard that actually covers the tableView
CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
windowRect = IASKCGRectSwap(windowRect);
}
CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute);
}
CGRect frame = _tableView.frame;
frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);


[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
_tableView.frame = frame;
[UIView commitAnimations];


UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];


// iOS 3 sends hide and show notifications right after each other
// when switching between textFields, so cancel -scrollToOldPosition requests
[NSObject cancelPreviousPerformRequestsWithTarget:self];


[_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
}


- (void) scrollToOldPosition {
[_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
}


- (void)_keyboardWillHide:(NSNotification*)notification {
if (self.navigationController.topViewController == self) {
NSDictionary* userInfo = [notification userInfo];


[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
_tableView.frame = self.view.bounds;
[UIView commitAnimations];


[self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];
}
}

这里是InAppSettingsKit中的类的完整代码。要测试它,请使用“Complete List”子窗格,您可以在其中测试上面提到的场景。

UITableViewController确实自动滚动。 与使用UIViewController的区别在于,当使用TableViewController.

时,你必须通过使用NavigationController以编程方式创建Navbar-Buttonitems
我尝试了几乎相同的方法,并提出了一个更简单和更小的代码。 我创建了一个IBOutlet iTextView,并与IB中的UITextView相关联
 -(void)keyboardWillShow:(NSNotification *)notification
{
NSLog(@"Keyboard");
CGRect keyFrame = [[[notification userInfo]objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue];


[UIView beginAnimations:@"resize view" context:nil];
[UIView setAnimationCurve:1];
[UIView setAnimationDuration:1.0];
CGRect frame = iTableView.frame;
frame.size.height = frame.size.height -  keyFrame.size.height;
iTableView.frame = frame;
[iTableView scrollRectToVisible:frame animated:YES];
[UIView commitAnimations];


}

我遇到了同样的问题,但注意到它只出现在一个视图中。所以我开始寻找不同的控制器。

我发现滚动行为是在超级实例的- (void)viewWillAppear:(BOOL)animated中设置的。

所以一定要像这样实现:

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// your code
}

不管你使用的是UIViewController还是UITableViewController;通过将UITableView作为self的子视图来检查它。在UIViewController中查看。这是相同的行为。如果缺少[super viewWillAppear:animated];调用,视图不允许滚动。

因此,经过数小时的艰苦工作,尝试使用这些当前的解决方案(完全失败),我终于让事情正常工作,并更新它们以使用新的动画块。我的答案完全基于Ortwin的回答在上面

所以不管出于什么原因,上面的代码就是不适合我。我的设置似乎和其他人的很相似,但可能是因为我用的是iPad或4.3……不知道。它在做一些古怪的数学运算然后把我的tableview从屏幕上拍下来。

见我的解决方案的最终结果:http://screencast.com/t/hjBCuRrPC(请忽略照片。: - p)

所以我沿用了Ortwin的基本原理,但改变了计算原点的方法。y和;大小。我的表格视图的高度和键盘的高度。当我从结果中减去窗口的高度时,它告诉我有多少交集。如果它大于0(也就是有一些重叠),我执行帧高度的动画。

此外,还有一些重绘问题得到了解决,1)等待滚动到单元格直到动画完成,2)使用UIViewAnimationOptionBeginFromCurrentState选项隐藏键盘。

有几件事需要注意。

  • _topmostRowBeforeKeyboardWasShown,_originalFrame是在头文件中声明的实例变量。
  • 自我。guestEntryTableView是我的tableView(我在外部文件中)
  • iaskcgrecswap是Ortwin用于翻转帧坐标的方法
  • 我只更新tableView的高度如果它至少有50px会显示
  • 因为我不在UIViewController中,所以我没有self。视图,我只是把tableView返回到它的原始框架

同样,如果我Ortwin没有提供关键,我也不会得到这个答案。代码如下:

- (IBAction)textFieldDidBeginEditing:(UITextField *)textField
{
self.activeTextField = textField;


if ([self.guestEntryTableView indexPathsForVisibleRows].count) {
_topmostRowBeforeKeyboardWasShown = (NSIndexPath*)[[self.guestEntryTableView indexPathsForVisibleRows] objectAtIndex:0];
} else {
// this should never happen
_topmostRowBeforeKeyboardWasShown = [NSIndexPath indexPathForRow:0 inSection:0];
[textField resignFirstResponder];
}
}


- (IBAction)textFieldDidEndEditing:(UITextField *)textField
{
self.activeTextField = nil;
}


- (void)keyboardWillShow:(NSNotification*)notification {
NSDictionary* userInfo = [notification userInfo];


NSValue* keyboardFrameValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];


// Reduce the tableView height by the part of the keyboard that actually covers the tableView
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
CGRect viewRectAbsolute = [self.guestEntryTableView convertRect:self.guestEntryTableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
CGRect keyboardFrame = [keyboardFrameValue CGRectValue];
if (UIInterfaceOrientationLandscapeLeft == orientation ||UIInterfaceOrientationLandscapeRight == orientation ) {
windowRect = IASKCGRectSwap(windowRect);
viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute);
keyboardFrame = IASKCGRectSwap(keyboardFrame);
}


// fix the coordinates of our rect to have a top left origin 0,0
viewRectAbsolute = FixOriginRotation(viewRectAbsolute, orientation, windowRect.size.width, windowRect.size.height);


CGRect frame = self.guestEntryTableView.frame;
_originalFrame = self.guestEntryTableView.frame;


int remainder = (viewRectAbsolute.origin.y + viewRectAbsolute.size.height + keyboardFrame.size.height) - windowRect.size.height;


if (remainder > 0 && !(remainder > frame.size.height + 50)) {
frame.size.height = frame.size.height - remainder;
float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration: duration
animations:^{
self.guestEntryTableView.frame = frame;
}
completion:^(BOOL finished){
UITableViewCell *textFieldCell = (UITableViewCell*) [[self.activeTextField superview] superview];
NSIndexPath *textFieldIndexPath = [self.guestEntryTableView indexPathForCell:textFieldCell];
[self.guestEntryTableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}];
}


}


- (void)keyboardWillHide:(NSNotification*)notification {
NSDictionary* userInfo = [notification userInfo];
float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration: duration
delay: 0.0
options: (UIViewAnimationOptionBeginFromCurrentState)
animations:^{
self.guestEntryTableView.frame = _originalFrame;
}
completion:^(BOOL finished){
[self.guestEntryTableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
}];


}


#pragma mark CGRect Utility function
CGRect IASKCGRectSwap(CGRect rect) {
CGRect newRect;
newRect.origin.x = rect.origin.y;
newRect.origin.y = rect.origin.x;
newRect.size.width = rect.size.height;
newRect.size.height = rect.size.width;
return newRect;
}


CGRect FixOriginRotation(CGRect rect, UIInterfaceOrientation orientation, int parentWidth, int parentHeight) {
CGRect newRect;
switch(orientation)
{
case UIInterfaceOrientationLandscapeLeft:
newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), rect.origin.y, rect.size.width, rect.size.height);
break;
case UIInterfaceOrientationLandscapeRight:
newRect = CGRectMake(rect.origin.x, parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height);
break;
case UIInterfaceOrientationPortrait:
newRect = rect;
break;
case UIInterfaceOrientationPortraitUpsideDown:
newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height);
break;
}
return newRect;
}

结合并填写几个答案(特别是Ortwin Gentz,用户98013)和另一篇文章中的空白,这将适用于iPad上的SDK 4.3的纵向或横向模式:

@implementation UIView (FindFirstResponder)
- (UIResponder *)findFirstResponder
{
if (self.isFirstResponder) {
return self;
}


for (UIView *subView in self.subviews) {
UIResponder *firstResponder = [subView findFirstResponder];
if (firstResponder != nil) {
return firstResponder;
}
}


return nil;
}
@end


@implementation MyViewController


- (UIResponder *)currentFirstResponder {
return [self.view findFirstResponder];
}


- (IBAction)editingEnded:sender {
[sender resignFirstResponder];
}


- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}


- (void)textFieldDidBeginEditing:(UITextField *)textField {
UITableViewCell *cell = (UITableViewCell*) [[textField superview] superview];
[_tableView scrollToRowAtIndexPath:[_tableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}


- (void)keyboardWillShow:(NSNotification*)notification {
if ([self currentFirstResponder] != nil) {
NSDictionary* userInfo = [notification userInfo];


// we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0
NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
if (!keyboardFrameValue) {
keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"];
}


// Reduce the tableView height by the part of the keyboard that actually covers the tableView
CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
CGRect frame = _tableView.frame;
if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
windowRect = CGRectMake(windowRect.origin.y, windowRect.origin.x, windowRect.size.height, windowRect.size.width);
viewRectAbsolute = CGRectMake(viewRectAbsolute.origin.y, viewRectAbsolute.origin.x, viewRectAbsolute.size.height, viewRectAbsolute.size.width);
}
frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);


[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
_tableView.frame = frame;
[UIView commitAnimations];


UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];


// iOS 3 sends hide and show notifications right after each other
// when switching between textFields, so cancel -scrollToOldPosition requests
[NSObject cancelPreviousPerformRequestsWithTarget:self];
_topmostRowBeforeKeyboardWasShown = [[_tableView indexPathsForVisibleRows] objectAtIndex:0];
[_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
}


- (void) scrollToOldPosition {
[_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
}


- (void)keyboardWillHide:(NSNotification*)notification {
if ([self currentFirstResponder] != nil) {


NSDictionary* userInfo = [notification userInfo];


[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
_tableView.frame = self.view.bounds;
[UIView commitAnimations];


[self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];
}
}


@end

我刚刚再次查看了iOS 5.0库参考,发现这一节题为“移动的内容,位于键盘下”:TextAndWebiPhoneOS KeyboardManagement

这是iOS 5之后的新功能吗?我还没有深入了解它,因为我正在做其他事情,但也许其他人知道得更多,可以启发我和其他人。

苹果文档是否会取代这里讨论的内容,或者这里的信息对iOS 5 SDK用户仍然有用?

另一个简单的方法(只适用于一个部分)

//cellForRowAtIndexPath
UItextField *tf;
[cell addSubview:tf];
tf.tag = indexPath.row;
tf.delegate = self;


//textFieldDidBeginEditing:(UITextField *)text
[[self.tableView scrollToRowsAtIndexPath:[NSIndexPath indexPathForRow:text.tag in section:SECTIONINTEGER] animated:YES];

这个解决方案为我工作,请注意这一行

[tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES];

您可以更改160值以使其与您的工作相匹配

- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = activeField.superview.frame;
bkgndRect.size.height += kbSize.height;
[activeField.superview setFrame:bkgndRect];
[tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES];
}


- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeField = textField;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
activeField = nil;
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
tableView.contentInset = contentInsets;
tableView.scrollIndicatorInsets = contentInsets;
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = activeField.superview.frame;
//bkgndRect.size.height += kbSize.height;
[activeField.superview setFrame:bkgndRect];
[tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height) animated:YES];
}

如果你的UITableView是由UITableViewController的子类管理的,而不是UITableView,并且文本字段委托是UITableViewController,它应该自动管理所有的滚动——所有这些其他注释在实践中是很难实现的。

有关一个好例子,请参阅apple示例代码项目:TaggedLocations。

你可以看到它自动滚动,但似乎没有任何代码做到这一点。该项目还具有自定义表视图单元格,因此,如果使用它作为指南构建应用程序,应该会得到预期的结果。

非常有趣的讨论线程,我也面临着同样的问题,可能更糟糕的一个原因

  1. 我正在使用一个自定义单元格和文本字段在里面。
  2. 我不得不使用UIViewController来满足我的要求,所以不能利用UITableViewController。
  3. 我有过滤/排序标准在我的表格单元格,即你的单元格不断改变和跟踪indexpath和所有将没有帮助。
所以在这里阅读线程并实现我的版本,这帮助我在iPad中以景观模式推送我的内容。 这里是代码(这不是傻瓜证明,但它修复了我的问题) 首先,你需要在你的自定义单元格类中有一个委托,在编辑开始时,将文本字段发送到你的视图控制器,并设置activefield = theTextField there

//只处理横向模式

- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect aRect = myTable.frame;


CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width);


aRect.size.height -= kbSize.height+50;
// This will the exact rect in which your textfield is present
CGRect rect =  [myTable convertRect:activeField.bounds fromView:activeField];
// Scroll up only if required
if (!CGRectContainsPoint(aRect, rect.origin) ) {




[myTable setContentOffset:CGPointMake(0.0, rect.origin.y) animated:YES];


}




}

//当发送UIKeyboardWillHideNotification时调用

- (void)keyboardWillHide:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
myTable.contentInset = contentInsets;
myTable.scrollIndicatorInsets = contentInsets;
NSDictionary* info = [aNotification userInfo];
CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width);
CGRect bkgndRect = activeField.superview.frame;
bkgndRect.size.height += kbSize.height;
[activeField.superview setFrame:bkgndRect];
[myTable setContentOffset:CGPointMake(0.0, 10.0) animated:YES];
}

-anoop4real

以下是我如何做到这一点的,这是Sam Ho和Marcel W的回答,以及我自己对蹩脚代码的一些错误修复。我用的是UITableViewController。当键盘显示时,表格现在可以正确地调整大小。

1)在viewDidLoad中我添加了:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2)我忘记调用viewWillAppearawakeFromNib中的super等价物。我把这些加回来了。

我可能错过了这一点,因为我没有阅读整篇文章,但我想到的似乎很简单。我并没有在所有情况下进行测试,但它看起来应该工作得很好。

简单地根据键盘的高度调整tableview的contentInset,然后将单元格滚动到底部:

- (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.myTableView.contentInset = contentInsets;
self.myTableView.scrollIndicatorInsets = contentInsets;


[self.myTableView scrollToRowAtIndexPath:self.currentField.indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

当然

- (void)keyboardWasHidden:(NSNotification *)aNotification
{
[UIView animateWithDuration:.3 animations:^(void)
{
self.myTableView.contentInset = UIEdgeInsetsZero;
self.myTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}];
}

这是不是太简单了?我遗漏了什么吗?到目前为止,它对我来说还不错,但就像我说的,我还没有把它经过折腾……

我刚刚解决了这样一个问题,我参考了大量的解决方案通过谷歌和堆栈溢出。

首先,请确保您已经设置了UIScrollView的IBOutlet, 那么请仔细看看Apple Doc:键盘管理。 最后,如果你可以滚动背景,但键盘仍然覆盖文本字段,请看看这段代码:

// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;


if (aRect.size.height < activeField.frame.origin.y+activeField.frame.size.height) {


CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y+activeField.frame.size.height-aRect.size.height);


[scrollView setContentOffset:scrollPoint animated:YES];

这款游戏与苹果游戏的主要区别在于if条件。我认为apple计算滚动距离和键盘是否覆盖文本字段的条件不准确,所以我做了如上修改。

如果有用请告诉我

我认为没有“正确”的方法来做这件事。您必须为您的用例选择最合适的解决方案。 在我的iPad应用程序中,我有一个UIViewController,它以UIModalPresentationFormSheet的形式呈现,由UITableView组成。该表每个单元格包含两个UITextFields。 只是在textFieldDidBeginEditing:方法中调用scrollToRowAtIndexPath:atScrollPosition:animated:对我不起作用。因此,我创建了tableFooterView:

- (void)viewDidLoad
{
[super viewDidLoad];


m_footerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, m_tableView.frame.size.width, 300.0f)];
[m_footerView setBackgroundColor:[UIColor clearColor]];
[m_tableView setTableFooterView:m_footerView];
[m_footerView release];
}

其思想是键盘隐藏tableFooterView而不是UITextFields。所以tableFooterView必须足够高。之后,你可以在textFieldDidBeginEditing:方法中使用scrollToRowAtIndexPath:atScrollPosition:animated:

我认为也可以通过添加键盘通知的观察者来动态地显示和隐藏tableFooterView,但我还没有尝试过:

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear: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
{
[m_tableView setTableFooterView:m_footerView];
}


- (void)keyboardWillHide:(NSNotification *)notification
{
[m_tableView setTableFooterView:nil];
}


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


[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

我的方法:

我首先子类化UITextField并添加一个indexPath属性。在牢房里因为…方法我交出indexPath属性。

然后我添加以下代码:

UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:textField.indexPath];


CGPoint cellPoint = [cell convertPoint:textField.center toView:self.tableView];
[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, cellPoint.y-50);}];

textFieldShould/WillBegin…等等。

当键盘消失时,你必须反转它:

[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, 0);}];

简单快捷的解决方案。

我只要滚动到右边的单元格

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView

假设我知道表现在是在这个模式"_keepMyCellOnTop" ,我知道选定的单元格“_selectedCellIndex”或滚动到选定的单元格

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{


if (_keepMyCellOnTop)
{
[self.tableView scrollToRowAtIndexPath:_selectedCellIndex atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
}

这将防止滚动。

将代码放在-(void) scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 将导致上下滚动

我将把我的解决方案(或者QuickDialog的)扔进帽子里。基本上是等待动画到滚动。获得键盘动画JIT而不是神奇的数字会很好。

-(void)textFieldDidBeginEditing:(UITextField *)textField
{
if (textField == self.emailTextField) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 50 * USEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
});
}
}
我经常在我的项目中使用这个。这个解决方案适用于滚动视图,表视图或集合视图,它很容易设置。 它还自动连接键盘上的“Next”按钮来在文本字段之间切换

检查它在这里

我正在用这些,它们就像魔法一样管用:

BSKeyboardControls - BSKeyboardControls github

tpkeyboardavoidance - TPKeyboardAvoiding github

我认为最好的方法是通过UITableViewController。

如果你想要UIViewController中的UITableView,只需要用一个嵌入式UITableViewController创建一个ContentView,并把下面的行放在UIViewController的viedDidLoad中:

self.tableView = ((UITableViewController*)self.childViewControllers[0]).tableView;
self.tableView.delegate = self;
self.tableView.dataSource = self;

容易,)

以下是我的解决方案,灵感来自iOS7日历应用程序的“事件编辑”屏幕。

该解决方案的关键之一是当用户滚动表格时键盘被取消。

实现:

1)添加属性,将存储选定的文本字段:

@property (strong) UITextField *currentTextField;

和BOOL变量,我们将使用它来检查当用户滚动表时是否需要隐藏键盘。

BOOL hideKeyboardOnScroll;

2)处理UITextField委托回调:

#pragma mark - UITextFieldDelegate


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


- (void) textFieldDidEndEditing: (UITextField *) textField {
self.currentTextField = nil;
}


- (BOOL) textFieldShouldReturn: (UITextField *) textField {
[textField resignFirstResponder];


CGPoint newContentOffset = CGPointZero;
if (tableView.contentSize.height > tableView.frame.size.height) {
newContentOffset.y = MIN(tableView.contentOffset.y, tableView.contentSize.height - tableView.frame.size.height);
}
[tableView setContentOffset: newContentOffset animated: YES];


return YES;
}

3)处理UIScrollViewDelegate方法来检查用户滚动视图。

#pragma mark - UIScrollViewDelegate


- (void) scrollViewDidScroll: (UIScrollView *) scrollView {
if (hideKeyboardOnScroll == YES) {
[self.currentTextField resignFirstResponder];
}
}

4)在视图控制器的[viewWillAppear]方法中订阅键盘通知,并在[viewWillDisappear]方法中取消订阅。

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


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


[ [NSNotificationCenter defaultCenter] removeObserver: self name: UIKeyboardDidShowNotification object: nil];
[ [NSNotificationCenter defaultCenter] removeObserver: self name: UIKeyboardWillHideNotification object: nil];
}

5)处理键盘通知:

- (void) keyboardWillShow: (NSNotification *) notification {
CGRect keyboardFrame = [ [ [notification userInfo] objectForKey: UIKeyboardFrameBeginUserInfoKey] CGRectValue];


// Find cell with textfield.
CGRect textFieldFrame = [tableView convertRect: self.currentTextField.frame fromView: self.currentTextField];
NSIndexPath *indexPath = [tableView indexPathForRowAtPoint: textFieldFrame.origin];
UITableViewCell *cell = [tableView cellForRowAtIndexPath: indexPath];
//


// Shrink tableView size.
CGRect tableViewFrame = tableView.frame;
tableView.frame = CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width,
self.view.frame.size.height - tableView.frame.origin.y - keyboardFrame.size.height);
//


// Check if cell is visible in shrinked table size.
BOOL cellIsFullyVisible = YES;
if ( cell.frame.origin.y < tableView.contentOffset.y ||
(cell.frame.origin.y + cell.frame.size.height) > (tableView.contentOffset.y + tableView.frame.size.height) ) {
cellIsFullyVisible = NO;
}
//


// If cell is not fully visible when scroll table to show cell;
if (cellIsFullyVisible == NO) {
CGPoint contentOffset = CGPointMake(tableView.contentOffset.x, CGRectGetMaxY(cell.frame) - tableView.frame.size.height);
if (cell.frame.origin.y < tableView.contentOffset.y) {
contentOffset.y = cell.frame.origin.y;
}
contentOffset.y = MAX(0, contentOffset.y);


// For some reason [setContentOffset] is called without delay then
// this code may not work for some cells. That why we call it with brief delay.
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[UIView animateWithDuration: 0.5 animations:^{
[tableView setContentOffset: contentOffset animated: NO];
} completion: ^(BOOL finished) {
hideKeyboardOnScroll = YES;
}];
});
} else {
hideKeyboardOnScroll = YES;
}
//


// Finally restore original table frame.
tableView.frame = tableViewFrame;
//
}


- (void) keyboardWillHide: (NSNotification *) notification {
[super keyboardWillHide: notification];


hideKeyboardOnScroll = NO;
}

我希望你们读了这些已经找到了答案。但我找到了我的解决方案如下。我期待你已经有一个UITextField单元格。在准备时,保持行索引到文本字段的标签中。

cell.textField.tag = IndexPath.row;

创建一个具有全局作用域的activeTextField实例,如下所示:

@interface EditViewController (){


UITextField *activeTextField;


}

现在你只需要在最后复制粘贴我的代码。并且不要忘记添加UITextFieldDelegate

#pragma mark - TextField Delegation


- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{


activeTextField = textField;


return YES;
}


- (void)textFieldDidEndEditing:(UITextField *)textField{


activeTextField = nil;


}

注册键盘notifications

#pragma mark - Keyboard Activity


- (void)registerForKeyboardNotifications


{


[[NSNotificationCenter defaultCenter] addObserver:self


selector:@selector(keyboardWasShown:)


name:UIKeyboardDidShowNotification object:nil];






[[NSNotificationCenter defaultCenter] addObserver:self


selector:@selector(keyboardWillBeHidden:)


name:UIKeyboardWillHideNotification object:nil];






}

处理键盘Notifications:

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.tableView setContentInset:contentInsets];


[self.tableView setScrollIndicatorInsets:contentInsets];


NSIndexPath *currentRowIndex = [NSIndexPath indexPathForRow:activeTextField.tag inSection:0];


[self.tableView scrollToRowAtIndexPath:currentRowIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];


}

UIKeyboardWillHideNotification被发送时调用

- (void)keyboardWillBeHidden:(NSNotification*)aNotification


{


UIEdgeInsets contentInsets = UIEdgeInsetsZero;


[self.tableView setContentInset:contentInsets];


[self.tableView setScrollIndicatorInsets:contentInsets];


}

现在还剩下一件事,调用registerForKeyboardNotifications方法到ViewDidLoad方法,如下所示:

- (void)viewDidLoad {


[super viewDidLoad];


// Registering keyboard notification


[self registerForKeyboardNotifications];


// Your codes here...


}

你完成了,希望你的textFields将不再隐藏在键盘。

使用UITextField's delegate方法:

斯威夫特

func textFieldShouldBeginEditing(textField: UITextField) -> bool {
let txtFieldPosition = textField.convertPoint(textField.bounds.origin, toView: yourTableViewHere)
let indexPath = yourTablViewHere.indexPathForRowAtPoint(txtFieldPosition)
if indexPath != nil {
yourTablViewHere.scrollToRowAtIndexPath(indexPath!, atScrollPosition: .Top, animated: true)
}
return true
}

objective - c

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
CGPoint txtFieldPosition = [textField convertPoint:CGPointZero toView: yourTablViewHere];
NSLog(@"Begin txtFieldPosition : %@",NSStringFromCGPoint(txtFieldPosition));
NSIndexPath *indexPath = [yourTablViewHere indexPathForRowAtPoint:txtFieldPosition];


if (indexPath != nil) {
[yourTablViewHere scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
return YES;
}

Swift中的一个例子,使用在UITableViewCell中获取UITextField的indexPath中的文本字段的确切位置:

func textFieldDidBeginEditing(textField: UITextField) {
let pointInTable = textField.convertPoint(textField.bounds.origin, toView: self.accountsTableView)
let textFieldIndexPath = self.accountsTableView.indexPathForRowAtPoint(pointInTable)
accountsTableView.scrollToRowAtIndexPath(textFieldIndexPath!, atScrollPosition: .Top, animated: true)
}

斯威夫特最简单的解决方案:

override func viewDidLoad() {
super.viewDidLoad()
    

searchBar?.becomeFirstResponder()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillShow(_:)), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillHide(_:)), name: UIKeyboardDidHideNotification, object: nil)
}


deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}


func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size.height {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
}
}
}


func keyboardWillHide(notification: NSNotification) {
UIView.animateWithDuration(0.2, animations: { self.table_create_issue.contentInset = UIEdgeInsetsMake(0, 0, 0, 0) })
// For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
}

Swift 4.2或更高版本

override func viewDidLoad() {
super.viewDidLoad()
searchBar?.becomeFirstResponder()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardDidHideNotification, object: nil)
}


deinit {
NotificationCenter.default.removeObserver(self)
}


@objc func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
let keyboardHeight = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue.size.height
accountSettingsTableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
        

}
}
@objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.2, animations: { self.accountSettingsTableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) })}
}

在viewdidload

-(void)viewdidload{


[super viewdidload];


[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}


-(void)keyboardWillChange:(NSNotification*)sender{


NSLog(@"keyboardwillchange sender %@",sender);


float margin=0  // set your own topmargin




CGFloat originY = [[sender.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y;




if (originY >= self.view.frame.size.height){


NSLog(@"keyboardclose");






[tb_ setFrame:CGRectMake(0, margin, self.view.frame.size.width, self.view.frame.size.height-margin)];


}else{


NSLog(@"keyobard on");


float adjustedHeight = self.view.frame.size.height - margin - (self.view.frame.size.height-originY);


[tb_ setFrame:CGRectMake(0, margin, self.view.frame.size.width, adjustedHeight)];
}














}

看看我的版本:)

    - (void)keyboardWasShown:(NSNotification *)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = cellSelected.superview.frame;
bkgndRect.size.height += kbSize.height;
[cellSelected.superview setFrame:bkgndRect];
[tableView setContentOffset:CGPointMake(0.0, cellSelected.frame.origin.y-kbSize.height) animated:YES];
}




- (void)keyboardWasHidden:(NSNotification *)aNotification
{
[tableView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
}

基于bartjoomiej semazynk解斯威夫特3最简单的解决方案:

override func viewDidLoad() {
super.viewDidLoad()


NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}


deinit {
NotificationCenter.default.removeObserver(self)
}


// MARK: Keyboard Notifications


@objc func keyboardWillShow(notification: NSNotification) {
if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
}
}


@objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.2, animations: {
// For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
})
}
// scroll tableview so content ends at the middle of the tableview (out of the way of the keyboard)
CGPoint newContentOffset = CGPointMake(0, [self.tableView contentSize].height - (self.tableView.bounds.size.height / 2));
[self.tableView setContentOffset:newContentOffset animated:YES];

Swift 4.2完整解决方案

我创建了GIST和一套协议,它简化了显示、隐藏或更改键盘时添加额外空间的工作。

特性:

  • 正确使用键盘帧变化(例如,键盘高度变化,如emojii→普通键盘)。
  • TabBar,工具栏支持UITableView的例子(在其他例子你收到不正确的插入)。
  • 动态动画持续时间(非硬编码)。
  • 面向协议的方法,可以很容易地为您的目的进行修改。

使用

包含一些滚动视图(当然也支持表格视图)的视图控制器的基本用法示例。

class SomeViewController: UIViewController {
@IBOutlet weak var scrollView: UIScrollView!


override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addKeyboardFrameChangesObserver()
}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeKeyboardFrameChangesObserver()
}
}


extension SomeViewController: ModifableInsetsOnKeyboardFrameChanges {
var scrollViewToModify: UIScrollView { return scrollView }
}

核心:帧改变观察者

协议KeyboardChangeFrameObserver将在每次键盘帧被改变时触发事件(包括显示、隐藏、帧改变)。

  1. viewWillAppear()或类似的方法调用addKeyboardFrameChangesObserver()
  2. viewWillDisappear()或类似的方法调用removeKeyboardFrameChangesObserver()

实现:滚动视图

ModifableInsetsOnKeyboardFrameChanges协议为核心协议添加了UIScrollView支持。当键盘帧改变时,它改变滚动视图的嵌入。

你的类需要设置滚动视图,一个人的插入将增加/减少键盘帧的变化。

var scrollViewToModify: UIScrollView { get }

不需要任何计算,使用下面的代码它将工作: 这段代码我在我的自定义UITableviewcell中使用,它正在工作:

override func viewDidLoad() {
super.viewDidLoad()


NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)


NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)}




func keyboardWillShow(_ notification:Notification) {


if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
}}




func keyboardWillHide(_ notification:Notification) {


if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}}

Swift 3-4动画和键盘帧改变的解决方案:

首先,创建Bool类型:

// MARK: - Private Properties
private var isKeyboardShowing = false

其次,在系统键盘通知中添加观察者:

// MARK: - Overriding ViewController Life Cycle Methods
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)


NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil)


NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil)


NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: .UIKeyboardWillChangeFrame, object: nil)
}

第三,准备动画功能:

func adjustTableViewInsets(keyboardHeight: CGFloat, duration: NSNumber, curve: NSNumber){
var extraHeight: CGFloat = 0
if keyboardHeight > 0 {
extraHeight = 20
isKeyboardShowing = true
} else {
isKeyboardShowing = false
}


let contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight + extraHeight, right: 0)
func animateFunc() {
//refresh constraints
//self.view.layoutSubviews()
tableView.contentInset = contentInset
}


UIView.animate(withDuration: TimeInterval(duration), delay: 0, options: [UIViewAnimationOptions(rawValue: UInt(curve))], animations: animateFunc, completion: nil)
}

然后添加target/action方法(由观察者调用):

// MARK: - Target/Selector Actions
func keyboardWillShow(notification: NSNotification) {
if !isKeyboardShowing {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardHeight = keyboardSize.height


let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber


adjustTableViewInsets(keyboardHeight: keyboardHeight, duration: duration, curve: curve)
}
}
}


func keyboardWillHide(notification: NSNotification) {
let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber
adjustTableViewInsets(keyboardHeight: 0, duration: duration, curve: curve)
}


func keyboardWillChangeFrame(notification: NSNotification) {
if isKeyboardShowing {
let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber


if let newKeyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardHeight = newKeyboardSize.height
adjustTableViewInsets(keyboardHeight: keyboardHeight, duration: duration, curve: curve)
}
}
}

最后,不要忘记在deinit或viewWillDisappear中删除观察者:

deinit {
NotificationCenter.default.removeObserver(self)
}

斯威夫特4.2的小变化…

在我的UITableView上,我有许多部分,但我必须避免浮动标题效应,所以我使用了“dummyViewHeight”方法,如在Stack Overflow上的其他地方所见… 这就是我对这个问题的解决方案(它也适用于键盘+工具栏+建议):

将其声明为类常量:

let dummyViewHeight: CGFloat = 40.0

然后

override func viewDidLoad() {
super.viewDidLoad()
//... some stuff here, not needed for this example


// Create non floating header
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: dummyViewHeight))
tableView.contentInset = UIEdgeInsets(top: -dummyViewHeight, left: 0, bottom: 0, right: 0)


addObservers()
}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeObservers()
}

这里所有的魔法……

@objc func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
let keyboardHeight = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue.size.height
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight))
tableView.contentInset = UIEdgeInsets(top: -dummyViewHeight, left: 0, bottom: keyboardHeight, right: 0)
}
}


@objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.25) {
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: self.dummyViewHeight))
self.tableView.contentInset = UIEdgeInsets(top: -self.dummyViewHeight, left: 0, bottom: 0, right: 0)
}
}

我刚刚发现另一个bug时使用UITableViewController。当键盘出现时,它不会自动滚动。我注意到这是因为contentInsetAdjustmentBehavior = .never在UITableView上。