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

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

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

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

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


当前回答

我认为最好的方法是通过UITableViewController。

如果你想在UIViewController中创建一个UITableView,只需要用一个嵌入的UITableViewController创建一个ContentView,并在UIViewController的viedDidLoad中放入以下代码行:

self.tableView = ((UITableViewController*)self.childViewControllers[0]).tableView;
self.tableView.delegate = self;
self.tableView.dataSource = self;

容易,)

其他回答

因此,经过数小时的艰苦工作,尝试使用这些当前的解决方案(完全失败),我终于让事情正常工作,并更新它们以使用新的动画块。我的回答完全基于Ortwin上面的回答。

所以不管出于什么原因,上面的代码就是不适合我。我的设置似乎和其他人的很相似,但可能是因为我用的是iPad或4.3……不知道。它在做一些古怪的数学运算然后把我的tableview从屏幕上拍下来。

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

所以我沿用了Ortwin的基本原理,但改变了计算原点的方法。Y和尺寸。我的表格视图的高度和键盘的高度。当我从结果中减去窗口的高度时,它告诉我有多少交集。如果它大于0(也就是有一些重叠),我执行帧高度的动画。

此外,还有一些重绘问题得到了解决,1)等待滚动到单元格直到动画完成,2)使用UIViewAnimationOptionBeginFromCurrentState选项隐藏键盘。

有几件事需要注意。

_topmostrowbeforekeyboardwasshow & _originalFrame是在头文件中声明的实例变量。 自我。guestEntryTableView是我的tableView(我在外部文件中) iaskcgrecswap是Ortwin用于翻转帧坐标的方法 我只更新tableView的高度如果它至少有50px会显示 因为我不在UIViewController中,所以我没有self。视图,我只是把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;
}

UITableViewController会自动进行滚动。 与使用UIViewController相比的区别是,当使用TableViewController时,你必须通过使用NavigationController以编程方式创建navbar - buttonitem。

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

执行滚动的函数可以简单得多:

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

就是这样。根本不需要计算。

一个更加流线型的解决方案。它滑入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;

}