views:

1514

answers:

6

Hi there,

I have to update a small amount of text in a scrolling UITextView. I'll only be inserting a character where the cursor currently is, and I'll be doing this on a press of a button on my navigation bar.

My problem is that whenever I call the setText method of the text view, it jumps to the bottom of the text. I've tried using contentOffset and resetting the selectedRange but it doesn't work! Here's my example:

// Remember offset and selection
CGPoint contentOffset = [entryTextView contentOffset];
NSRange selectedRange = [entryTextView selectedRange];
// Update text
entryTextView.text = entryTextView.text;
// Try and reset offset and selection
[entryTextView setContentOffset:contentOffset animated:NO];
[entryTextView setSelectedRange: selectedRange];

Is there any way you can update the text without any scroll movement at all... as if they'd just typed something on the keyboard?

Many thanks,

Michael

Edit:

I've tried using the textViewDidChange: delegate method but it's still not scrolling up to the original location.

- (void)textViewDidChange:(UITextView *)textView {
    if (self.programChanged) {
     [textView setSelectedRange:self.selectedRange];
     [textView setContentOffset:self.contentOffset animated:NO];
     self.programChanged = NO;
    }
}

- (void)changeButtonPressed:(id)sender {
    // Remember position
    self.programChanged = YES;
    self.contentOffset = [entryTextView contentOffset];
    self.selectedRange = [entryTextView selectedRange];
    // Update text
    entryTextView.text = entryTextView.text;
}
A: 

Take a look at the UITextViewDelegate, I believe the textViewDidChangeSelection method may allow you to do what you need.

drewh
Thanks for your input. What should happen in this delegate method? Is this when the final resetting of the contentOffset and selectedRange should take place?
Michael Waterfall
A: 

UITextViewDelegate is what you want, but I think you'd rather modify textViewDidChange: since you're not changing the selection of the view, you're changing the text in the view. What you want to do is define your own delegate object, then implement that method and do the content offsetting.

The documentation exists at this page (registration as Apple developer required).

Tim
I've tried using the textViewDidChange: delegate method but it's still not scrolling up to the original location. Please see my amended question for the delegate code I have tried.
Michael Waterfall
A: 

Not so elegant solution- but it works so who cares:

  • (IBAction)changeTextProgrammaticaly{ myTextView.text = @"Some text"; [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(rewindOffset) userInfo:nil repeats:NO]; }

  • (void)rewindOffset{ [myTextView setContentOffset:CGPointMake(0,0) animated: NO]; }

Tiger
(This solution came after all the other solutions suggested here didn't work for me)
Tiger
+2  A: 

If you use iPhone 3.0 or later, you can solve this problem:

textView.scrollEnabled = NO;

//You should know where the cursor will be(if you update your text by appending/inserting/deleting you can know the selected range) so keep it in a NSRange variable.

Then update text: textView.text = yourText;

textView.scrollEnabled = YES; textView.selectedRange = range;//you keep before

It should work now (no more jumping)

Regards Meir Assayag

Meir Assayag
A: 

No of the suggested solutions worked for me. -setContentOffset:animated: gets triggered by -setText: 3 times with animated YES and a contentOffset of the end (minus the default 8pt margin of a UITextView). I wrapped the -setText: in a guard:

textView.contentOffsetAnimatedCallsDisabled = YES;
textView.text = text;
textView.contentOffsetAnimatedCallsDisabled = NO;

In a UITextView subclass in -setContentOffset:animated: put

if (contentOffsetAnimatedCallsDisabled) return; // early return

among your other logic. Don’t forget the super call. This works.

Raphael

Raphael Schaad
Thanks for the suggestion, I'll try it out!
Michael Waterfall
It's not private API, you override UITextView and implement the behavior yourself according to my example. Meir Assayag's answer didn't work in my case and on iOS 3.2.
Raphael Schaad
A: 

Building on Meir's suggestion, here's code that removes the selection programmatically (yes I know there's a selection menu button that does it too, but I'm doing something a bit funky) without scrolling the text view.

NSRange selectedRange = textView.selectedRange;
textView.scrollEnabled = NO;
// I'm deleting text. Replace this line with whatever insertions/changes you want
textView.text = [textView.text
                stringByReplacingCharactersInRange:selectedRange withString:@""];
selectedRange.length = 0;
// If you're inserting text, you might want to increment selectedRange.location to be
// after the text you inserted
textView.selectedRange = selectedRange;
textView.scrollEnabled = YES;
Jacques