如何让UIScrollView滚动到UITextView的光标位置?
我有一个类似于笔记应用的视图 - 即在一张纸上打字。为了使文本和纸张同时滚动,我禁用了UITextView的滚动功能,而是将UITextView和UIImageView放入UIScrollView中。如何让UIScrollView滚动到UITextView的光标位置?
唯一的问题是,当用户键入时,文本在键盘下消失,因为显然UIScrollView不知道滚动到光标位置。
有没有简单的方法可以检索光标位置并告诉UIScrollView在那里滚动?
---编辑---
从类似的东西开始here(其中有人试图做一个UITableView类似的东西),我已经成功地使越来越多,可编辑的UITextView有固定的背景差不多完美滚动。现在唯一的问题是:
- 如果用户键入的速度特别快,文本向上移动会有轻微抖动。
- 如果用户隐藏键盘,选择屏幕底部的文本,然后再次显示键盘,则必须在文本再次可见之前键入几个字母 - 它不会立即向上滚动。
- 当用户隐藏键盘时,作为滚动视图框架填充屏幕的动画不知何故感觉不太正确。
这里是代码 - 我真的很感激,如果任何人都可以进一步细化它...
#import "NoteEditViewController.h"
#import "RLWideLabelTableCell.h"
@implementation NoteEditViewController
@synthesize keyboardSize;
@synthesize keyboardHideDuration;
@synthesize scrollView;
@synthesize noteTextView;
//
// Dealloc and all that stuff
//
- (void)loadView
{
[super loadView];
UIScrollView *aScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
self.scrollView = aScrollView; [aScrollView release];
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, noteTextView.frame.size.height);
[self.view addSubview:scrollView];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Get notified when keyboard is shown. Don't need notification when hidden because we are
// using textViewDidEndEditing so we can start animating before the keyboard disappears.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
// Add the Done button so we can test dismissal of the keyboard
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(doneButton:)];
self.navigationItem.rightBarButtonItem = doneButton; [doneButton release];
// Add the background image that will scroll with the text
CGRect noteImageFrame = CGRectMake(self.view.bounds.origin.x,
noteTitleImageFrame.size.height,
self.view.bounds.size.width, 500);
UIView *backgroundPattern = [[UIView alloc] initWithFrame:noteImageFrame];
backgroundPattern.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Notepaper-iPhone-Line"]];
[self.scrollView addSubview:backgroundPattern];
[self.view sendSubviewToBack:backgroundPattern];
[backgroundPattern release];
// Add the textView
CGRect textViewFrame = CGRectMake(noteImageFrame.origin.x+27,
noteImageFrame.origin.y-3,
noteImageFrame.size.width-35,
noteImageFrame.size.height);
RLTextView *textView = [[RLTextView alloc] initWithFrame:textViewFrame];
self.noteTextView = textView; [textView release];
self.noteTextView.font = [UIFont fontWithName:@"Cochin" size:21];
self.noteTextView.backgroundColor = [UIColor clearColor];
self.noteTextView.delegate = self;
self.noteTextView.scrollEnabled = NO;
[self.scrollView addSubview:self.noteTextView];
}
- (void)doneButton:(id)sender
{
[self.view endEditing:TRUE];
}
// When the keyboard is shown, the UIScrollView's frame shrinks so that it fits in the
// remaining space
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
float kbHideDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
self.keyboardHideDuration = kbHideDuration;
self.keyboardSize = kbSize;
self.scrollView.frame = CGRectMake(self.view.bounds.origin.x,
self.view.bounds.origin.y,
self.view.bounds.size.width,
self.view.bounds.size.height - kbSize.height);
}
// When the user presses 'done' the UIScrollView expands to the size of its superview
// again, as the keyboard disappears.
- (void)textViewDidEndEditing:(UITextView *)textView
{
[UIScrollView animateWithDuration:keyboardHideDuration animations:^{self.scrollView.frame = self.view.bounds;}];
}
// This method needs to get called whenever there is a change of cursor position in the text box
// That means both textViewDidChange: and textViewDidChangeSelection:
- (void)scrollToCursor
{
// if there is a selection cursor…
if(noteTextView.selectedRange.location != NSNotFound) {
NSLog(@"selectedRange: %d %d", noteTextView.selectedRange.location, noteTextView.selectedRange.length);
// work out how big the text view would be if the text only went up to the cursor
NSRange range;
range.location = noteTextView.selectedRange.location;
range.length = noteTextView.text.length - range.location;
NSString *string = [noteTextView.text stringByReplacingCharactersInRange:range withString:@""];
CGSize size = [string sizeWithFont:noteTextView.font constrainedToSize:noteTextView.bounds.size lineBreakMode:UILineBreakModeWordWrap];
// work out where that position would be relative to the textView's frame
CGRect viewRect = noteTextView.frame;
int scrollHeight = viewRect.origin.y + size.height;
CGRect finalRect = CGRectMake(1, scrollHeight, 1, 1);
// scroll to it
[self.scrollView scrollRectToVisible:finalRect animated:YES];
}
}
// Whenever the text changes, the textView's size is updated (so it grows as more text
// is added), and it also scrolls to the cursor.
- (void)textViewDidChange:(UITextView *)textView
{
noteTextView.frame = CGRectMake(noteTextView.frame.origin.x,
noteTextView.frame.origin.y,
noteTextView.frame.size.width,
noteTextView.contentSize.height);
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width,
noteTextView.frame.size.height+200);
[self scrollToCursor];
}
// The textView scrolls to the cursor whenever the user changes the selection point.
- (void)textViewDidChangeSelection:(UITextView *)aTextView
{
[self scrollToCursor];
}
// PROBLEM - the textView does not scroll until the user starts typing - just selecting
// it is not enough.
- (void)textViewDidBeginEditing:(UITextView *)textView
{
[self scrollToCursor];
}
很酷,你发现我的帖子,很高兴它是有帮助的!
我相信你可能无法看到,因为这条线的底线:
CGRect finalRect = CGRectMake(1, scrollHeight, 1, 1);
你正在创建一个1x1点框。一行文本可能是20或30个点的高度(取决于字体大小)。因此,如果您将此点滚动显示,它可能只会显示底线的顶部像素 - 使底线实际上不可见!如果您finalRect高一点,所以它涵盖了整条生产线,它可能会更好地工作:
CGRect finalRect = CGRectMake(1, scrollHeight, 1, 30);
此外,您可以一次打电话给你scrollRectToVisible代码多次,这可能会导致“颤动”。在我的代码中,我只运行textViewDidChangeSelection中的scrollRectToVisible,并在textViewDidChange中调整UITextView的大小(如果需要)。 UIScrollView(并通过继承UITableView)内置支持将主动选择的元素滚动到可见状态,这在我测试时运行良好,只需在键入时调整UITextView的大小(但不是在通过触摸选择内部特定点时)。
有没有简单的方法来发现屏幕坐标中的任何文本或光标UITextView
。
你应该做的是注册UIKeyboardWillShowNotification
和UIKeyboardWillShowNotification
。在回调中,您可以调整UIScrollView
的size
或contentInsets
,以调整键盘的大小。
键盘的大小,甚至动画持续时间都在userInfo
的通知中提供,因此您可以用一种很好的动画形式进行操作。
你会发现更多的信息和示例代码在这里:http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
不是严格意义上的回答你的问题,但这里有一个不同的方法来衬背景招调:http://www.cocoanetics.com/2010/03/stuff-you-learn-from-reverse-engineering-notes-app/
我用它和它效果很好。
谢谢,Manesh!正如你所说的那样,我刚刚改变了finalRect参数,这使得文本更加整齐地滚动到视图中。但我所指的“底线”问题略有不同:如果我点击屏幕的下半部分以使textView处于活动状态,则当键盘出现时,它不会滚动*根本不必键入在滚动触发之前书写或移动光标。我猜这是因为在调用'textViewDidBeginEditing'时还没有设置东西,所以scrollToCursor方法不起作用。 – 2011-05-12 15:31:06
你有可能发布一个带有工作代码的示例项目吗?另外,如果你想继续尝试自己调试它,我会考虑在每个函数中放置NSLog语句,并观察它们触发的顺序,这可能会提供一些见解。根据您显示和隐藏键盘的方式,某些功能可能会或可能不会被触发。 – Manesh 2011-05-15 05:05:31
谢谢Manesh - 碰巧发现了一个更简单的解决方案。由于UITextView是UIScrollView的子类,因此我可以将代码放在'scrollViewDidScroll:'委托方法中以移动背景图像。所以我只需要像平常一样滚动我的UITextView,而单独的背景视图就会响应它而移动。这样,滚动行为是完美的,因为它是UITextView的内置行为。感谢所有的帮助! – 2011-05-16 15:21:17