选择文本字段时使UITableView滚动


251

经过大量的反复试验后,我放弃了,问了一个问题。我见过很多有类似问题的人,但无法获得所有正确答案的答案。

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

当我尝试滚动和编辑底部的单元格时 UITableView,无法设法将单元格正确在键盘上方。

我已经看到了很多有关更改视图大小等问题的答案,但是到目前为止,这些方法都效果不佳。

有人可以通过一个具体的代码示例来阐明实现此目的的“正确”方法吗?



@ChrisP该链接指出它尚未针对iOS 4.0更新
Bae

这段代码可能会有所帮助: gist.github.com/TimMedcalf/9505416
landonandrey '16

按照下面的网址,它会工作:stackoverflow.com/questions/48922266/...
文卡塔斯摹

Answers:


126

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


13
您是否尝试过发现不起作用?还是您认为解决方案太简单了?只需扩展UITableViewController而不是UIViewController即可,只要文本字段成为第一个响应者,包含文本字段的单元格就会在键盘上方滚动。无需额外的代码。
Sam Ho,2010年

3
是的,但是特别是在iPad上,我们需要一种不涉及UITableViewController的方法来执行此操作。
Bob Spryn 2011年

13
要澄清一下,说每次使用表格视图都需要全屏显示(特别是在iPad上)是不合理的答案。有成千上万个不这样做的优秀应用程序示例。例如,苹果公司自己的许多公司,包括iPad上的“联系人”应用程序。
Bob Spryn 2011年

32
如果您覆盖[super viewWillAppear:YES],它将无法正常工作。除此之外,它应该工作。
拉巴蒂诺2014年

18
如果您重写了viewWillAppear:(BOOL)动画,请不要忘记调用[super viewWillAppear:animated]; :)
Médéric酒店佩蒂特

93

进行滚动的功能可能要简单得多:

- (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];
}

而已。完全没有计算。


2
那么为何不?!只需将UITableViewScrollPositionTop替换为UITableViewScrollPositionMiddle。当然,您只需要重新缩放UITableView即可调整可见区域。
Mihai Damian 2010年

3
如果在显示键盘时UITableViewController已经完成了表视图大小的调整,这似乎不起作用:控制器使用来减小可见尺寸contentInset,显然在请求visibleRows或时未考虑在内indexPathsForVisibleRows
朱利安D.12年

16
对于表格视图的最后几行无效。键盘仍会遮盖所有无法在键盘上方滚动的行。
Alex Zavatone 2014年

3
若要使自动滚动行为在表格的最后几行上起作用,请检测何时开始编辑这些行,并在表格视图的末尾添加页脚,并以一定高度的空白视图添加空白。这将允许tableview将单元格滚动到正确的位置。
2014年

10
通过一连串的superview调用进入单元是不可靠的,除非您确定自己实际上已经进入单元。参见stackoverflow.com/a/17757851/1371070stackoverflow.com/a/17758021/1371070
Cezar 2014年

70

我正在做与通用类非常相似的事情,无需为您的代码计算特定的东西。只需检查代码中的注释即可:

在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()
        }
    }
}

在结合设备方向的同时使用通知并获取键盘高度真是太棒了,谢谢!滚动部分由于某种原因对我不起作用,因此我不得不使用此选项:[tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
taber

7
我认为这是最好的答案。很干净。只有两件事:1)您的viewDidLoad不调用[super viewDidLoad],以及2)我必须在frame.size.height行上进行一些选项卡式数学运算。否则完美!谢谢。
toxaq 2010年

3
这是toxaq描述的修改:MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; 在任何使用键盘高度的地方,从键盘高度中减去tabBarHeight。
史蒂夫·N 2010年

1
如果用户点击文本框,则可以完美工作。但是,如果用户在不按回车键的情况下点击另一个文本字段,则会减小表格视图的大小。
Bhavin Ramani

1
@BhavinRamani同意。我添加了一个简单的布尔属性,以记住键盘是否已经显示,并在不必要时跳过代码重新执行。
肮脏的亨利

46

基于BartłomiejSemańczyk解决方案的Swift 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)
    })
}

较小的细节...使用Notification而不是NSNotification“ Swift 3-y” :-)
Nicolas Miari

如果有导航栏,这将有助于重新定位-如果让let环绕UIView.animate-如果让frame = self.navigationController?.navigationBar.frame {let y = frame.size.height + frame.origin.y}
肖恩·德夫

当旋转发生时,加载中会出现小故障,手动滚动tableview时某些单元会消失
jothikenpachi

好的解决方案,谢谢!注意-不再需要执行removeObserver。
尼克·麦康奈尔

44

我有同样的问题,但注意到它仅在一种视图中出现。因此,我开始寻找控制器中的差异。

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

因此,请务必像这样实现:

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

而且无论您使用UIViewController还是UITableViewController; 通过将a UITableView作为self.view的子视图放入来检查它UIViewController。这是相同的行为。如果呼叫[super viewWillAppear:animated];丢失,则该视图不允许滚动。


1
这非常好。我想知道为什么人们说UITableView会帮我解决这个问题。谢谢!
olivaresF

5
我也有这个问题,这个答案应该使它达到最高!
艾米尔·马丁

我浪费了很多时间试图自己解决...谢谢;)
budidino 2014年

+1开始哭了一点,我有那条线,但也需要[tableViewController viewWillAppear:animated]; 因为我要向UIViewController添加UITableViewController。没有更多的眼泪了:)
科林·拉马尔2014年

41

我可能错过了这一点,因为我在这里没有阅读整篇文章,但是我想到的似乎很简单。我没有通过绞拧器,在所有情况下进行测试,但似乎应该可以正常工作。

只需通过键盘的高度调整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;
    }];
}

这太简单了吗?我想念什么吗?到目前为止,它对我来说还不错,但是正如我所说的,我还没有通过绞拧器来解决...


IMO,这是最好的解决方案。我唯一要更改的是您的硬编码有效期[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Andy

这很简单。但是我发现的一个问题是,它不会使更改生效,也不会contentInset急剧改变滚动范围。
极客

这对我来说是最好的,但是有一些问题。1)我不知道在哪里可以找到“ currentField.indexPath”,因此我不得不将indexPath.row保存为字段的标记,并在以后创建indexPath。2)不适用于表格顶部的行,而是将其滚动到屏幕之外。必须添加一些代码才能仅在currentField的indexPath大于屏幕上可以容纳的范围时滚动。3)如果使用iPad,则必须在iPad上使用kbSize.Width(而不是高度)
Travis M.

抱歉,我们已经习惯了自己的代码,有时会忘记,是吗?currentField是当前的文本字段我的工作,并indexPath是一个扩展,我加入到类,只是增加了一个NSIndexPath,所以我知道这小区在不在。
mickm

这是一种方法,不要只修改表属性就移动框架。
Nextorlg

35

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

首先,在您的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数组和一个跟踪它们的枚举,因此将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];
}

这似乎工作得很好。


不错的解决方案。感谢您发布。
亚历克斯·雷诺兹

2
UIKeyboardBoundsUserInfoKey从iOS 3.2开始不推荐使用。请参阅下面的解决方案,该解决方案适用于所有当前版本≥3.0的iOS。/ @ iPhoneDev
Ortwin Gentz 2010年

这比需要的要复杂。@ user91083的答案很简单并且有效。
理查德·布莱威尔

1
这个解决方案有一个小问题。keyboardWillShow称为AFTER textFieldDidBeginEditing,所以当我们要滚动到某个单元格时,tableView的框架尚未更改,因此无法正常工作
HiveHicks 2012年

35

如果可以使用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中完整代码。要对其进行测试,请使用“完整列表”子窗格,在其中可以测试上述方案。


我不知道使用字符串代替常量是否有用,因为如果Apple由于某些原因而提出在内部更改String的想法,那么您的解决方案将无法正常工作。同样,当它被弃用时,您也不会收到警告。我认为

@iPortable:这不理想,我知道。您能建议一个更好的解决方案在所有版本≥3.0上运行吗?
Ortwin Gentz 2010年

1
像魅力一样工作,但不适用于UIInterfaceOrientationPortraitUpsideDown。然后,还必须以上下颠倒为基础来计算高度的减少量:
克拉斯

这在我的iPad和Simulator(4.3)上具有非常明显的视觉故障。使用太明显了。:(
Bob Spryn 2011年

我喜欢这种解决方案将占屏幕底部的工具栏。
pdemarest

24

Swift最简单的解决方案:

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 3版本:stackoverflow.com/a/41040630/1064438
squall2022

曾经见过的超级完美解决方案,我尝试了其他解决方案,但存在一些问题。您的解决方案在ios 10.2上完美工作。
Wangdu Lin

8

我希望你们已经有了阅读所有这些内容的解决方案。但是我发现我的解决方案如下。我希望您已经有个牢房UITextField。因此在准备时,只需将行索引保留在文本字段的标签中即可。

cell.textField.tag = IndexPath.row;

创建一个具有全局范围的activeTextField实例,UITextField如下所示:

@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的键盘将不再隐藏您。


6

合并并填写几个答案(特别是Ortwin Gentz,用户98013)和另一篇文章的空白,对于iPad 4.3在纵向或横向模式下的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 4.x中很好地使用了此代码,但是在iOS5中,它在scrollToOldPosition中崩溃了,因为_topmostRowBeforeKeyboardWasShown当时已经被释放。不确定解决方案是什么。大概记住索引而不是对象。
Thomas Tempelmann 2011年

5

如果您使用uitableview放置文本字段(来自Jeff Lamarche),则可以像这样使用委托方法滚动表视图。

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

- (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];
    }

您不更新tableView框架。然后,显示键盘时,滚动条和滚动行为是错误的。请参阅我的解决方案。
Ortwin Gentz 2010年

5

键盘通知有效,但是Apple的示例代码假定滚动视图是窗口的根视图。通常情况并非如此。您必须补偿制表符栏等,以获得正确的偏移量。

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

// 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;
}

UIKeyboardCenterEndUserInfoKeyUIKeyboardBoundsUserInfoKey被弃用的iOS 3.2。请参阅下面的解决方案,该解决方案适用于所有当前版本≥3.0的iOS。
Ortwin Gentz 2010年

5

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

self.autoresizesForKeyboard = YES

这需要照顾:

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

做完了。


Three20在这里是什么?您可以指定吗?
Mubin购物中心

5

我的方法:

我首先子类化UITextField并添加一个indexPath属性。在cellFor ...方法中,我移交了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 ... etc。

当键盘消失时,您必须使用以下方法将其反转:

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

4

更简化的解决方案。它包含在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;

}


4

使用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
}

物镜

- (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上工作的问题。我的UITextFields连接到UITableViewCell。如果我在UIViewController中实现此代码,则无法访问UITextFields。有任何想法吗?
Vetuka '17

4

Swift 4.2完整解决方案

我已经用协议集创建了GIST该可以简化显示,隐藏或更改键盘时增加额外空间的工作。

特点

  • 正确处理键盘框架更改(例如,键盘高度更改,例如emojii→普通键盘)。
  • TabBar和ToolBar支持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. 打电话addKeyboardFrameChangesObserver()viewWillAppear()或类似的方法。
  2. 打电话removeKeyboardFrameChangesObserver()viewWillDisappear()或类似的方法。

实现:滚动视图

ModifableInsetsOnKeyboardFrameChanges协议增加了UIScrollView对核心协议的支持。更改键盘框架时,它将更改滚动视图的插图。

您的班级需要设置滚动视图,更改键盘框架时会增加或减少其插图。

var scrollViewToModify: UIScrollView { get }

3

由于表中有文本字段,所以最好的方法实际上是调整表的大小-您需要将tableView.frame的高度设置为小于键盘的大小(我认为约为165像素),然后在出现以下情况时再次对其进行扩展键盘被关闭。

如果您不希望用户滚动,也可以选择同时禁用tableView的用户交互。


我第二次,并注册UIKeyboardWillShowNotification来动态查找键盘的大小。
benzado

通知对象返回的数字虽然无效。或至少不是2.2,返回的数字不正确,我不得不对165值进行硬编码以正确调整高度(它偏离了5到10像素)
Kendall Helmstetter Gelner,2009年

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

您为什么要针对每个文本字段使用特殊情况?从单元格的NSIndexPath中标识每个文本字段,并将该if语句更改为两行代码。您确实需要一个cellForRowAtIndexPath调用,然后从该单元格获取textField。
Alex Zavatone 2014年

实际上,考虑到这种情况在iOS上是多么令人难以置信,我认为可以为这种情况编写“完全解开,荒谬的文字”代码。
Fattie

考虑这个答案是在6年前。
WrightsCS '16

2

我尝试了几乎相同的方法,并针对相同的问题提出了更简单,更小的代码。我创建了一个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];

    }

2

因此,经过数小时的辛苦工作,尝试使用这些当前的解决方案(并且完全失败了),我终于使事情运转良好,并对其进行了更新,以使用新的动画块。我的答案完全基于上述Ortwin的答案

因此,无论出于何种原因,上面的代码对我都不起作用。我的设置似乎与其他设置非常相似,但是可能是因为我使用的是iPad或4.3。它正在做一些古怪的数学运算,并在屏幕外拍摄我的tableview。

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

因此,我沿用了Ortwin所做的要点,但是改变了它在做一些数学运算的方法,以将我的表格视图的origin.y和size.height与键盘的高度相加。当我从该结果中减去窗口的高度时,它告诉我正在进行多少交叉。如果其大于0(又有一些重叠),则执行帧高度的动画。

此外,还存在一些重绘问题,这些问题可以通过以下方法解决:1)等待滚动到单元格直到动画完成,以及2)隐藏键盘时使用UIViewAnimationOptionBeginFromCurrentState选项。

需要注意的几件事。

  • _topmostRowBeforeKeyboardWasShown和_originalFrame是在标头中声明的实例变量。
  • self.guestEntryTableView是我的tableView(我在外部文件中)
  • IASKCGRectSwap是Ortwin的翻转框架坐标的方法
  • 我只会在显示至少50px的情况下更新tableView的高度
  • 因为我不在UIViewController中,所以没有self.view,所以我只是将tableView返回其原始框架

再说一次,如果我的奥尔特温没有提供答案的话,我将不会接近这个答案。这是代码:

- (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;
}

添加了我的FixOriginRotation函数,该函数在更新视图框架之前修复视图的坐标系等。我认为这就是为什么我一开始遇到麻烦的原因。尚不知道设备随设备旋转的iOS窗口坐标系!
Bob Spryn 2011年

2

此解决方案对我有用,请注意

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

2

非常有趣的讨论线程,我也遇到过同样的问题,可能更糟的一个,因为

  1. 我使用的是自定义单元格,而文本字段位于其中。
  2. 我必须使用UIViewController来满足我的要求,所以不能利用UITableViewController。
  3. 我的表格单元格中有过滤/排序标准,即您的单元格不断在变化并跟踪索引路径,而所有这些都无济于事。

因此,请阅读此处的主题并实现我的版本,这有助于我以横向模式在iPad中推送内容。这是代码(这并非万无一失,但它解决了我的问题)首先,您需要在自定义单元格类中有一个委托,该类在编辑开始时将文本字段发送给ur viewcontroller并在其中设置activefield = TheTextField

//仅适用于处理风景模式

- (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


2

在我介绍了通过Google和Stack Overflow找到的大量解决方案之后,我自己解决了这个问题。

首先,请确保已设置UIScrollView的IBOutlet,然后仔细查看Apple Doc:Keyboard Management。最后,如果您可以滚动背景,但键盘仍覆盖“文本字段”,请查看以下代码:

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

这件作品与Apple作品之间的主要区别在于if条件。我相信苹果的滚动距离计算和键盘覆盖的文本字段是否不正确的条件,因此我进行了上述修改。

让我知道是否有效


2

Swift中的示例,使用带有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)
}

1

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

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

1

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

有关示例,请参阅苹果示例代码项目:TaggedLocations。

您可以看到它会自动滚动,但是似乎没有任何代码可以执行此操作。该项目还具有自定义的表格视图单元格,因此,如果以此为指导构建应用程序,则应获得所需的结果。


1

这是我的工作方式,它是Sam Ho和Marcel W的答案的结合,以及我对糟糕的代码所做的一些错误修复。我正在使用UITableViewController。现在,显示键盘时,表格可以正确调整大小。

1)在viewDidLoad我添加:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2)我忘了superviewWillAppear和中调用等效项awakeFromNib。我重新添加了这些。


1

UITableViewController确实会自动进行滚动。与使用a相比,不同之处UIViewController在于NavigationController,当使用时,您必须使用来以编程方式创建Navbar-Buttonitems TableViewController

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.