Stop UITextView from jumping when programmatically

2020-02-09 06:50发布

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?

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

13条回答
相关推荐>>
2楼-- · 2020-02-09 07:28

Finally try this, checked on iOS 10

let offset = textView.contentOffset
textView.attributedText = newValue
OperationQueue.main.addOperation {
    self.textView.setContentOffset(offset, animated: false)
}
查看更多
看我几分像从前
3楼-- · 2020-02-09 07:30

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

查看更多
Ridiculous、
4楼-- · 2020-02-09 07:33

This decision works for iOS 8:

let offset = textView.contentOffset
textView.text = newText
textView.layoutIfNeeded()
textView.setContentOffset(offset, animated: false)

It is necessary to call exactly setContentOffset:animated: because only this will cancel animation. textView.contentOffset = offset will not cancel the animation and will not help.

查看更多
劫难
5楼-- · 2020-02-09 07:35

The following two solutions don't work for me on iOS 8.0.

textView.scrollEnabled = NO;
[textView.setText: text];
textView.scrollEnabled = YES;

and

CGPoint offset = textView.contentOffset;
[textView.setText: text];
[textView setContentOffset:offset];

I setup a delegate to the textview to monitor the scroll event, and noticed that after my operation to restore the offset, the offset is reset to 0 again. So I instead use the main operation queue to make sure my restore operation happens after the "reset to 0" option.

Here's my solution that works for iOS 8.0.

CGPoint offset = self.textView.contentOffset;
self.textView.attributedText = replace;
[[NSOperationQueue mainQueue] addOperationWithBlock: ^{
    [self.textView setContentOffset: offset];
}];
查看更多
虎瘦雄心在
6楼-- · 2020-02-09 07:38

I found a solution that works reliably in iOS 6 and 7 (and probably earlier versions). In a subclass of UITextView, do the following:

@interface MyTextView ()
@property (nonatomic) BOOL needToResetScrollPosition;
@end

@implementation MyTextView

- (void)setText:(NSString *)text
{
    [super setText:text];
    self.needToResetScrollPosition = YES;
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    if (self.needToResetScrollPosition) {
        self.contentOffset = CGPointMake(0, 0);
        self.needToResetScrollPosition = NO;
    }
}

None of the other answers work in iOS 7 because it will adjust the scroll offsets at display time.

查看更多
劳资没心,怎么记你
7楼-- · 2020-02-09 07:40

in iOS 7. There seams to be a bug with sizeThatFits and having linebreaks in your UITextView the solution I found that works is to wrap it by disabling scrolling. Like this:

textView.scrollEnabled = NO;
CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
textView.scrollEnabled = YES;

and weird jumping has been fixed.

查看更多
登录 后发表回答