确定 UIScrollVIew 中 UITextView 的滚动偏移量
Posted
技术标签:
【中文标题】确定 UIScrollVIew 中 UITextView 的滚动偏移量【英文标题】:Determine scroll offset for UITextView in UIScrollVIew 【发布时间】:2013-08-08 09:20:14 【问题描述】:我在UIScrollView
的 textView 下方和之后有 UITextView
和其他视图。
当用户输入文本时,我会更改 textView.contentSize.height 上的 textView 框架高度。
这个想法是为用户保存所有文本和其他元素的可能性。
我有一些问题。当用户点击我想滚动到这一行。这一定是这样的:
我无法确定如何计算偏移量并始终滚动到当前插入符号。
我的初始画面:
所有视图都是UIScrollView
的子视图。
每次用户输入 @"\n"
时,我都会更改 UITextView
框架:
CGRect textViewFrame = textView.frame;
CGSize textViewContentSize = textView.contentSize;
NSLog(@"textView content size: %@", NSStringFromCGSize(textViewContentSize));
textViewFrame.size.height = textViewContentSize.height;
[textView setFrame:textViewFrame];
然后我增加UIScrollView
contentSize。
我的问题 - 我不明白如何在键盘下滚动到 textView 的 CURSOR。
我尝试做一些事情:
CGPoint cursorPosition = [textView caretRectForPosition:textView.selectedTextRange.start].origin;
CGPoint relativeCursorPoint = [textView convertPoint:cursorPosition toView:scrollView];
if(textView.isFirstResponder && relativeCursorPoint.y >= scrollViewFrame.size.height + scrollView.contentOffset.y)
int offset = relativeCursorPoint.y - scrollViewFrame.size.height + 18.0f;
//int offset = textViewRect.origin.y - scrollViewFrame.size.height + 18.0f;
[scrollView setContentOffset:CGPointMake(0, offset) animated:YES];
但是没有用。
【问题讨论】:
您是否在问如何将视图滚动到键盘上方?我发现有点难以理解你的问题,你能澄清一下吗?谢谢! @NinjaLikesCheez,是的,像这样。 文本的长度(你在TextField中输入的)几乎等于TextField的高度和滚动。 @G.Ganesh, :) 我询问了 textView 和 textView 在 scrollView 中的作用 【参考方案1】:A.) 为了实现与 Apple Notes 滚动 + 键盘关闭类似的效果,我首先在 UIScrollView 中添加了一个 UITextView。
B.) 然后添加 self 作为键盘变化的观察者:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewDidBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
C.) 然后当用户点击 textview / textfield 时,会调用下面的方法。您获取键盘的开始高度和结束高度,并将名为keyboardHeight 的变量设置为endHeight 值(稍后使用)。
下面计算的“差异”用于在用户上下移动自动更正/建议栏时上下移动文本。如果你不想要这个,你可以将 textView 上的 'autocorrectionType' 设置为 false。
-(void)keyboardWillChangeFrame:(NSNotification *)n
float beginHeight = [[n.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;
float endHeight = [[n.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
float difference = beginHeight-endHeight;
keyBoardHeight = endHeight; //set the var
//animate change
if (difference != 0) //animate frame change in your textview
D.) 在 textViewDidBegin 方法上,将 UIScrollview 移动到屏幕顶部,使 textview 与顶部齐平(setContentOffset)。
您计算 textview 的高度,因为它应该在视图顶部和键盘顶部之间,下面称为“initialTVHeight”。
您应该强制 textview 启用其滚动,因此将 textView.contentSize 设置为最小尺寸 (initialTVHeight) + 一个像素 - 这会强制滚动。
-(void)textViewDidBeginEditing:(UITextView *)textView
//need to move the tv up to the top
float yOff = 0;
yOff += durationCell.frame.size.height;
yOff += reminderCell.frame.size.height;
yOff += fixedCell.frame.size.height;
yOff += repeatCell.frame.size.height;
[mainScroller setContentOffset:CGPointMake(0, yOff) animated:true];
//resize the textview to meet the top of the keyboard
float padding = 0; //padding between keyboard and last line in the textview
initialTVHeight = h - 60 - keyBoardHeight - padding; //the height when text does not overflow
textTV.frame = CGRectMake(0, textTV.frame.origin.y, w, initialTVHeight); //set the frame to that size
//if smaller than minimum, increase (if editing existing text)
if (textTV.contentSize.height < initialTVHeight)
textTV.contentSize = CGSizeMake(w, initialTVHeight+1); //force initial scroll
E.) 此方法将 textView contentSize 更新为最小高度以确保启用滚动(即,如果只有两行文本,用户仍然可以反弹滚动)。 textView 内容长于初始大小的地方,让它自然增长。
-(void)textViewDidChange:(UITextView *)textView
//require minimum height for scroll
if (textTV.contentSize.height < initialTVHeight) //content height comes in under, force scroll
textTV.contentSize = CGSizeMake(w, initialTVHeight+1); //adding one forces scroll
F.) 添加这些方法,确保仅通过用户拖动(而不是用户输入新行)关闭键盘
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
if ([scrollView isEqual:textTV])
enableDragDismiss = true;
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
if ([scrollView isEqual:textTV])
enableDragDismiss = false;
G.) 最后,添加这个方法,如果用户拖动到键盘高度以下,它会关闭键盘。
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
if (enableDragDismiss && [scrollView isEqual:textTV])
float y = [textTV.panGestureRecognizer locationInView:self.view].y;
float keyboardY = h-keyBoardHeight;
if (y > keyboardY)
enableDragDismiss = false; //allow only once
[textTV resignFirstResponder];
[UIView animateWithDuration:0.5f
delay:0.0f
usingSpringWithDamping:1.0f
initialSpringVelocity:0.8f
options:UIViewAnimationOptionCurveEaseOut
animations:^
mainScroller.contentOffset = CGPointMake(0, 0);
textTV.contentOffset = CGPointMake(0, 0);
completion:^(BOOL finished)
];
float yOff = 60;
yOff += durationCell.frame.size.height;
yOff += reminderCell.frame.size.height;
yOff += fixedCell.frame.size.height;
yOff += repeatCell.frame.size.height;
textTV.frame = CGRectMake(0, textTV.frame.origin.y, w, h-yOff);
【讨论】:
【参考方案2】:好的,如果您尝试将 UITextView 滚动到键盘“上方”,那么您可以使用 -[UITextView textViewDidBeginEditing:]
在初始触摸时修改您的 scrollView 的内容偏移量,但是如果您希望每次用户都滚动添加换行符或类似内容,您应该可以使用-[UITextView shouldChangeTextInRange:]
,如下所示。
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
if ([text isEqualToString:@"\n"])
// User has pressed 'return' and entered a new line
// change the content offset + height of line text
self.textView.contentOffset = CGPointMake(x, y);
return NO;
return YES;
UITextView
继承自 UIScrollView
,因此您可以像在 UIScrollView
中一样更改内容偏移量。
【讨论】:
【参考方案3】:试试这个,它对我有用
- (void)viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
-(BOOL)textFieldShouldReturn:(UITextField *)textField
[textField resignFirstResponder];
return YES;
- (void)textFieldDidBeginEditing:(UITextField *)textField
activeField=textField;
-(void)keyboardWasShown:(NSNotification *)notification
float viewWidth = 1024;
float viewHeight = 654;
NSDictionary *keyboardInfo = [notification userInfo];
CGSize keyboardSize = [[keyboardInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
float keyboardHeight = keyboardSize.width;
CGRect viewableAreaFrame = CGRectMake(0.0, 0.0, viewWidth, viewHeight - keyboardHeight);
CGRect txtDemoFrame = [activeField.superview convertRect:activeField.frame toView:self.view];
if (!CGRectContainsRect(viewableAreaFrame, txtDemoFrame))
// We need to calculate the new Y offset point.
//float scrollPointY = viewHeight - keyboardHeight;
// Don't forget that the scroller should go to its original position
// so store the current Y point of the content offset.
self.originalScrollerOffsetY = self.SCRVMain.contentOffset.y;
// Finally, scroll.
[self.SCRVMain setContentOffset:CGPointMake(0.0, [activeField.superview convertPoint:activeField.frame.origin toView:self.view].y-200) animated:YES];
-(void)keyboardWillHide:(NSNotification *)notification
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.SCRVMain.contentInset = contentInsets;
self.SCRVMain.scrollIndicatorInsets = contentInsets;
[self.SCRVMain setContentOffset:CGPointMake(0.0, self.originalScrollerOffsetY) animated:YES];
【讨论】:
【参考方案4】:在我的 textViewDidChange 中:我已经实现了这样的解决方案(为非常原始的代码道歉;)):
NSString* substringToSelection = [textView.text substringToIndex:textView.selectedRange.location];
UIFont* textFont = textView.font;
CGRect boundingRect = [substringToSelection boundingRectWithSize:CGSizeMake(textView.frame.size.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@NSFontAttributeName : textFont context:nil];
CGRect translatedRect = [textView convertRect:boundingRect toView:self.scrollView];
if (!CGRectContainsPoint(self.scrollView.bounds, CGPointMake(CGRectGetMaxX(translatedRect), CGRectGetMaxY(translatedRect))))
[self.scrollView scrollRectToVisible:translatedRect animated:YES];
【讨论】:
我喜欢这个解决方案,但有一个评论和问题:首先,可能不需要 CGRectContainsPoint() 检查,因为如果该区域已经可见,-scrollRectToVisible:animated:
方法不会执行任何操作。其次,该解决方案似乎向下滚动刚好足以使该区域的顶部可见。在我的情况下(在 ios7 上),这显示了行的前几个像素(光标所在的位置)而不是整行。您是否也看到了这种行为?我通过添加来解决这个问题:translatedRect.origin.y += textFont.capHeight;
以上是关于确定 UIScrollVIew 中 UITextView 的滚动偏移量的主要内容,如果未能解决你的问题,请参考以下文章
确定 UIScrollVIew 中 UITextView 的滚动偏移量
View 生命周期中确定 UIScrollView 屏幕大小的最佳时间
如何在分页 UIScrollView 中显示确定的项目,而不是仅从 0 加载它
UIScrollView 在分页时确定 scrollViewDidEndDragging 上的滚动方向