经过大量的试验和错误,我放弃了,并提出了这个问题。我见过很多人有类似的问题,但不能得到所有的答案工作正确。

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

当我试图滚动和编辑UITableView底部的单元格时,我不能设法让我的单元格正确地定位在键盘上方。

我看到过很多关于改变视图大小的回答……但到目前为止,没有一种效果很好。

谁能用一个具体的代码示例来阐明这样做的“正确”方式?


当前回答

我认为没有“正确”的方法来做这件事。您必须为您的用例选择最合适的解决方案。 在我的iPad App中,我有一个UIViewController,它以UIModalPresentationFormSheet的形式呈现,由一个UITableView组成。这个表格每个单元格包含两个UITextFields。 只是调用scrollToRowAtIndexPath:atScrollPosition:animated:在textFieldDidBeginEditing:方法不为我工作。因此,我创建了一个tableFooterView:

- (void)viewDidLoad
{
    [super viewDidLoad];

    m_footerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, m_tableView.frame.size.width, 300.0f)];
    [m_footerView setBackgroundColor:[UIColor clearColor]];
    [m_tableView setTableFooterView:m_footerView];
    [m_footerView release];
}

这个想法是键盘隐藏tableFooterView而不是UITextFields。tableFooterView必须足够高。之后,你可以在textFieldDidBeginEditing:方法中使用scrollToRowAtIndexPath:atScrollPosition:animated:。

我认为也可以通过添加键盘通知的观察者来动态地显示和隐藏tableFooterView,但我还没有尝试过:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

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

- (void)keyboardWillShow:(NSNotification *)notification 
{
     [m_tableView setTableFooterView:m_footerView];
}

- (void)keyboardWillHide:(NSNotification *)notification 
{
     [m_tableView setTableFooterView:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

其他回答

我刚刚发现另一个bug时使用UITableViewController。当键盘出现时,它不会自动滚动。我注意到这是因为contentInsetAdjustmentBehavior = .never在UITableView上。

一个更加流线型的解决方案。它滑入UITextField委托方法,所以它不需要搞砸/ UIKeyboard通知。

实现注意事项:

kSettingsRowHeight——UITableViewCell的高度。

offsetTarget和offsetThreshold基于kSettingsRowHeight。如果使用不同的行高,则将这些值设置为point的y属性。[alt:以不同的方式计算行偏移量。]

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
CGFloat offsetTarget    = 113.0f; // 3rd row
CGFloat offsetThreshold = 248.0f; // 6th row (i.e. 2nd-to-last row)

CGPoint point = [self.tableView convertPoint:CGPointZero fromView:textField];

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

CGRect frame = self.tableView.frame;
if (point.y > offsetThreshold) {
    self.tableView.frame = CGRectMake(0.0f,
                      offsetTarget - point.y + kSettingsRowHeight,
                      frame.size.width,
                      frame.size.height);
} else if (point.y > offsetTarget) {
    self.tableView.frame = CGRectMake(0.0f,
                      offsetTarget - point.y,
                      frame.size.width,
                      frame.size.height);
} else {
    self.tableView.frame = CGRectMake(0.0f,
                      0.0f,
                      frame.size.width,
                      frame.size.height);
}

[UIView commitAnimations];

return YES;

}

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

[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

CGRect frame = self.tableView.frame;
self.tableView.frame = CGRectMake(0.0f,
                  0.0f,
                  frame.size.width,
                  frame.size.height);

[UIView commitAnimations];

return YES;

}

键盘通知可以工作,但苹果的示例代码假设滚动视图是窗口的根视图。通常情况并非如此。您必须补偿标签栏等,以获得正确的偏移量。

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

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

Swift 3-4动画和键盘帧改变的解决方案:

首先,创建Bool类型:

// MARK: - Private Properties
private var isKeyboardShowing = false

其次,在系统键盘通知中添加观察者:

// MARK: - Overriding ViewController Life Cycle Methods
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

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

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

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: .UIKeyboardWillChangeFrame, object: nil)
}

第三,准备动画功能:

func adjustTableViewInsets(keyboardHeight: CGFloat, duration: NSNumber, curve: NSNumber){
    var extraHeight: CGFloat = 0
    if keyboardHeight > 0 {
        extraHeight = 20
        isKeyboardShowing = true
    } else {
        isKeyboardShowing = false
    }

    let contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight + extraHeight, right: 0)
    func animateFunc() {
        //refresh constraints
        //self.view.layoutSubviews()
        tableView.contentInset = contentInset
    }

    UIView.animate(withDuration: TimeInterval(duration), delay: 0, options: [UIViewAnimationOptions(rawValue: UInt(curve))], animations: animateFunc, completion: nil)
}

然后添加target/action方法(由观察者调用):

// MARK: - Target/Selector Actions
func keyboardWillShow(notification: NSNotification) {
    if !isKeyboardShowing {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            let keyboardHeight = keyboardSize.height

            let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
            let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber

            adjustTableViewInsets(keyboardHeight: keyboardHeight, duration: duration, curve: curve)
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
    let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber
    adjustTableViewInsets(keyboardHeight: 0, duration: duration, curve: curve)
}

func keyboardWillChangeFrame(notification: NSNotification) {
    if isKeyboardShowing {
        let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
        let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber

        if let newKeyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            let keyboardHeight = newKeyboardSize.height
            adjustTableViewInsets(keyboardHeight: keyboardHeight, duration: duration, curve: curve)
        }
    }
}

最后,不要忘记在deinit或viewWillDisappear中删除观察者:

deinit {
    NotificationCenter.default.removeObserver(self)
}

在viewdidload

-(void)viewdidload{

[super viewdidload];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}

    -(void)keyboardWillChange:(NSNotification*)sender{

        NSLog(@"keyboardwillchange sender %@",sender);

float margin=0  // set your own topmargin


        CGFloat originY = [[sender.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y;


        if (originY >= self.view.frame.size.height){

            NSLog(@"keyboardclose");



            [tb_ setFrame:CGRectMake(0, margin, self.view.frame.size.width, self.view.frame.size.height-margin)];

        }else{

            NSLog(@"keyobard on");

            float adjustedHeight = self.view.frame.size.height - margin - (self.view.frame.size.height-originY);

            [tb_ setFrame:CGRectMake(0, margin, self.view.frame.size.width, adjustedHeight)];
        }







    }