ios 在常规 UIViewController 上的 UITableViewCell 中的键盘上方滚动 UITextField

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15036519/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 22:24:14  来源:igfitidea点击:

Scroll UITextField above Keyboard in a UITableViewCell on a regular UIViewController

iosios4

提问by Matt Hudson

I have tried most of the examples here on StackOverflow. I also used Apple's. The problem I seem to have with them is that they don't account for the UITextField being in a UITableView. I've done this many times, but not in this way. I have a custom UITableViewCell with a UITextField in it.

我已经尝试了 StackOverflow 上的大部分示例。我也用过苹果的。我似乎对他们的问题是他们没有考虑 UITableView 中的 UITextField。我已经这样做了很多次,但不是这样。我有一个带有 UITextField 的自定义 UITableViewCell。

On my UITableView (which is NOT a UITableViewController), I need to be able to avoid hiding the UITextField under the UITableView.

在我的 UITableView(不是 UITableViewController)上,我需要能够避免在 UITableView 下隐藏 UITextField。

I have this as of now:

我现在有这个:

-(void)viewDidLoad
{
....
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

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

....
}

- (void)scrollToRectOfTextField {
    UITableViewCell *cell = (UITableViewCell*)[self.activeTextField superview];
    CGRect r = CGRectMake(self.activeTextField.frame.origin.x,
                          cell.frame.origin.y+self.activeTextField.frame.origin.y,
                          self.activeTextField.frame.size.width,
                          self.activeTextField.frame.size.height);
    [self.tableView scrollRectToVisible:r animated:YES];
}

- (void)keyboardDidShow:(NSNotification *)notification
{
    if (UI_USER_INTERFACE_IDIOM()== UIUserInterfaceIdiomPhone) {
        NSDictionary *userInfo = [notification userInfo];
        CGSize size = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
        NSLog(@"TableView: %@", NSStringFromCGRect(self.tableView.frame));
        CGRect newTableViewFrame = CGRectMake(self.tableView.frame.origin.x,
                                              self.tableView.frame.origin.y,
                                              self.tableView.frame.size.width, self.tableView.frame.size.height - size.height);
        self.tableView.frame = newTableViewFrame;
        NSLog(@"New TableView: %@", NSStringFromCGRect(self.tableView.frame));


        self.tableView.contentSize = CGSizeMake(self.tableView.contentSize.width, self.tableView.contentSize.height-size.height);
    }
}


- (void)keyboardWillHide:(NSNotification *)notification
{
    NSDictionary *userInfo = [notification userInfo];
    CGSize size = [[userInfo objectForKey: UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
    self.tableView.frame = CGRectMake(self.tableView.frame.origin.x,
                                      self.tableView.frame.origin.y,
                                      self.tableView.frame.size.width,
                                      self.tableView.frame.size.height + size.height);

    NSLog(@"%@", NSStringFromCGRect(self.tableView.frame));
}


-(void)textFieldDidBeginEditing
{
     [self scrollToRectOfTextField];
}

This seems to push a bunch of white space up past my keyboard. Please also note that I have an inputAccessoryView on my UITextField as well.

这似乎在我的键盘上推了一堆空白。另请注意,我的 UITextField 上也有一个 inputAccessoryView。

Oh, and I cannot use any third party classes. I love TPKeyboardAvoidingScrollView, but I cannot use anything third party.

哦,我不能使用任何第三方类。我喜欢 TPKeyboardAvoidingScrollView,但我不能使用任何第三方。

回答by Matt Hudson

I spent all day trying to figure this out. I posted it here, then found a blog link and an incredibly simple solution. It looks like this:

我花了一整天的时间试图弄清楚这一点。我把它贴在这里,然后找到了一个博客链接和一个非常简单的解决方案。它看起来像这样:

-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    CGPoint pointInTable = [textField.superview convertPoint:textField.frame.origin toView:self.tableView];
    CGPoint contentOffset = self.tableView.contentOffset;

    contentOffset.y = (pointInTable.y - textField.inputAccessoryView.frame.size.height);

    NSLog(@"contentOffset is: %@", NSStringFromCGPoint(contentOffset));

    [self.tableView setContentOffset:contentOffset animated:YES];

    return YES;
}


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

    if ([textField.superview.superview isKindOfClass:[UITableViewCell class]])
    {
        UITableViewCell *cell = (UITableViewCell*)textField.superview.superview;
        NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

        [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:TRUE];
    }

    return YES;
}

Check this for iOS 8

在 iOS 8 上检查这个

回答by Salman Hasrat Khan

I tried the link that @inturbidus posted for iOS8 but unfortunately it did not work for me. After a bit of digging, it turns out that Apple does have sample code on their website to make this work naturally as they do in the UITableViewController. Here's the Apple link for the Objective-C version and I'm also adding the swift version here.

我尝试了@inturbidus 为 iOS8 发布的链接,但不幸的是它对我不起作用。经过一番挖掘,事实证明 Apple 在他们的网站上确实有示例代码,可以像在 UITableViewController 中那样自然地完成这项工作。这是 Objective-C 版本的 Apple 链接,我也在此处添加了 swift 版本。

Objective-C link from Apple (Scroll to listing 5-1):https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html

Apple 的 Objective-C 链接(滚动到清单 5-1):https : //developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html

Swift adaptation of the Objective-C Version

Objective-C 版本的 Swift 适配

var activeField: UITextField?

func textFieldDidBeginEditing(textField: UITextField) {
    self.activeField = textField
}

func textFieldDidEndEditing(textField: UITextField) {
    self.activeField = nil
}

func registerForKeyboardNotifications() {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
}    

func keyboardWasShown(aNotification: NSNotification) {
    let info = aNotification.userInfo as! [String: AnyObject],
    kbSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue().size,
    contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: kbSize.height, right: 0)

    self.tableView.contentInset = contentInsets
    self.tableView.scrollIndicatorInsets = contentInsets

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

    if !CGRectContainsPoint(aRect, activeField!.frame.origin) {
        self.tableView.scrollRectToVisible(activeField!.frame, animated: true)
    }
}

func keyboardWillBeHidden(aNotification: NSNotification) {
    let contentInsets = UIEdgeInsetsZero
    self.tableView.contentInset = contentInsets
    self.tableView.scrollIndicatorInsets = contentInsets
}

回答by Victor

I made a mix with the answers of Mattand also Salman. This works for many textfields in a tableView. Swift 3

我混合了MattSalman的答案。这适用于 tableView 中的许多文本字段。斯威夫特 3

Basically is get the BOTTOM Ypoint of the textInput touched. Once we have that I check if the keyboard cover the textInput and if do it I would change the contentOffset of the tableView

基本上是获取触摸的 textInput的BOTTOM Y点。一旦我们有了它,我就会检查键盘是否覆盖了 textInput,如果这样做,我将更改 tableView 的 contentOffset

First register to the notifications. In the init if is a UIView or in viewDidLoad if is a UIViewController:

首先注册通知。在 init 如果是 UIView 或在 viewDidLoad 如果是 UIViewController:

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

Then set up the textfield touched as current input

然后将触摸的文本字段设置为当前输入

var inputActive: UITextField!

func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
    inputActive = textInput
    return true
}

Finally implement the notifications method:

最后实现通知方法:

func keyboardWillShow(notification: NSNotification) {
    var userInfo = notification.userInfo!
    if let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        // Get my height size
        let myheight = tableView.frame.height
        // Get the top Y point where the keyboard will finish on the view
        let keyboardEndPoint = myheight - keyboardFrame.height
        // Get the the bottom Y point of the textInput and transform it to the currentView coordinates.
        if let pointInTable = inputActive.superview?.convert(inputActive.frame.origin, to: tableView) {
            let textFieldBottomPoint = pointInTable.y + inputActive.frame.size.height + 20
            // Finally check if the keyboard will cover the textInput
            if keyboardEndPoint <= textFieldBottomPoint {
                tableView.contentOffset.y = textFieldBottomPoint - keyboardEndPoint
            } else {
                tableView.contentOffset.y = 0
            }
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    tableView.contentOffset.y = 0
}

Few things to add. The paddingis some extra distance that I add in my case was 20 points. Also in my case the UITableView was added to a UIViewController but this should work also in a UITableViewController

要添加的东西很少。该填充是一些额外的距离,我在我的情况下加了20分。同样在我的情况下 UITableView 被添加到 UIViewController 但这也应该在 UITableViewController 中工作

Hope this helps!

希望这可以帮助!

回答by Nhan Lam Dai

Thank @Salman for the link.
Just notice that Apple example is used for scrollview in normal view.
In table view you must convert Point and Rect of activeField to table's coordinate, so I change some lines in function keyboardWasShown:

感谢@Salman 提供链接。
请注意,Apple 示例用于普通视图中的滚动视图。
在表格视图中,您必须将 activeField 的 Point 和 Rect 转换为表格的坐标,因此我更改了函数 keyboardWasShown 中的一些行:

func keyboardWasShown(aNotification: NSNotification) {
        let info = aNotification.userInfo as! [String: AnyObject],
        kbSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue().size,
        contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: kbSize.height, right: 0)

        self.tableBasket.contentInset = contentInsets
        self.tableBasket.scrollIndicatorInsets = contentInsets

        var aRect = self.view.frame
        aRect.size.height -= kbSize.height

        let pointInTable = activeField!.superview!.convertPoint(activeField!.frame.origin, toView: tableView)
        let rectInTable = activeField!.superview!.convertRect(activeField!.frame, toView: tableView)

        if !CGRectContainsPoint(aRect, pointInTable) {
            self.tableView.scrollRectToVisible(rectInTable, animated: true)
        }
}

And if your view has tabBarController, remember to reset contentInsets with tabBar height instead of UIEdgeInsetsZero (if not, your last bottom rows maybe hiden under tabBar):

如果你的视图有 tabBarController,记得用 tabBar 高度而不是 UIEdgeInsetsZero 重置 contentInsets(如果没有,你的最后一行可能隐藏在 tabBar 下):

func keyboardWillBeHidden(aNotification: NSNotification) {
        //UIEdgeInsetsZero is used in view without tabBar
        //let contentInsets = UIEdgeInsetsZero
        let tabBarHeight = self.tabBarController!.tabBar.frame.height
        let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: tabBarHeight, right: 0)
        self.tableView.contentInset = contentInsets
        self.tableView.scrollIndicatorInsets = contentInsets
    }
}

回答by Georgios Iniatis

This answer is for Xamarin iOS users, however someone can easily modify it to make it work with Swift or Objective C.

此答案适用于 Xamarin iOS 用户,但有人可以轻松修改它以使其适用于 Swift 或 Objective C。

The solution is very simple and has worked perfectly in my case where I needed a cell to appear on the top half of the screen in order for the cell's textfield to be above keyboard. So what I did is the following:

该解决方案非常简单,并且在我需要一个单元格出现在屏幕的上半部分以便单元格的文本字段位于键盘上方的情况下非常有效。所以我所做的是以下内容:

I implemented textField.ShouldBeginEditing the following way:

我通过以下方式实现了 textField.ShouldBeginEditing:

myTextField.ShouldBeginEditing = textField =>
{
   myTableView.ScrollToRow(indexPath, UITableViewScrollPosition.Top, true);

   return true;
};

Where:

在哪里:

myTextFieldis the cell's text field,

myTextField是单元格的文本字段,

myTableViewis the table view,

myTableView是表格视图,

indexPathis the cell's index path within the table view.

indexPath是表格视图中单元格的索引路径。

As I previously mentioned, this solution works perfectly in my case so I thought I could share it with you guys in case it works for you as well. Happy coding :)

正如我之前提到的,这个解决方案在我的情况下非常有效,所以我想我可以与你们分享它,以防它也适用于你。快乐编码:)

回答by jqgsninimo

My solution (worked on Xcode 8.3.3, Swift 3, iOS 10.3.1):

我的解决方案(适用于 Xcode 8.3.3、Swift 3、iOS 10.3.1):

class MyTableViewController: UITableViewController {

    override func viewWillAppear(_ animated: Bool) {
        NotificationCenter.default.addObserver(self, selector: #selector(actionKeyboardDidShow(with:)), name: .UIKeyboardDidShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(actionKeyboardWillHide(with:)), name: .UIKeyboardWillHide, object: nil)
    }

    override func viewDidDisappear(_ animated: Bool) {
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidShow, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
    }

    @objc private func actionKeyboardDidShow(with notification: Notification) {
        guard let userInfo = notification.userInfo as? [String: AnyObject],
            let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
            else { return }

        var contentInset = self.tableView.contentInset
        contentInset.bottom += keyboardFrame.height

        self.tableView.contentInset = contentInset
        self.tableView.scrollIndicatorInsets = contentInset
    }

    @objc private func actionKeyboardWillHide(with notification: Notification) {
        guard let userInfo = notification.userInfo as? [String: AnyObject],
            let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
            else { return }

        var contentInset = self.tableView.contentInset
        contentInset.bottom -= keyboardFrame.height

        self.tableView.contentInset = contentInset
        self.tableView.scrollIndicatorInsets = contentInset
    }

}

回答by Jayden Irwin

jqgsninimo's solution updated to 4.2. Works on iOS 12.0.

jqgsninimo的解决方案更新到 4.2。适用于 iOS 12.0。

Swift 4.2

斯威夫特 4.2

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow(with:)), name: UIResponder.keyboardDidShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(with:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidShowNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}

@objc func keyboardDidShow(with notification: Notification) {
    guard let userInfo = notification.userInfo as? [String: AnyObject],
        let keyboardFrame = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        else { return }

    var contentInset = self.tableView.contentInset
    contentInset.bottom += keyboardFrame.height

    tableView.contentInset = contentInset
    tableView.scrollIndicatorInsets = contentInset
}

@objc func keyboardWillHide(with notification: Notification) {
    guard let userInfo = notification.userInfo as? [String: AnyObject],
        let keyboardFrame = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        else { return }

    var contentInset = self.tableView.contentInset
    contentInset.bottom -= keyboardFrame.height

    tableView.contentInset = contentInset
    tableView.scrollIndicatorInsets = contentInset
}

回答by Joaquin Pereira

I did a mix between the answer of @Victor and some code that I had. I created a handy extension that can be use across many UITextField.

我在@Victor 的答案和我拥有的一些代码之间进行了混合。我创建了一个方便的扩展,可以在许多 UITextField 中使用。

1.- First we need an extension for the UITextFieldto manage the keyboard notifications

1.- 首先我们需要一个UITextField扩展来管理键盘通知

extension UITextField {

    func keepTextFieldAboveKeyboard(tableView:UITableView) {

        var willShowNotification: NSObjectProtocol?
        var willHideNotification: NSObjectProtocol?

        willShowNotification = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: OperationQueue.main) {(notification) in

            var userInfo = notification.userInfo!

            if let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
                // Get my height size
                let myheight = tableView.frame.height
                // Get the top Y point where the keyboard will finish on the view
                let keyboardEndPoint = myheight - keyboardFrame.height
                // Get the the bottom Y point of the textInput and transform it to the currentView coordinates.
                if let pointInTable = self.superview?.convert(self.frame.origin, to: tableView) {

                    let textFieldBottomPoint = pointInTable.y + self.frame.size.height + 20

                    // Finally check if the keyboard will cover the textInput
                    if keyboardEndPoint <= textFieldBottomPoint {

                        tableView.contentOffset.y = textFieldBottomPoint - keyboardEndPoint
                    } else {

                        tableView.contentOffset.y = 0
                    }
                }
            }

            NotificationCenter.default.removeObserver(willShowNotification!)
        }

        willHideNotification = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillHide, object: nil, queue: OperationQueue.main) { (notification) in

            tableView.contentOffset.y = 0

            NotificationCenter.default.removeObserver(willHideNotification!)
        }

    }

}

2.- This previous extension should be call inside textFieldShouldBeginEditingso we can have an extension for that as well

2.- 这个先前的扩展应该在textFieldShouldBeginEditing内调用,这样我们也可以有一个扩展

extension UITextField : UITextFieldDelegate {

    public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool  {

        guard let tableView = textField.parentView(of: UITableView.self) else { return true };

        textField.keepTextFieldAboveKeyboard(tableView:tableView);

        return true;

    }
}

3.- Finally as you notice in the previous method, we need the TableViewwhere the cell with the UITextField is located. So we can use this extension (Which is also useful for other purposes)

3.- 最后,正如您在前面的方法中注意到的,我们需要带有 UITextField 的单元格所在的TableView。所以我们可以使用这个扩展(这对于其他目的也很有用)

extension UIView {
    func parentView<T: UIView>(of type: T.Type) -> T? {
        guard let view = self.superview else {
            return nil
        }
        return (view as? T) ?? view.parentView(of: T.self)
    }
}

4.- Finally in the cell where the UITextField is located, we just write this simple line of code

4.- 最后在UITextField 所在单元格中,我们只需编写这行简单的代码

textField.delegate = textField;

Completely reusable and universal.

完全可重复使用和通用。

回答by Parth Changela

Scroll UITextField above Keyboard in a UITableViewCell on a regular UIViewController swift 3 and Xcode 8.1

在常规 UIViewController swift 3 和 Xcode 8.1 上的 UITableViewCell 中的键盘上方滚动 UITextField

1) set Delegate = UITextFieldDelegate

1) 设置 Delegate = UITextFieldDelegate

  override func viewWillAppear(_ animated: Bool) {

        NotificationCenter.default.addObserver(self, selector: #selector(DayViewController.keyboardWillShow), name:NSNotification.Name.UIKeyboardWillShow, object: nil);
        NotificationCenter.default.addObserver(self, selector: #selector(DayViewController.keyboardWillHide), name:NSNotification.Name.UIKeyboardWillHide, object: nil);

   }
      func textFieldDidBeginEditing(_ textField: UITextField) {
          self.activeField = textField
   }
      func textFieldDidEndEditing(_ textField: UITextField) {
        self.activeField = nil
   }


//MARK: - Keyboard Show and Hide Methods
    func keyboardWillShow(notification: NSNotification)
    {
        let info = notification.userInfo! as! [String: AnyObject],
        kbSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue.size,
        contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: kbSize.height, right: 0)

        self.FourthTblMainTableView.contentInset = contentInsets
        self.FourthTblMainTableView.scrollIndicatorInsets = contentInsets

        var aRect = self.FourthTblMainTableView.frame
        aRect.size.height -= kbSize.height
    }
    func keyboardWillHide(notification: NSNotification)
    {
         let contentInsets = UIEdgeInsets.zero
        self.FourthTblMainTableView.contentInset = contentInsets
        self.FourthTblMainTableView.scrollIndicatorInsets = contentInsets
    }

回答by Susim Samanta

In swift4we can create an extensionof UIViewController.

swift4我们可以创建一个扩展UIViewController中

extension UIViewController {
    //MARK: Keyboard user interactions
    func handleContainerViewFrameOnKeyboardShowHide(originalContainerFrame: CGRect) {
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: OperationQueue.main) {(notification) in
            var info = (notification as NSNotification).userInfo!
            let keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
            var tableViewFrame = originalContainerFrame
            tableViewFrame.size.height = originalContainerFrame.size.height - keyboardFrame.size.height
            self.view.frame = tableViewFrame
        }
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillHide, object: nil, queue: OperationQueue.main) { (notification) in
            self.view.frame = originalContainerFrame
        }
    }
}