在显示键盘时,在iPad上的表单中调整tableview的大小

时间:2015-02-18 10:17:07

标签: ios objective-c uitableview

当在iPad上的表单中以模态方式呈现视图时,当键盘出现时,我在调整tableview的大小并滚动到活动文本字段时遇到了困难。它在iPhone上工作正常,因为我不必考虑表单视图的偏移量 - 我可以将tableview的底部contentInset更改为与键盘高度相同。但是,这不适用于iPad,因为表单及其桌面视图不占用整个屏幕。

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

4 个答案:

答案 0 :(得分:11)

好的,我自己偶然发现了这个问题。我创建了一个适用于所有情况的解决方案(因此不仅仅是作为表单显示的viewControllers)。解决方案是在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
    })
}

答案 1 :(得分:0)

为什么不调整整个UITableView而不是设置contentInset?

我会在显示键盘时修改tableview的约束。

在.h中创建一个实际约束的引用,该约束将在隐藏键盘时发生变化。 (例如,tableview底部和superview底部之间的边距。)

@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 :(得分: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)
}
  • 根据键盘的更改修改表格视图的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
    }
}

答案 3 :(得分:-1)

我在这个主题中发布了一个如何执行此演示的演示链接:

iOS 7 iPad Orientation issue, when view moves upside after keyboard appears on 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像素的键盘高度。