UITextView
is a subclass of UIScrollView
, so the answer involves the contentOffset
property, which is what is being changed, not the insets or the content size. If the scroll position is correct when the view first appears, then you can store the content offset for later recall.
YourViewController.h snipped
@interface YourViewController : UIViewController <UITextViewDelegate, UIScrollViewDelegate>
@property(nonatomic, weak) IBOutlet UITextView *textView;
@end
YourViewController.m snippet
@implementation YourViewController {
@private
BOOL _freezeScrolling;
CGFloat _lastContentOffsetY;
}
// UITextViewDelegate
- (void)textViewDidBeginEditing:(UITextView *)textView {
// tell the view to hold the scrolling
_freezeScrolling = YES;
_lastContentOffsetY = self.textView.contentOffset.y;
}
// UITextViewDelegate
- (void)textViewDidEndEditing:(UITextView *)textView {
_freezeScrolling = NO;
}
// UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (_freezeScrolling) {
// prevent the scroll view from actually scrolling when we don't want it to
[self repositionScrollView:scrollView newOffset:CGPointMake(scrollView.contentOffset.x, _lastContentOffsetY)];
}
}
// UIScrollViewDelegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// scroll prevention should only be a given scroll event and turned back off afterward
_freezeScrolling = NO;
}
// UIScrollViewDelegate
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
// when the layout is redrawn, scrolling animates. this ensures that we are freeing the view to scroll
_freezeScrolling = NO;
}
/**
This method allows for changing of the content offset for a UIScrollView without triggering the scrollViewDidScroll: delegate method.
*/
- (void)repositionScrollView:(UIScrollView *)scrollView newOffset:(CGPoint)offset {
CGRect scrollBounds = scrollView.bounds;
scrollBounds.origin = offset;
scrollView.bounds = scrollBounds;
}
What's also important to note in the code sample above is the last method. Calling any sort of setContentOffset:
will actually trigger scrolling, which results in calling scrollViewDidScroll:
. So calling setContentOffset:
results in an infinite loop. Setting the scroll bounds is the workaround for this.
In a nutshell, we tell the view controller to prevent the UITextView
from scrolling when we detect that the user has selected the text for editing. We also store the current content offset (since we know that the position is what we want). If the UITextView
tries to scroll, then we hold the content offset in place until the scroll has stopped (which triggers either scrollViewDidEndDecelerating:
or scrollViewDidEndScrollingAnimation:
). We also unfreeze the scrolling when the user is done editing.
Remember, this is a basic example, so you'll need to tweak the code based on the exact behavior you want.