iPad上表单视图中当键盘弹出时如何调整表格视图大小

7
我在调整tableview大小并在iPad上以表单形式显示时,遇到了一些困难。当键盘出现时,我无法滚动到活动文本字段。在iPhone上,这个问题没有出现,因为我不需要考虑表单视图的偏移量 - 我只需将tableview的底部contentInset更改为键盘高度即可。然而,在iPad上这种方法行不通,因为表单视图及其tableview并未占据整个屏幕。

有什么最佳方法来计算新的tableview底部contentInset应该是多少?


你好!你找到解决问题的方法了吗?我遇到了同样的问题。想知道是否有其他人已经解决了这个问题。 - Fmessina
4个回答

14

好的,我自己也遇到了这个问题。我创建了一个适用于所有情况的解决方案(不仅仅适用于以表单形式呈现的视图控制器)。解决方案使用Swift 3编写,所以您仍然需要将其转换为Objective-C,但是如果您仔细阅读注释,这应该不是问题。

该解决方案的关键是在键盘动画完成并且表单已经处于新位置时更新tableView(或scrollView)插入内容。

在您的UIViewController子类中添加:

override func viewDidLoad() {
     super.viewDidLoad()
     NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame(_:)), name: .UIKeyboardWillChangeFrame, object: nil)
     NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame(_:)), name: .UIKeyboardWillHide, object: nil)
}

这将在键盘显示/隐藏时添加一个观察器。我们还需要取消订阅这些通知,否则应用程序将崩溃:

deinit {
    NotificationCenter.default.removeObserver(self)
}

最后,最重要的代码:

func getTableViewInsets(keyboardHeight: CGFloat) -> UIEdgeInsets {
    // Calculate the offset of our tableView in the 
    // coordinate space of of our window
    let window = (UIApplication.shared.delegate as! AppDelegate).window!
    let tableViewFrame = tableView.superview!.convert(tableView.frame, to: window)

    // BottomInset = part of keyboard that is covering the tableView
    let bottomInset = keyboardHeight 
        - ( window.frame.height - tableViewFrame.height - tableViewFrame.origin.y )

    // Return the new insets + update this if you have custom insets
    return UIEdgeInsetsMake(
         tableView.contentInset.top, 
         tableView.contentInset.left, 
         bottomInset, 
         tableView.contentInset.right
    )
}

func keyboardWillChangeFrame(_ notification: Notification){
    guard let info = (notification as NSNotification).userInfo else {
        return
    }

    guard let animationDuration = info[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval else {
        return
    }

    // Default: keyboard will hide:
    var keyboardHeight: CGFloat = 0

    if notification.name == .UIKeyboardWillChangeFrame {
        // The keyboard will show
        guard let keyboardFrame = info[UIKeyboardFrameEndUserInfoKey] as? NSValue else {
            return
        }

        keyboardHeight = keyboardFrame.cgRectValue.height
    }

    let contentInsets = getTableViewInsets(keyboardHeight: keyboardHeight)

    UIView.animate(withDuration: animationDuration, animations: {
        self.tableView.contentInset = contentInsets
        self.tableView.scrollIndicatorInsets = contentInsets
    }, completion: {(completed: Bool) -> Void in
        // Chances are the position of our view has changed, (form sheet)
        // so we need to double check our insets
        let contentInsets = self.getTableViewInsets(keyboardHeight: keyboardHeight)
        self.tableView.contentInset = contentInsets
        self.tableView.scrollIndicatorInsets = contentInsets
    })
}

2
谢谢,对于bottomInset的计算,我使用了let bottomInset = keyboardHeight - (window.frame.maxY - tableViewFrame.maxY) - user1046037
我做了与此非常相似的事情,但更改了包含表视图和按钮的视图的底部约束。 我想动画化约束的更改,但这需要表单表格的最终位置。 有没有办法获得它? - Olav Gausaker
1
为什么你使用 UIKeyboardWillChangeFrame 而不是 UIKeyboardWillShow - Radek Wilczak

0

首先,感谢Simon Backx,我在我的真实项目中使用了他的方法。它有效,但有一些情况没有完全考虑到。我对他的答案进行了优化。我原本想在他的答案下面添加评论,但我们没有足够的声望来添加评论。我只能创建一个新的答案,希望这个答案可以帮助其他人。

  • 注册观察者
override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillChangeFrame(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}

注销观察者 (如果您的应用程序目标是 iOS 9.0 及更高版本或 macOS 10.11 及更高版本,则无需注销观察者)
deinit {
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
  • 根据键盘的变化修改tableview的contentInsets
private func calculateTableViewContentInsets(keyboardHeight: CGFloat) -> UIEdgeInsets {
    let window = (UIApplication.shared.delegate as! AppDelegate).window!
    let tableViewFrame = tableView.superview!.convert(tableView.frame, to: window)

    let bottomInset = keyboardHeight
        - (window.frame.height - tableViewFrame.height - tableViewFrame.origin.y)
        - tableView.safeAreaInsets.bottom

    var newInsets = tableView.contentInset
    newInsets.bottom = bottomInset

    return newInsets
}


@objc private func keyboardWillChangeFrame(_ sender: Notification) {
    guard let userInfo = sender.userInfo else { return }
    guard let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
    guard let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }
    let keyboardHeight = keyboardFrame.height

    let contentInsets = calculateTableViewContentInsets(keyboardHeight: keyboardHeight)
    UIView.animate(withDuration: duration, animations: {
        self.tableView.contentInset = contentInsets
        self.tableView.scrollIndicatorInsets = contentInsets
    }, completion: { _ in
        let contentInsets = self.calculateTableViewContentInsets(keyboardHeight: keyboardHeight)
        self.tableView.contentInset = contentInsets
        self.tableView.scrollIndicatorInsets = contentInsets
    })
}

@objc private func keyboardWillHide(_ sender: Notification) {
    guard let userInfo = sender.userInfo else { return }
    guard let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }

    UIView.animate(withDuration: duration) {
        self.tableView.contentInset.bottom = 0
        self.tableView.scrollIndicatorInsets.bottom = 0
    }
}

-1
为什么不调整整个UITableView的大小,而是设置contentInset?
当键盘出现时,我会修改tableview的约束。
在你的.h文件中创建一个引用,引用将在键盘隐藏或显示时进行更改。 (例如,调整tableview底部与父视图底部之间的边距。)
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomMargin;

UIKeyboardWillShowNotification添加观察者

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

以及实际的方法

-(void)keyboardWillShow:(NSNotification*)notification {
    [self.view layoutIfNeeded]; 

    NSDictionary* userInfo = [notification userInfo]; 
    CGRect keyboardFrameEnd;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameEnd]; //getting the frame of the keyboard
    //you can get keyboard animation's duration and curve with UIKeyboardAnimationCurveUserInfoKey and UIKeyboardAnimationDurationUserInfoKey if you want to animate it

    //calculate the new values of the constraints of your UITableView

    //for the example, the new value will be the keyboard height
    self.bottomMargin.constant = keyboardFrameEnd.size.height;
    [self.view layoutIfNeeded];
}

2
我认为这并不能解决我的问题,我的问题是如何计算我应该改变表格视图高度或内容插入的量。在iPhone应用程序中,这很容易,因为键盘直接出现在具有表格视图的视图控制器上方 - 它们具有相同的底部坐标。但在我的iPad应用程序中,表格视图及其视图控制器位于模态表单表中,这意味着我还必须考虑从窗口底部到表单表底部的像素数量。我可以计算这个值,只是想知道最好的方法是什么。 - rodskagg

-1

我在这篇帖子中发布了一个演示如何完成此操作的链接:

iOS 7 iPad方向问题,在UITextField上键盘出现后视图向上移动

它还包括一个keyboardWillShow函数,但它可以与iOS 8兼容:

-(void)onKeyboardWillShow:(NSNotification*)notification
{
    //  The user has turned on the onscreen keyboard.
    //
    NSDictionary *info = [notification userInfo];
    NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
    CGRect keyboardFrame = [kbFrame CGRectValue];
    keyboardFrame = [self.view convertRect:keyboardFrame fromView:nil];

    CGFloat keyboardHeight = keyboardFrame.size.height;
}

我发现在iOS 8.x中,如果我没有使用convertRect函数,有时iPad在横屏模式下会报告键盘高度为1024像素。


1
如果表视图以模态表单形式显示,这个功能也能正常工作吗? - rodskagg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接