经过大量的试验和错误,我放弃了,并提出了这个问题。我见过很多人有类似的问题,但不能得到所有的答案工作正确。
我有一个由自定义单元格组成的UITableView。单元格由相邻的5个文本字段组成(有点像网格)。
当我试图滚动和编辑UITableView底部的单元格时,我不能设法让我的单元格正确地定位在键盘上方。
我看到过很多关于改变视图大小的回答……但到目前为止,没有一种效果很好。
谁能用一个具体的代码示例来阐明这样做的“正确”方式?
经过大量的试验和错误,我放弃了,并提出了这个问题。我见过很多人有类似的问题,但不能得到所有的答案工作正确。
我有一个由自定义单元格组成的UITableView。单元格由相邻的5个文本字段组成(有点像网格)。
当我试图滚动和编辑UITableView底部的单元格时,我不能设法让我的单元格正确地定位在键盘上方。
我看到过很多关于改变视图大小的回答……但到目前为止,没有一种效果很好。
谁能用一个具体的代码示例来阐明这样做的“正确”方式?
当前回答
看看我的版本:)
- (void)keyboardWasShown:(NSNotification *)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = cellSelected.superview.frame;
bkgndRect.size.height += kbSize.height;
[cellSelected.superview setFrame:bkgndRect];
[tableView setContentOffset:CGPointMake(0.0, cellSelected.frame.origin.y-kbSize.height) animated:YES];
}
- (void)keyboardWasHidden:(NSNotification *)aNotification
{
[tableView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
}
其他回答
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)
}
键盘通知可以工作,但苹果的示例代码假设滚动视图是窗口的根视图。通常情况并非如此。您必须补偿标签栏等,以获得正确的偏移量。
这比听起来容易。下面是我在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;
}
我遇到了同样的问题,但注意到它只出现在一个视图中。所以我开始寻找不同的控制器。
我发现滚动行为设置在- (void)viewWillAppear:(BOOL)动画的超级实例。
所以一定要像这样实现:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// your code
}
不管你用的是UIViewController还是UITableViewController;通过将UITableView作为self的子视图来检查它。UIViewController中的视图。这是相同的行为。视图不允许滚动,如果调用[super viewWillAppear:animated];失踪了。
以下是我的解决方案,灵感来自iOS7日历应用程序的“事件编辑”屏幕。
该解决方案的关键之一是当用户滚动表格时键盘被取消。
实现:
1)添加属性,将存储选定的文本字段:
@property (strong) UITextField *currentTextField;
和BOOL变量,我们将使用它来检查当用户滚动表时是否需要隐藏键盘。
BOOL hideKeyboardOnScroll;
2)处理UITextField委托回调:
#pragma mark - UITextFieldDelegate
- (void) textFieldDidBeginEditing: (UITextField *) textField {
self.currentTextField = textField;
}
- (void) textFieldDidEndEditing: (UITextField *) textField {
self.currentTextField = nil;
}
- (BOOL) textFieldShouldReturn: (UITextField *) textField {
[textField resignFirstResponder];
CGPoint newContentOffset = CGPointZero;
if (tableView.contentSize.height > tableView.frame.size.height) {
newContentOffset.y = MIN(tableView.contentOffset.y, tableView.contentSize.height - tableView.frame.size.height);
}
[tableView setContentOffset: newContentOffset animated: YES];
return YES;
}
3)处理UIScrollViewDelegate方法来检查用户滚动视图。
#pragma mark - UIScrollViewDelegate
- (void) scrollViewDidScroll: (UIScrollView *) scrollView {
if (hideKeyboardOnScroll == YES) {
[self.currentTextField resignFirstResponder];
}
}
4)在视图控制器的[viewWillAppear]方法中订阅键盘通知,并在[viewWillDisappear]方法中取消订阅。
- (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) viewWillDisappear: (BOOL) animated {
[super viewWillDisappear: animated];
[ [NSNotificationCenter defaultCenter] removeObserver: self name: UIKeyboardDidShowNotification object: nil];
[ [NSNotificationCenter defaultCenter] removeObserver: self name: UIKeyboardWillHideNotification object: nil];
}
5)处理键盘通知:
- (void) keyboardWillShow: (NSNotification *) notification {
CGRect keyboardFrame = [ [ [notification userInfo] objectForKey: UIKeyboardFrameBeginUserInfoKey] CGRectValue];
// Find cell with textfield.
CGRect textFieldFrame = [tableView convertRect: self.currentTextField.frame fromView: self.currentTextField];
NSIndexPath *indexPath = [tableView indexPathForRowAtPoint: textFieldFrame.origin];
UITableViewCell *cell = [tableView cellForRowAtIndexPath: indexPath];
//
// Shrink tableView size.
CGRect tableViewFrame = tableView.frame;
tableView.frame = CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width,
self.view.frame.size.height - tableView.frame.origin.y - keyboardFrame.size.height);
//
// Check if cell is visible in shrinked table size.
BOOL cellIsFullyVisible = YES;
if ( cell.frame.origin.y < tableView.contentOffset.y ||
(cell.frame.origin.y + cell.frame.size.height) > (tableView.contentOffset.y + tableView.frame.size.height) ) {
cellIsFullyVisible = NO;
}
//
// If cell is not fully visible when scroll table to show cell;
if (cellIsFullyVisible == NO) {
CGPoint contentOffset = CGPointMake(tableView.contentOffset.x, CGRectGetMaxY(cell.frame) - tableView.frame.size.height);
if (cell.frame.origin.y < tableView.contentOffset.y) {
contentOffset.y = cell.frame.origin.y;
}
contentOffset.y = MAX(0, contentOffset.y);
// For some reason [setContentOffset] is called without delay then
// this code may not work for some cells. That why we call it with brief delay.
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[UIView animateWithDuration: 0.5 animations:^{
[tableView setContentOffset: contentOffset animated: NO];
} completion: ^(BOOL finished) {
hideKeyboardOnScroll = YES;
}];
});
} else {
hideKeyboardOnScroll = YES;
}
//
// Finally restore original table frame.
tableView.frame = tableViewFrame;
//
}
- (void) keyboardWillHide: (NSNotification *) notification {
[super keyboardWillHide: notification];
hideKeyboardOnScroll = NO;
}
Swift最简单的解决方案:
override func viewDidLoad() {
super.viewDidLoad()
searchBar?.becomeFirstResponder()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillShow(_:)), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillHide(_:)), name: UIKeyboardDidHideNotification, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size.height {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
}
}
}
func keyboardWillHide(notification: NSNotification) {
UIView.animateWithDuration(0.2, animations: { self.table_create_issue.contentInset = UIEdgeInsetsMake(0, 0, 0, 0) })
// For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
}
Swift 4.2或更高版本
override func viewDidLoad() {
super.viewDidLoad()
searchBar?.becomeFirstResponder()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardDidHideNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
let keyboardHeight = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue.size.height
accountSettingsTableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
}
}
@objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.2, animations: { self.accountSettingsTableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) })}
}