I've been asking a question or two over the past few days of working on an application that keeps a custom toolbar aligned to the top of the iPhone keyboard. I'm using the method described by Josh in this question; basically, I have the view controller listen for the UIKeyboardWillShowNotification and add the toolbar as necessary.
在过去的几天里,我一直在问一个问题,那就是让自定义工具栏与iPhone键盘顶部对齐的应用程序。我正在使用Josh在这个问题中描述的方法;基本上,我让视图控制器监听UIKeyboardWillShowNotification并根据需要添加工具栏。
The view controller itself manages a table view whose cells all contain a UITextField. The keyboard and toolbar being presented are editing these text fields. The only issue I'm still having is this: when the keyboard and toolbar are presented for a cell more than about halfway down the table, it scrolls to be visible above the keyboard, but not above the toolbar.
视图控制器本身管理一个表视图,其单元格都包含UITextField。显示的键盘和工具栏正在编辑这些文本字段。我仍然遇到的唯一问题是:当键盘和工具栏出现在距离桌子大约一半的单元格时,它会滚动到键盘上方,但不会在工具栏上方。
The cells and text fields are still editable, but about half the cell is hidden under the toolbar. If I disappear the toolbar (don't add it in the notification responder), the entire cell becomes visible, but obviously I lose the functionality the toolbar provides.
单元格和文本字段仍可编辑,但大约一半的单元格隐藏在工具栏下。如果我消失工具栏(不要将其添加到通知响应器中),整个单元格将变为可见,但显然我将丢失工具栏提供的功能。
Is there a way to change the location the text field gets scrolled to? I've tried playing around with the UITableView method scrollToRowAtIndexPath:atScrollPosition:animated:
, but it tends to give weird results when toggling through several cells.
有没有办法更改文本字段滚动到的位置?我尝试过使用UITableView方法scrollToRowAtIndexPath:atScrollPosition:animated:,但是当切换到几个单元格时,它往往会产生奇怪的结果。
What's the best method for scrolling a table view cell to a visible position above a custom keyboard toolbar?
将表格视图单元格滚动到自定义键盘工具栏上方的可见位置的最佳方法是什么?
4 个解决方案
#1
I do exactly what you are describing in my app. This is the code I use, verbatim. It's very nicely animated, and seems to work wonderfully.
我完全按照你在我的应用中描述的内容。这是我使用的代码,逐字逐句。这是非常好的动画,似乎工作得非常好。
Two disclaimers:
- I pass in parentView as part of a custom initialization.
- I did not write this code, I'm not taking credit for it. I got it from Matt Gallagher's truly wonderful blog Cocoa With Love.
我将parentView作为自定义初始化的一部分传递。
我没有写这个代码,我不赞成它。我是从Matt Gallagher真正精彩的博客Cocoa With Love那里得到的。
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
// adjust this following value to account for the height of your toolbar, too
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
- (void)textFieldDidEndEditing:(UITextField *)textField
{
CGRect viewFrame = self.parentView.frame;
viewFrame.origin.y += animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.parentView setFrame:viewFrame];
[UIView commitAnimations];
}
- (void) textFieldDidBeginEditing:(UITextField*) textField {
CGRect textFieldRect = [self.parentView.window convertRect:textField.bounds fromView:textField];
CGRect viewRect = [self.parentView.window convertRect:self.parentView.bounds fromView:self.parentView];
CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator = midline - viewRect.origin.y - MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator = (MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION) * viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
if (heightFraction < 0.0) {
heightFraction = 0.0;
}else if (heightFraction > 1.0) {
heightFraction = 1.0;
}
animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
CGRect viewFrame = self.parentView.frame;
viewFrame.origin.y -= animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.parentView setFrame:viewFrame];
[UIView commitAnimations];
}
#2
Assuming that you know the NSIndexPath that you would show above the keyboard, you can use the following fast and clean method:
假设您知道将在键盘上方显示的NSIndexPath,您可以使用以下快速而干净的方法:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
UITableViewCell *aCell = [myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:6 inSection:0]];
CGSize cellSize = aCell.frame.size;
[myTableView setContentOffset:CGPointMake(0, cellSize.height*6) animated:YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
[myTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
}
Since the UITableView inherits from UIScrollView you can adjust the contentOffset property to reflect the row visibility in which you are interested.
由于UITableView继承自UIScrollView,因此您可以调整contentOffset属性以反映您感兴趣的行可见性。
In the DidEndEditing method, you have to restore the original scroll position. This is achieved with a standard scrollToRowAtIndexPath:atScrollPosition:animated call.
在DidEndEditing方法中,您必须恢复原始滚动位置。这是通过标准scrollToRowAtIndexPath:atScrollPosition:动画调用实现的。
#3
Maybe you could resize the UITableView
to be the height between the navigation bar and the toolbar above the keyboard, and then scroll the row into view?
也许您可以将UITableView调整为导航栏和键盘上方工具栏之间的高度,然后将行滚动到视图中?
#4
Since all UITableView
s are also UIScrollView
s, you can call all of the standard scroll methods on them.
由于所有UITableViews也是UIScrollViews,因此您可以调用它们上的所有标准滚动方法。
Something like this will do:
这样的事情会做:
- (void)scrollTableView:(UITableView *)tableView toIndexPath:(NSIndexPath *)indexPath withBottomPadding:(CGFloat)bottomPadding
{
CGRect rectForRow = [tableView rectForRowAtIndexPath:indexPath];
CGRect bounds = [tableView bounds];
CGPoint contentOffset = [tableView contentSize];
if (rectForRow.origin.y + rectForRow.size.height > contentOffset.y + bounds.size.height - bottomPadding)
[tableView setContentOffset:rectForRow.origin.y + rectForRow.size.height - bounds.size.height - bottomPadding animated:YES];
else
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
}
Note: I'm not at a Mac with Xcode at the moment. I will test this code when I am
注意:我目前不在Mac上使用Xcode。我会在这时测试这段代码
#1
I do exactly what you are describing in my app. This is the code I use, verbatim. It's very nicely animated, and seems to work wonderfully.
我完全按照你在我的应用中描述的内容。这是我使用的代码,逐字逐句。这是非常好的动画,似乎工作得非常好。
Two disclaimers:
- I pass in parentView as part of a custom initialization.
- I did not write this code, I'm not taking credit for it. I got it from Matt Gallagher's truly wonderful blog Cocoa With Love.
我将parentView作为自定义初始化的一部分传递。
我没有写这个代码,我不赞成它。我是从Matt Gallagher真正精彩的博客Cocoa With Love那里得到的。
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
// adjust this following value to account for the height of your toolbar, too
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
- (void)textFieldDidEndEditing:(UITextField *)textField
{
CGRect viewFrame = self.parentView.frame;
viewFrame.origin.y += animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.parentView setFrame:viewFrame];
[UIView commitAnimations];
}
- (void) textFieldDidBeginEditing:(UITextField*) textField {
CGRect textFieldRect = [self.parentView.window convertRect:textField.bounds fromView:textField];
CGRect viewRect = [self.parentView.window convertRect:self.parentView.bounds fromView:self.parentView];
CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator = midline - viewRect.origin.y - MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator = (MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION) * viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
if (heightFraction < 0.0) {
heightFraction = 0.0;
}else if (heightFraction > 1.0) {
heightFraction = 1.0;
}
animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
CGRect viewFrame = self.parentView.frame;
viewFrame.origin.y -= animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.parentView setFrame:viewFrame];
[UIView commitAnimations];
}
#2
Assuming that you know the NSIndexPath that you would show above the keyboard, you can use the following fast and clean method:
假设您知道将在键盘上方显示的NSIndexPath,您可以使用以下快速而干净的方法:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
UITableViewCell *aCell = [myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:6 inSection:0]];
CGSize cellSize = aCell.frame.size;
[myTableView setContentOffset:CGPointMake(0, cellSize.height*6) animated:YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
[myTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
}
Since the UITableView inherits from UIScrollView you can adjust the contentOffset property to reflect the row visibility in which you are interested.
由于UITableView继承自UIScrollView,因此您可以调整contentOffset属性以反映您感兴趣的行可见性。
In the DidEndEditing method, you have to restore the original scroll position. This is achieved with a standard scrollToRowAtIndexPath:atScrollPosition:animated call.
在DidEndEditing方法中,您必须恢复原始滚动位置。这是通过标准scrollToRowAtIndexPath:atScrollPosition:动画调用实现的。
#3
Maybe you could resize the UITableView
to be the height between the navigation bar and the toolbar above the keyboard, and then scroll the row into view?
也许您可以将UITableView调整为导航栏和键盘上方工具栏之间的高度,然后将行滚动到视图中?
#4
Since all UITableView
s are also UIScrollView
s, you can call all of the standard scroll methods on them.
由于所有UITableViews也是UIScrollViews,因此您可以调用它们上的所有标准滚动方法。
Something like this will do:
这样的事情会做:
- (void)scrollTableView:(UITableView *)tableView toIndexPath:(NSIndexPath *)indexPath withBottomPadding:(CGFloat)bottomPadding
{
CGRect rectForRow = [tableView rectForRowAtIndexPath:indexPath];
CGRect bounds = [tableView bounds];
CGPoint contentOffset = [tableView contentSize];
if (rectForRow.origin.y + rectForRow.size.height > contentOffset.y + bounds.size.height - bottomPadding)
[tableView setContentOffset:rectForRow.origin.y + rectForRow.size.height - bounds.size.height - bottomPadding animated:YES];
else
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
}
Note: I'm not at a Mac with Xcode at the moment. I will test this code when I am
注意:我目前不在Mac上使用Xcode。我会在这时测试这段代码