27

How is that possible that my editable UITextView (placed inside a straightforward UIViewController inside a UISplitView that acts as delegate for the UITextView) is not showing text from the beginning but after something like 6-7 lines?

Screenshot

I didn't set any particular autolayout or something similar, trying to delete text doesn't help (so no hidden chars or something).

I'm using iOS 7 on iPad, in storyboard looks good... The problem is the same on iOS simulator and real devices. I'm getting mad :P

Here's some code. This is the ViewController viewDidLoad()

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.itemTextField.delegate = self;
    self.itemTextField.text = NSLocalizedString(@"NEWITEMPLACEHOLDER", nil);
    self.itemTextField.textColor = [UIColor lightGrayColor]; //optional
}

And here are the overridden functions for the UITextView I'm using some code I've found on StackOverflow to simulate a placeholder for the view (the same stuff on iPhone version of the storyboard works fine)...

// UITextView placeholder
- (void)textViewDidBeginEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:NSLocalizedString(@"NEWITEMPLACEHOLDER", nil)]) {
        textView.text = @"";
        textView.textColor = [UIColor blackColor]; //optional
    }
    [textView becomeFirstResponder];
}

- (void)textViewDidEndEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:@""]) {
        textView.text = NSLocalizedString(@"NEWITEMPLACEHOLDER", nil);
        textView.textColor = [UIColor lightGrayColor]; //optional
    }
    [textView resignFirstResponder];
}

-(void)textViewDidChange:(UITextView *)textView
{
    int len = textView.text.length;
    charCount.text = [NSString stringWithFormat:@"%@: %i",  NSLocalizedString(@"CHARCOUNT", nil),len];
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
    return YES;
}
napolux
  • 15,574
  • 9
  • 51
  • 70

8 Answers8

34

Try to call -sizeToFit after passing the text. This answer could be useful to Vertically align text within a UILabel.
[UPDATE]
I update this answer o make it more readable.
The issue is that from iOS7, container view controllers such as UINavigationController or UITabbarController can change the content insets of scroll views (or views that inherit from it), to avoid content overlapping. This happens only if the scrollview is the main view or the first subviews. To avoid that you should disable this behavior by setting automaticallyAdjustsScrollViewInsets to NO, or overriding this method to return NO.

Community
  • 1
  • 1
Andrea
  • 26,120
  • 10
  • 85
  • 131
  • Not working. The `UITextView` is resized to something like 30 x 20 pixels and no text is shown. – napolux Oct 19 '13 at 16:48
  • Are you using attributed string? – Andrea Oct 19 '13 at 16:49
  • 8
    Ok, maybe lorik pointed me to the right direction. The inset is due to the fact that UITextField has scrool capabilities, since iOS7 in sotryboard each sort of scrollview is automatically inset of the size of statusbar+navbar (in UINavController). Probably here you put a constraint form the top, that fix the "top distance" but you didn't unselect the automatic edge option. So check on storyboard for this VC and unselect adjust scroll view insets option. – Andrea Oct 20 '13 at 08:56
15

I got through the same kind of issue.

Solved it by disabling the automatic scrollView insets adjustement :

if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")){
    self.automaticallyAdjustsScrollViewInsets = NO; // Avoid the top UITextView space, iOS7 (~bug?)
}
Tanguy G.
  • 2,143
  • 19
  • 18
7

This is a fairly common problem, so I would create a simple UITextView subclass, so that you can re-use it and use it in IB.

I would used the contentInset instead, making sure to gracefully handle the case where the contentSize is larger than the bounds of the textView

@interface BSVerticallyCenteredTextView : UITextView

@end

@implementation BSVerticallyCenteredTextView

- (id)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        [self addObserver:self forKeyPath:@"contentSize" options:  (NSKeyValueObservingOptionNew) context:NULL];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder])
    {
        [self addObserver:self forKeyPath:@"contentSize" options:  (NSKeyValueObservingOptionNew) context:NULL];
    }
    return self;
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"contentSize"])
    {
        UITextView *tv = object;
        CGFloat deadSpace = ([tv bounds].size.height - [tv contentSize].height);
        CGFloat inset = MAX(0, deadSpace/2.0);
        tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right);
    }  
}

- (void)dealloc
{
    [self removeObserver:self forKeyPath:@"contentSize"];
}

@end
Jeff Holliday
  • 760
  • 6
  • 7
4

use -observerForKeyPath with contentSize KeyPath

Look some code at My Blog (don't focus on Thai Language)

http://www.macbaszii.com/2012/10/ios-dev-uitextview-vertical-alignment.html

Rishil Patel
  • 1,977
  • 3
  • 14
  • 30
Kiattisak Anoochitarom
  • 2,157
  • 1
  • 20
  • 15
2

Inspired by Kiattisak, I've implemented vertical alignment as a category over UITextView so that you can control the vertical alignment of legacy UITextView.

You can find it as a gist here.

Rishil Patel
  • 1,977
  • 3
  • 14
  • 30
Tom Susel
  • 3,397
  • 1
  • 24
  • 25
1

I had the same issue with iOS 8.1, and none of these suggestions worked.

What did work was to go into the Storyboard, and drag my UITableView or UITextView so that it was no longer the first subview of my screen's UIView.

http://www.codeproject.com/Tips/852308/Bug-in-XCode-Vertical-Gap-Above-UITableView

It seems to be linked to having a UIView embedded in a UINavigationController.

Bug ? Bug ? Did I say "bug" ...?

;-)

Mike Gledhill
  • 27,846
  • 7
  • 149
  • 159
1

Swift version of Tanguy.G's answer:

if(UIDevice.currentDevice().systemVersion >= "7.0") {
            self.automaticallyAdjustsScrollViewInsets = false; // Avoid the top UITextView space, iOS7 (~bug?)
  }
  • Note too, that you often have to call for layout: `view.layoutIfNeeded()`. It's very annoying and exceptionally badly done in iOS. – Fattie Feb 24 '17 at 04:39
0

Check top content inset of textView in -viewDidLoad:

NSLog(@"NSStringFromUIEdgeInsets(self.itemTextField.contentInset) = %@", NSStringFromUIEdgeInsets(self.itemTextField.contentInset));

Reset it in storyboard if it is not zero

LorikMalorik
  • 2,001
  • 1
  • 14
  • 14