经过大量的试验和错误,我放弃了,并提出了这个问题。我见过很多人有类似的问题,但不能得到所有的答案工作正确。
我有一个由自定义单元格组成的UITableView。单元格由相邻的5个文本字段组成(有点像网格)。
当我试图滚动和编辑UITableView底部的单元格时,我不能设法让我的单元格正确地定位在键盘上方。
我看到过很多关于改变视图大小的回答……但到目前为止,没有一种效果很好。
谁能用一个具体的代码示例来阐明这样做的“正确”方式?
经过大量的试验和错误,我放弃了,并提出了这个问题。我见过很多人有类似的问题,但不能得到所有的答案工作正确。
我有一个由自定义单元格组成的UITableView。单元格由相邻的5个文本字段组成(有点像网格)。
当我试图滚动和编辑UITableView底部的单元格时,我不能设法让我的单元格正确地定位在键盘上方。
我看到过很多关于改变视图大小的回答……但到目前为止,没有一种效果很好。
谁能用一个具体的代码示例来阐明这样做的“正确”方式?
当前回答
使用UITextField的委托方法:
斯威夫特
func textFieldShouldBeginEditing(textField: UITextField) -> bool {
let txtFieldPosition = textField.convertPoint(textField.bounds.origin, toView: yourTableViewHere)
let indexPath = yourTablViewHere.indexPathForRowAtPoint(txtFieldPosition)
if indexPath != nil {
yourTablViewHere.scrollToRowAtIndexPath(indexPath!, atScrollPosition: .Top, animated: true)
}
return true
}
objective - c
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
CGPoint txtFieldPosition = [textField convertPoint:CGPointZero toView: yourTablViewHere];
NSLog(@"Begin txtFieldPosition : %@",NSStringFromCGPoint(txtFieldPosition));
NSIndexPath *indexPath = [yourTablViewHere indexPathForRowAtPoint:txtFieldPosition];
if (indexPath != nil) {
[yourTablViewHere scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
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;
}
另一个简单的方法(只适用于一个部分)
//cellForRowAtIndexPath
UItextField *tf;
[cell addSubview:tf];
tf.tag = indexPath.row;
tf.delegate = self;
//textFieldDidBeginEditing:(UITextField *)text
[[self.tableView scrollToRowsAtIndexPath:[NSIndexPath indexPathForRow:text.tag in section:SECTIONINTEGER] 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)
}
基于bartjomiej semazynk解决方案的Swift 3最简单的解决方案:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: Keyboard Notifications
@objc func keyboardWillShow(notification: NSNotification) {
if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
}
}
@objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.2, animations: {
// For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
})
}
在Swift中的一个例子,使用从UITableViewCell中获取UITextField的indexPath的文本字段的确切点:
func textFieldDidBeginEditing(textField: UITextField) {
let pointInTable = textField.convertPoint(textField.bounds.origin, toView: self.accountsTableView)
let textFieldIndexPath = self.accountsTableView.indexPathForRowAtPoint(pointInTable)
accountsTableView.scrollToRowAtIndexPath(textFieldIndexPath!, atScrollPosition: .Top, animated: true)
}