Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make my UITextView the height of the screen minus the keyboard under iOS 7?

My code was working fine under iOS 6, but under iOS 7 I can't seem to make my UITextView the height of the device's screen minus the keyboard (in other words, when the keyboard is up, have the UITextView still be fullscreen, but not go under the keyboard).

For one, when I put the UITextView in my view controller (which is embedded in a navigation controller) it has to be under the nav bar as well, otherwise it starts too far down.

From there I tried all of these examples:

self.textView.contentInset = UIEdgeInsetsMake(0, 0, 230, 0);
self.textView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, 230, 0);
self.textView.textContainerInset = UIEdgeInsetsMake(0, 0, 230, 0);

Where in each the keyboard still goes over the textview at some points. I also tried setting a height constraint and manipulating the constant, but no luck.

self.height.constant = self.height.constant - 240.0;

(Where height is the constraint's outlet.)

These have all been tried before and after a [self.textField becomeFirstResponder]; call.

How am I to accomplish this? I just want a full screen UITextView where the caret thing won't go under the keyboard, but it such basic functionality seems crazy under iOS 7.

like image 813
Doug Smith Avatar asked Oct 28 '13 00:10

Doug Smith


2 Answers

The "correct" way to do this pre-iOS7 has always been to adjust the contentInset property of the UITextView (a UIScrollView subclass) when the keyboard shows or hides. I haven't investigated personally but this appears not to work well in iOS7 because the bottom inset is either not honored or there is some issue with the cursor still going below the keyboard. See this question for reference.

In your case where you're using auto-layout, and all you want is a "full screen" text view you can simply adjust a single constraint when the keyboard shows or hides. This will adjust the height of your text view:

@implementation TSViewController
{
    IBOutlet NSLayoutConstraint* _textViewSpaceToBottomConstraint;
}

- (void) dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver: self];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void) keyboardWillShow: (NSNotification*) n
{
    NSValue* bv = n.userInfo[UIKeyboardFrameEndUserInfoKey];
    CGRect br = [bv CGRectValue];

    _textViewSpaceToBottomConstraint.constant = br.size.height;
}

- (void) keyboardWillHide: (NSNotification*) n
{
    _textViewSpaceToBottomConstraint.constant = 0;
}

@end

In your storyboard, drop your UITextView on your view-controller view and add 4 constraints to glue its edges to the edges of the view-controller view. Connect the bottom space constraint to the IBOutlet _textViewSpaceToBottomConstraint in the view controller.

You can likely play with this a bit and adjust the size inside an animation block, borrowing the animation time and curve from the keyboard notification.

I'd be interested in seeing a version of this that sets the contentInset and has it work properly...

enter image description here

EDIT

Here is another SO question that covers this topic, with a solution. Apparently the issue with the caret (cursor) going out of frame is the same issue I mentioned above re. the "correct" way to do this, which is to set the contentInset property. So a fix for this problem should enable you to simply adjust the contentInset vs. changing the text view frame (via .frame or via a constraint).

EDIT 2

One last thought on this. In iOS7, the keyboard is translucent. The user should be able to see content behind the keyboard. By resizing the UITextView to be above the keyboard, this won't ever happen. So the "correct" solution is still to adjust the contentInset for the bottom of the text view, to add the height of the keyboard to the scrollable area of the text view. Then, in an ideal world, you would adjust the contentOffset of the text view to keep the caret in view when the keyboard appears. Finally, add to that the 'fix' to keep the caret in position above the keyboard when it's displayed but the user is entering newlines.

like image 70
TomSwift Avatar answered Oct 17 '22 18:10

TomSwift


The solution to this in iOS7 is to put the tableview into a container view, which becomes your primary view. You can add constraints to it so that its attached to the topLayoutGuide. Add a second placeholder view that is constrained to the bottom of the table on top, and to the bottomLayoutConstraint on the bottom. Add a constraint to this placeholder view so its height is 0, and keep an ivar pointing to it. You can provide a UITableViewController subclass that overrides the tableView property with the real tableview.

When the keyboard is ready to pop, you can get the height of the table from the notification (code below), and animate the setting of the constraint property of the placeholder view to the height of the keyboard.

The keyboard notification code is below:

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
    [defaultCenter addObserver:self selector:@selector(keyboardMoving:) name:UIKeyboardWillShowNotification object:nil];
    [defaultCenter addObserver:self selector:@selector(keyboardMoving:) name:UIKeyboardWillHideNotification object:nil];
    [defaultCenter addObserver:self selector:@selector(keyboardMoving:) name:UIKeyboardDidHideNotification object:nil];
}

-(void)viewWillDisappear:(BOOL)animated
{
    NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
    [defaultCenter removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [defaultCenter removeObserver:self name:UIKeyboardWillHideNotification object:nil];
    [defaultCenter removeObserver:self name:UIKeyboardDidHideNotification object:nil];

    [super viewWillDisappear:animated];
}
- (void)keyboardMoving:(NSNotification *)note
{
    NSString *msg = note.name;

    if([msg isEqualToString:UIKeyboardWillShowNotification] && !_keyboardUp) {
        _keyboardUp = YES;
        [[note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&_animationDuration];
        [[note.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&_keyboardRect];
        NSLog(@"ORIG KEYBOARD %@", NSStringFromCGRect(_keyboardRect));
        _keyboardRect = [self.view convertRect:_keyboardRect fromView:nil];
        NSLog(@"NEW KEYBOARD %@", NSStringFromCGRect(_keyboardRect));
        _animate = YES;
    } else
    if([msg isEqualToString:UIKeyboardWillHideNotification] && _keyboardUp) {
        _keyboardUp = NO;
        _animate = YES;
    } else
    if([msg isEqualToString:UIKeyboardDidHideNotification]) {
        _keyboardUp = NO;
        _animate = NO;
    }
}

- (BOOL)isKeyboardMovingUp
{
    return _keyboardUp == YES && _animate == YES;
}
- (BOOL)isKeyboardMovingDown
{
    return _keyboardUp == NO && _animate == YES;
}

- (BOOL)isKeyboardDown
{
    return _keyboardUp == NO && _animate == NO;
}

What you will need to do is before the keyboard pops, or just always, create a empty container view and add it to , add the table to that view, add

like image 24
David H Avatar answered Oct 17 '22 19:10

David H