如何将UIScrollView滚动到UITextView的光标位置?

时间:2011-05-11 14:45:15

标签: iphone cocoa-touch uiscrollview uitextview

我有一个类似于笔记应用程序的视图 - 即在一张带衬里的纸上打字。为了使文本和纸张同时滚动,我已经禁用了UITextView的滚动,而是将我的UITextView和我的UIImageView放在UIScrollView中。

唯一的问题是,当用户输入时,文本消失在键盘下方,因为显然UIScrollView不知道滚动到光标位置。

有没有简单的方法可以检索光标位置并告诉UIScrollView在那里滚动?

--- --- EDIT

从类似的here开始(有人试图用UITableView做类似的事情),我设法制作了一个不断增长的,可编辑的UITextView,它具有几乎滚动的固定背景完美。现在唯一的问题是:

  1. 如果用户输入速度特别快,文字会向上移动,会有轻微的颤抖。
  2. 如果用户隐藏键盘,选择屏幕底部的文本,然后再次显示键盘,他们必须在文本再次可见之前键入几个字母 - 它不会立即向上滚动。< / LI>
  3. 当用户隐藏键盘时,动画作为滚动视图的框架填满屏幕感觉不太合适。
  4. 这是代码 - 如果有人能进一步完善它,我真的很感激......

    #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];
    }
    

3 个答案:

答案 0 :(得分:5)

很酷,你发现了我的帖子,很高兴它有用!

我相信你可能不会因为这一行而看到底线:

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的大小(但在触摸时选择特定点时不行)。 / p>

答案 1 :(得分:1)

找不到UITextView中任何文字或光标的屏幕坐标都没有简单的方法。

您应该注册的是UIKeyboardWillShowNotificationUIKeyboardWillShowNotification。在回调中,您可以调整size的{​​{1}}或contentInsets来调整键盘的大小。

通知UIScrollView中提供了键盘的大小,甚至是动画持续时间,因此您可以以精美的动画方式进行操作。

您可以在此处找到更多信息和示例代码:http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html

答案 2 :(得分:1)

不严格地回答你的问题,但这里有一个不同的方法来记录笔记背景技巧:http://www.cocoanetics.com/2010/03/stuff-you-learn-from-reverse-engineering-notes-app/

我已经习惯了它并且效果很好。