30

I have a UITextView included in a UITableViewCell. The layout is correct when the views are initially displayed, but once I click in the UITextView it automatically scrolls up a bit and the top half of the characters on the first line becomes invisible.

This image is when the UITextView is not active:
UITextView not active http://gerodt.homeip.net/uitextview-notactive.png

And this one is when I clicked in the UITextView to make it active:
UITextView active http://gerodt.homeip.net/uitextview-active.png

I do not the UITextView to scroll up at all, it should simple stay fixed. How can I achieve this? I already tried several settings in Interface Builder, but no luck so far.

Any suggestions are appreciated.

Gero

DShah
  • 9,768
  • 11
  • 71
  • 127
Gero
  • 2,967
  • 6
  • 22
  • 20

6 Answers6

61

UITextView is a subclass of UIScrollView, so it has a configurable contentInset property. Unfortunately, if you try to change contentInset on a UITextView instance, the bottom edge inset always gets reset to 32. I've run into this before with short UITextView frames and found this to be an issue. I suspect this is what is causing your problem, but you should check the contentInset of your textview in the debugger to be sure.

The workaround/solution is simple: subclass UITextView and override the contentInset method so that it always returns UIEdgeInsetZero. Try this:

//
// BCTextView
//
// UITextView seems to automatically be resetting the contentInset
// bottom margin to 32.0f, causing strange scroll behavior in our small
// textView.  Maybe there is a setting for this, but it seems like odd behavior.
// override contentInset to always be zero.
//
@interface BCZeroEdgeTextView : UITextView
@end

@implementation BCZeroEdgeTextView

- (UIEdgeInsets) contentInset { return UIEdgeInsetsZero; }

@end 
Brian Chapados
  • 4,916
  • 2
  • 17
  • 7
16

This is how UITextView behaves according to Apple's engineer this is intended and UITextView is meant for text that are at least a few lines in height. There is no work around to this, use a UITextField instead or increase your UITextView to at least 3 lines in height.

erotsppa
  • 14,248
  • 33
  • 123
  • 181
  • 1
    Thanks. I increased the high to 55 and with a fontsize of 12 it now works as I want. – Gero Jul 24 '09 at 19:22
6

You can also just do:

textView.contentInset=UIEdgeInsetsZero;

in your delegate file.

Snowman
  • 31,411
  • 46
  • 180
  • 303
2

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.

mikeho
  • 6,790
  • 3
  • 34
  • 46
1

I was experiencing a similar issue with undesired UITextView scrolling. I finally managed to fix it by resetting the contentSize at the end of my keyboardDidShow:

- (void)keyboardDidShow:(NSNotification *)notification {
  textView.contentSize = CGSizeZero;
}

You also will need to register for the keyboard notification, like so:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];

In my case I didn't want any scrolling since I was resetting the frame to the height of the textView's contentSize when textViewDidChange (growing textview inside a UIScrollView).

DShah
  • 9,768
  • 11
  • 71
  • 127
funroll
  • 35,925
  • 7
  • 54
  • 59
0

Try putting in Redraw on the textview instead of Scale to Fill. You still might have to capture the delegate and keep the content offset but it should at least prevent the jump to point (0,0). Also Autoresizes subview must be turned off. It was jumping to top of textview every time on me too and this solved that problem.

enter image description here

Scott Jenkins
  • 321
  • 2
  • 7