【问题标题】:Making a UITableView scroll when text field is selected选择文本字段时使 UITableView 滚动
【发布时间】:2010-10-10 07:12:11
【问题描述】:

经过大量的反复试验,我放弃并提出这个问题。我见过很多人有类似的问题,但无法得到所有正确的答案。

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

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

我已经看到很多关于更改视图大小等的答案......但到目前为止,它们都没有很好地工作。

谁能用一个具体的代码示例阐明“正确”的做法?

【问题讨论】:

标签: ios iphone uitableview keyboard scroll


【解决方案1】:

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

【讨论】:

  • 您试过后发现没有用吗?还是解决方案太简单以至于您无法相信?只需扩展 UITableViewController 而不是 UIViewController ,只要文本字段成为第一响应者,包含文本字段的单元格就会在键盘上方滚动。不需要额外的代码。
  • 是的,但尤其是在 iPad 上,我们需要一种不涉及 UITableViewController 的方法来做到这一点。
  • 澄清一下,说每次使用 tableview 都需要全屏显示,尤其是在 iPad 上,这不是一个合理的答案。有大量优秀应用程序的示例不这样做。例如,Apple 自己的许多应用程序,包括 iPad 上的联系人应用程序。
  • 如果你覆盖 [super viewWillAppear:YES] 它将不起作用。除此之外,它应该可以工作。
  • 如果你覆盖 viewWillAppear:(BOOL)animated,别忘了调用 [super viewWillAppear:animated]; :)
【解决方案2】:

滚动的功能可以简单得多:

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

就是这样。根本没有计算。

【讨论】:

  • 为什么不呢?!只需将 UITableViewScrollPositionTop 替换为 UITableViewScrollPositionMiddle 即可。当然,您只需要重新缩放 UITableView 即可调整可见区域。
  • 如果在显示键盘时 UITableViewController 已经处理了表格视图调整大小,则似乎不起作用:控制器使用 contentInset 减小可见大小,显然没有考虑到这一点询问visibleRowsindexPathsForVisibleRows 时。
  • 不适用于表格视图的最后几行。键盘仍然会遮挡所有无法在键盘上方滚动的行。
  • 为了让自动滚动行为在表格的最后几行上起作用,检测这些行何时开始编辑,并在表格视图的末尾添加一个页脚,其中包含某个空白视图高度。这将允许 tableview 将单元​​格滚动到正确的位置。
  • 通过对 superview 的一连串调用到达单元格是不可靠的,除非您确保您确实到达了单元格。见stackoverflow.com/a/17757851/1371070stackoverflow.com/a/17758021/1371070
【解决方案3】:

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

在 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];
  • 这是我认为最好的答案。很干净。只有两件事:1)您的 viewDidLoad 没有调用 [super viewDidLoad] 和 2)我必须在 frame.size.height 行上进行一些标签栏数学运算。否则完美!谢谢。
  • 这里是 toxaq 描述的修改:MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; 然后在使用键盘高度的任何地方从键盘高度中减去 tabBarHeight。
  • 如果用户点击文本字段,它的工作完美。但是如果用户在没有按返回键的情况下点击另一个文本字段,那么它会减小 tableview 的大小。
  • @BhavinRamani 同意了。我添加了一个简单的布尔属性来记住键盘是否已经显示,并在不需要时跳过代码重新执行。
【解决方案4】:

Swift 3最简单的解决方案,基于Bartłomiej Semańczyk solution

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”:-)
  • 如果有导航栏,这将有助于重新定位 -- 用这个包围 UIView.animate if let -- if let frame = self.navigationController?.navigationBar.frame { let y = frame.size.高度 + frame.origin.y }
  • 当发生旋转时,加载会出现故障,并且当手动滚动 tableview 时某些单元格会消失
  • 很好的解决方案,谢谢!注意 - 不再需要 removeObserver。
【解决方案5】:

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

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

所以一定要这样实现:

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

而且不管你用UIViewController还是UITableViewController;通过将UITableView 作为self.view 的子视图放入UIViewController 来检查它。这是同样的行为。如果调用 [super viewWillAppear:animated]; 丢失,则视图不允许滚动。

【讨论】:

  • 这非常有效。我想知道为什么人们说 UITableView 会为我做这件事,这解决了它。谢谢!
  • 我也遇到过这个问题,这个答案应该能顶!
  • 我花了很多时间试图自己解决这个问题...谢谢 ;)
  • +1 开始有点哭了,我有那行但也需要 [tableViewController viewWillAppear:animated];因为我将 UITableViewController 添加到 UIViewController。没有更多的眼泪:)
【解决方案6】:

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

只需通过键盘高度调整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]
  • 这很简单。但我发现的一个问题是它不会为contentInset 中的变化设置动画并急剧改变滚动边界。
  • 这个对我来说效果最好,但是,有一些问题。 1)我不知道你在哪里可以得到“currentField.indexPath”,所以我不得不将 indexPath.row 保存为字段的标签,然后再创建 indexPath。 2) 不适用于表格顶部的行,它会将它们滚动到屏幕外。如果 currentField 的 indexPath 大于屏幕上可以容纳的值,则必须添加一些代码才能滚动。 3) 如果 iPad 是横向的,则必须在 iPad 上使用 kbSize.Width(而不是高度)
  • 抱歉,我们已经习惯了自己的代码,以至于有时会忘记,是吗? currentField 是我正在使用的当前文本字段,indexPath 是我添加到类的扩展,它只是添加了一个 NSIndexPath,所以我知道这是在哪个单元格中。
  • 这是要走的路,不移动框架只是修改表格属性。
【解决方案7】:

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

首先,在你的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];
    }
}

现在这里是滚动位,我们首先计算出几个尺寸,然后我们看到我们在可见区域中的位置,并将我们想要滚动的矩形设置为在中间上方或下方的半视图文本字段基于它在视图中的位置。在这种情况下,我们有一个 UITextField 数组和一个跟踪它们的枚举,因此将 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];
}

这似乎工作得很好。

【讨论】:

  • 不错的解决方案。感谢您发布它。
  • UIKeyboardBoundsUserInfoKey 自 iOS 3.2 起已弃用。请参阅下面的适用于所有当前 iOS 版本≥ 3.0 的解决方案。 /@iPhoneDev
  • 这比它需要的要复杂。 @user91083 的回答很简单而且很有效。
  • 这个解决方案有个小问题。 keyboardWillShow 是在 textFieldDidBeginEditing 之后调用的,所以当我们想滚动到某个单元格时,tableView 的框架还没有改变,所以它不会工作
【解决方案8】:

如果您可以使用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 中的 full code of the class。要对其进行测试,请使用“完整列表”子窗格,您可以在其中测试上述场景。

【讨论】:

  • 我不知道使用字符串而不是常量是否有用,因为如果 Apple 出于某些原因在内部提出更改字符串的想法,那么您的解决方案将不再有效。同样,当它被弃用时,您也没有收到警告。我认为
  • @iPortable:我知道这并不理想。您能否提出一个在所有 ≥3.0 版本上运行的更好的解决方案?
  • 像魅力一样工作,但不适用于 UIInterfaceOrientationPortraitUpsideDown。然后高度减少的计算也必须倒置: CGFloat reduceHeight = keyboardRect.size.height - ( CGRectGetMinY(viewRectAbsolute) - CGRectGetMinY(windowRect));
  • 这在我的 iPad 和模拟器 (4.3) 上有非常明显的视觉故障。使用起来太显眼了。 :(
  • 我喜欢这个解决方案将在屏幕底部显示一个工具栏。
【解决方案9】:

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

【讨论】:

  • 完美运行,只需最少的计算。我添加了一些代码来恢复表格插图以完成此答案。
  • 最佳解决方案,谢谢。我在这里发布了 Swift 3 版本:stackoverflow.com/a/41040630/1064438
  • 见过的超级完美的解决方案,我试过其他的,但有一些问题。您的解决方案在 ios 10.2 上完美运行。
【解决方案10】:

我希望你们在阅读所有这些后已经得到了解决方案。但我发现我的解决方案如下。我期待您已经拥有一个带有UITextField 的单元格。因此,在准备时,只需将行索引保留在文本字段的标签中即可。

cell.textField.tag = IndexPath.row;

创建一个activeTextFieldUITextField的实例,全局范围如下:

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

【讨论】:

    【解决方案11】:

    结合并填写几个答案(特别是 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 4.x 中使用此代码很好,但在 iOS5 中它在 scrollToOldPosition 中崩溃,因为 _topmostRowBeforeKeyboardWasShown 那时已经被释放。不确定解决方案是什么。可能记得索引而不是对象。
    【解决方案12】:

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

    (注意:我的文本字段存储在一个数组中,其索引与 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];
        }
    

    【讨论】:

    • 你没有更新 tableView 框架。然后,当显示键盘时,滚动条和滚动行为是错误的。查看我的解决方案。
    【解决方案13】:

    键盘通知有效,但 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 版本。
    【解决方案14】:

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

    self.autoresizesForKeyboard = YES
    

    这会处理:

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

    做完了。

    【讨论】:

    • 这里的 Three20 是什么?你能具体说明一下吗?
    【解决方案15】:

    我的做法:

    我首先继承 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...等

    当键盘消失时,您必须将其反转:

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

    【讨论】:

      【解决方案16】:

      Swift 4.2 完整解决方案

      我创建了GIST with set of protocols,它通过在显示、隐藏或更改键盘时添加额外空间来简化工作。

      特点

      • 适用于键盘框架更改(例如,键盘高度更改,如 emojii → 普通键盘)。
      • 对 UITableView 示例的 TabBar 和 ToolBar 支持(在其他示例中,您收到不正确的插图)。
      • 动态动画持续时间(非硬编码)。
      • 面向协议的方法,可以根据您的目的轻松修改。

      用法

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

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

      核心:帧变化观察者

      Protocol KeyboardChangeFrameObserver 将在每次更改键盘框架时触发事件(包括显示、隐藏、框架更改)。

      1. 通过viewWillAppear() 或类似方法致电addKeyboardFrameChangesObserver()
      2. 拨打removeKeyboardFrameChangesObserver()viewWillDisappear()或类似方法。

      实现:滚动视图

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

      你的班级需要设置滚动视图,一个人的插图会随着键盘框架的变化而增加/减少。

      var scrollViewToModify: UIScrollView { get }
      

      【讨论】:

        【解决方案17】:

        更精简的解决方案。它滑入 UITextField 委托方法,因此不需要使用 UIKeyboard 通知进行混乱。

        实施说明:

        kSettingsRowHeight -- UITableViewCell 的高度。

        offsetTarget 和 offsetThreshold 与 kSettingsRowHeight 无关。如果您使用不同的行高,请将这些值设置为点的 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;
        

        }

        【讨论】:

          【解决方案18】:

          使用UITextField'sdelegate方法:

          斯威夫特

          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
          }
          

          目标-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 上工作时遇到问题。我的 UITextFields 连接到 UITableViewCell。如果我在 UIViewController 中实现此代码,我将无法访问 UITextFields。有什么想法吗?
          【解决方案19】:

          由于您在表格中有文本字段,因此最好的方法是调整表格的大小 - 您需要将 tableView.frame 的高度设置为按键盘的大小缩小(我认为大约 165 像素),然后展开键盘关闭时再次输入。

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

          【讨论】:

          • 我秒这个,注册UIKeyboardWillShowNotification动态查找键盘大小。
          • 通知对象返回的数字不起作用。或者至少在 2.2 中没有,返回的数字不正确,我必须硬编码 165 值才能正确调整高度(它偏离了 5 到 10 个像素)
          【解决方案20】:

          这很完美,在 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 语句更改为 2 行代码。你真的想要一个 cellForRowAtIndexPath 调用,然后从单元格中获取 textField。
          • 实际上考虑到这种情况在 iOS 上是多么令人难以置信,我认为为这种情况编写“完全展开,可笑的文字”代码是可以的。
          • 考虑到这个答案是 6 年前给出的。
          【解决方案21】:

          我尝试了几乎相同的方法,并为此想出了一个更简单、更小的代码。 我创建了一个 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];
          
              }
          

          【讨论】:

            【解决方案22】:

            因此,在尝试使用这些当前解决方案(并且完全失败)进行了数小时的艰苦工作后,我终于让一切运行良好,并更新了它们以使用新的动画块。我的回答完全基于Ortwin's answer above

            因此,无论出于何种原因,上面的代码都不适合我。我的设置似乎与其他人非常相似,但可能是因为我使用的是 iPad 或 4.3……不知道。它正在做一些古怪的数学运算,并将我的桌面视图从屏幕上射出。

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

            所以我遵循 Ortwin 所做的要点,但改变了它做一些数学运算的方式,以将我的表格视图的 origin.y 和 size.height 与键盘的高度相加。当我从结果中减去窗口的高度时,它会告诉我有多少交叉点。如果它大于 0(也就是有一些重叠),我会执行帧高度的动画。

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

            有几点需要注意。

            • _topmostRowBeforeKeyboardWasShown & _originalFrame 是在标头中声明的实例变量。
            • self.guestEntryTableView 是我的 tableView(我在外部文件中)
            • IASKCGRectSwap 是 Ortwin 用于翻转帧坐标的方法
            • 如果至少要显示 50 像素,我只会更新 tableView 的高度
            • 由于我不在 UIViewController 中,我没有 self.view,所以我只是将 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;
            }
            

            【讨论】:

            • 添加了我的 FixOriginRotation 函数,该函数在更新视图的框架等之前修复视图的坐标系。我认为这就是我一开始遇到麻烦的部分原因。不知道 iOS 窗口坐标系随设备旋转!
            【解决方案23】:

            这个解决方案对我有用,请注意这一行

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

            【讨论】:

              【解决方案24】:

              非常有趣的讨论帖,我也遇到过同样的问题,可能会更糟,因为

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

              因此,请阅读此处的主题并实现我的版本,这有助于我在 iPad 中以 landscape 模式推送我的内容。 这是代码(这不是万无一失的,但它解决了我的问题) 首先你需要在你的自定义单元格类中有一个委托,它在编辑开始时将文本字段发送到你的视图控制器并在那里设置 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

              【讨论】:

                【解决方案25】:

                在参考了通过 Google 和 Stack Overflow 找到的大量解决方案后,我自己解决了这样一个问题。

                首先,请确保您已经设置了 UIScrollView 的 IBOutlet, 那么请仔细查看Apple Doc: Keyboard Management。 最后,如果你可以滚动背景,但键盘仍然覆盖Text Fields,请看一下这段代码:

                // 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 条件。我认为苹果计算的滚动距离和键盘是否覆盖文本字段的条件不准确,所以我按照上面的方式进行了修改。

                让我知道它是否有效

                【讨论】:

                  【解决方案26】:

                  Swift 中的示例,使用来自Get indexPath of UITextField in UITableViewCell with Swift 的文本字段的精确点:

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

                  【讨论】:

                    【解决方案27】:

                    Swift 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)
                        }
                    }
                    

                    【讨论】:

                      【解决方案28】:

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

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

                      【讨论】:

                        【解决方案29】:

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

                        有关一个很好的示例,请参阅苹果示例代码项目:TaggedLocations。

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

                        【讨论】:

                          【解决方案30】:

                          以下是我完成这项工作的方法,其中混合了 Sam Ho 和 Marcel W 的答案,以及我自己对我糟糕的代码进行的一些错误修复。我正在使用 UITableViewController。现在,当显示键盘时,表格可以正确调整大小。

                          1) 在viewDidLoad 我添加了:

                          self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

                          2) 我忘记在viewWillAppearawakeFromNib 中调用super 等效项。我把这些加回去了。

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2019-06-23
                            • 2013-05-14
                            • 2020-12-13
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多