Using NSLayoutConstraint on UITextView resets contentSize to {0,0} - iphone

I have a UITextView on which I am using an NSLayoutConstraint to dodge the keyboard. Here's the constraint:
self.textViewBottomConstraint = [NSLayoutConstraint constraintWithItem:textView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
[self.view addConstraint:self.textViewBottomConstraint];
When the keyboard shows/hides I animate the constraint by setting the constraint constant to the keyboard height. However, doing so for some reason resets the contentSize to {0,0}, thus breaking scrolling. I've added a hack to handleKeyboardDidHide: to reset the contentSize to what it was before being reset, but this has some ugly side effects such as the scroll position being reset and the view not scrolling to the cursor position until typing starts.
- (void) handleKeyboardDidShow:(NSNotification *)notification
{
CGFloat height = [KeyboardObserver sharedInstance].keyboardFrame.size.height;
self.textView.constant = -height;
[self.view layoutIfNeeded];
}
- (void) handleKeyboardDidHide:(NSNotification *)notification
{
// for some reason, setting the bottom constraint resets the contentSize to {0,0}...
// so let's save it before and reset it after.
// HACK
CGSize size = self.textView.contentSize;
self.textView.constant = 0.0;
[self.view layoutIfNeeded];
self.textView.contentSize = size;
}
Anyone know how to avoid this issue altogether?

I don't know what's wrong with your code, and we can deal with that in detail if you want. But as an initial suggestion, if possible, don't resize the UITextView: just change its content and scroll insets, like this:
- (void) keyboardShow: (NSNotification*) n {
NSDictionary* d = [n userInfo];
CGRect r = [d[UIKeyboardFrameEndUserInfoKey] CGRectValue];
self.tv.contentInset = UIEdgeInsetsMake(0,0,r.size.height,0);
self.tv.scrollIndicatorInsets = UIEdgeInsetsMake(0,0,r.size.height,0);
}
Even so, I find that you have to wait until the keyboard hide animation completes before resetting those values:
- (void) keyboardHide: (NSNotification*) n {
NSDictionary* d = [n userInfo];
NSNumber* curve = d[UIKeyboardAnimationCurveUserInfoKey];
NSNumber* duration = d[UIKeyboardAnimationDurationUserInfoKey];
[UIView animateWithDuration:duration.floatValue delay:0
options:curve.integerValue << 16
animations:
^{
[self.tv setContentOffset:CGPointZero];
} completion:^(BOOL finished) {
self.tv.contentInset = UIEdgeInsetsZero;
self.tv.scrollIndicatorInsets = UIEdgeInsetsZero;
}];
}
(It may be that that trick would help your code somehow as well.)

Related

TableView content Height Changed after keyboard show

I have a Scroll View, Table View, and textfield at the bottom which will trigger a keyboard show after clicked.
Table View is just a subview inside the Scroll view which to show some comments for that photos.
At the beginning, the tableView height shows correctly. However, after clicked any textField in the class, the tableView height changed. Anyone has solutions for this.
I have tested for the keyboard height. It will affect the additional height of the UITableView.
But I have no any ideas on how to keep the height the same as before the keyboard shows.
Please help.
Here is some codes,
//---when a TextField view begins editing---
-(BOOL) textFieldDidBeginEditing:(UITextField *)textFieldView {
currentTextField = textFieldView;
return YES;
}
-(BOOL) textFieldShouldReturn:(UITextField *) textFieldView {
[textFieldView resignFirstResponder];
return NO;
}
//---when a TextField view is done editing---
-(void) textFieldDidEndEditing:(UITextField *) textFieldView {
currentTextField = nil;
}
//---when the keyboard appears---
-(void) keyboardDidShow:(NSNotification *) notification {
if (keyboardIsShown) return;
NSDictionary* info = [notification userInfo];
//---obtain the size of the keyboard---
NSValue *aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
//---resize the scroll view (with keyboard)---
CGRect viewFrame = [v_comment_editor frame];
viewFrame.size.height -= keyboardSize.height;
v_comment_editor.frame = viewFrame;
//---scroll to the current text field---
CGRect textFieldRect = [currentTextField frame];
[v_comment_editor scrollRectToVisible:textFieldRect animated:YES];
keyboardIsShown = YES;
}
//---when the keyboard disappears---
-(void) keyboardDidHide:(NSNotification *) notification {
NSDictionary* info = [notification userInfo];
//---obtain the size of the keyboard---
NSValue *aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
//---resize the scroll view back to the original size (without keyboard)---
CGRect viewFrame = [v_comment_editor frame];
viewFrame.size.height += keyboardSize.height;
v_comment_editor.frame = viewFrame;
keyboardIsShown = NO;
}
-(void) viewWillDisappear:(BOOL)animated {
//---removes the notifications for keyboard---
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
The code you show does resize the views when the keyboard is shown. It looks like it is supposed to return to the correct size when keyboard is hidden again.
If you have trouble with one of the subviews, it is possible that it's autoresize masks are set up in a weird way.
The simplest way around it would be to have an instance variable in your handling class like:
CGRect tableframe;
and store the correct frame to it in the keyboarddidshow function, and restore the table to the original frame in ** keyboardDidHide** method.
Might help to look into UIScrollView's contentOffset and contentInset properties. Also it will help to understand the difference between the bounds of the scrollView and the frame.
I strongly advise creating a simple test project and experimenting with the above concepts. Having a thorough understanding will make your life much easier.
Note: be careful of translucent navigationBars and their affect on the above properties.
For all of other people reference, in case you have this problem again.
The solution i used to go through this is to reset the size again in the keyboardDidHide, and code is as below:
CGRect frame = tbl_comment.frame;
frame.size.height = 145;
tbl_comment.frame = frame;

UIScrollView not scrolling when keyboard covers active UITextField (Using apple's example)

I'm new to iOS programming, and I'm having trouble with getting a UIScrollView to move when editing a UITextField that is obscured by the keyboard. The code is straight out of Apple's documentation but it's not working for some reason.
Through debugging I've found that the notifications seem to be getting passed correctly (i.e. it logs "View should resize", but only when activeField is the textField that is under the keyboard) and scrollpoint is being set correctly, but the scrollview still does not move. Also, I'm reasonably sure that the delegation pattern is correct (ViewController is delegate of textFields as well as scrollView)
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
NSLog(#"%#",#"view should resize");
}
}
Seeing as the code is straight from the documentation, I'm probably just missing something simple. Can anyone point me in the direction of things to check for?
Apple's example has a bug in that it doesn't explicitly set the scroll view's content size and thus uses the default content size which is (0, 0). I fixed this problem by adding this code in my view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Set the scroll view's content size to be the same width as the
// application's frame but set its height to be the height of the
// application frame minus the height of the navigation bar's frame
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
CGRect navigationFrame = [[self.navigationController navigationBar] frame];
CGFloat height = applicationFrame.size.height - navigationFrame.size.height;
CGSize newContentSize = CGSizeMake(applicationFrame.size.width, height);
((UIScrollView *)self.view).contentSize = newContentSize;
}

UIScrollView scrolling issue

I have a scrollview which works perfectly until I dismiss the keyboard. After that it scrolls but not all the way down anymore... I am a bit puzzled.
in viewDidLoad I save the frame so I can reset it later
frame = self.scrollView.frame;
NSLog(#"Height beginning: %f",self.scrollView.frame.size.height);
offset = self.scrollView.contentOffset;
For debugging I checked the scrollview's height which is 629
in my keyboardDidHide method I set the old frame back
self.scrollView.frame = frame;
NSLog(#"Height end: %f",self.scrollView.frame.size.height);
// Reset the scrollview to previous location
self.scrollView.contentOffset = offset;
The debugging output is 629 as well which means that the scrollview's height has been set to the old value. It does scroll but when I let go it bounces all the way back to the beginning...
EDIT:
When using 480 as height it is not filling the whole screen because of iphone 4
Some code
-(void) keyboardDidShow: (NSNotification *)notif
{
NSLog(#"Keyboard is visible");
// If keyboard is visible, return
if (keyboardVisible) {
NSLog(#"Keyboard is already visible. Ignore notification.");
return;
}
// Get the size of the keyboard.
CGRect keyboardEndFrame;
[[notif.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
// Resize the scroll view to make room for the keyboard
CGRect viewFrame = frame;
viewFrame.size.height = 200;
//viewFrame.size.height -= keyboardEndFrame.size.height;
self.scrollView.frame = viewFrame;
offset = self.scrollView.contentOffset;
CGRect textFieldRect;
if(!activeField)
textFieldRect = comment.frame;
else
textFieldRect = activeField.frame;
textFieldRect.origin.y += 10;
[self.scrollView scrollRectToVisible:textFieldRect animated:YES];
// Keyboard is now visible
keyboardVisible = YES;
}
-(void) keyboardDidHide: (NSNotification *)notif
{
// Is the keyboard already shown
if (!keyboardVisible) {
NSLog(#"Keyboard is already hidden. Ignore notification.");
return;
}
//viewFrame.size.height -= keyboardEndFrame.size.height;
self.scrollView.frame = frame;
[self.scrollView setContentSize:CGSizeMake(320, 700)];
// Reset the scrollview to previous location
self.scrollView.contentOffset = offset;
// Keyboard is no longer visible
keyboardVisible = NO;
}

UITextView not resizing along woth keyboard showing

I have a text view (nib-file) with a UITextView maximised the entire view.
When ever i push the viewcontroller on to the navigationcontroller i load the keyboard in the text view's viewDidLoad method.
My problem is that when i write text that has more lines than there is space for, the text is behind the keyboard. I followed the sampe from Apple here.
If i log the "newTextViewFrame" it's NULL. but if i debug the thing it has a fine value.
Even if i hardcode it, the UITextView doesent change size.
Here is my code:
- (void)keyboardWillShow:(NSNotification *)notification {
/*
Reduce the size of the text view so that it's not obscured by the keyboard.
Animate the resize so that it's in sync with the appearance of the keyboard.
*/
NSDictionary *userInfo = [notification userInfo];
// Get the origin of the keyboard when it's displayed.
NSValue* aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
// Get the top of the keyboard as the y coordinate of its origin in self's view's coordinate system. The bottom of the text view's frame should align with the top of the keyboard's final position.
CGRect keyboardRect = [aValue CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
CGFloat keyboardTop = keyboardRect.origin.y;
CGRect newTextViewFrame = self.view.bounds;
newTextViewFrame.size.height = keyboardTop; - (self.view.bounds.origin.y);
// Get the duration of the animation.
NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval animationDuration;
[animationDurationValue getValue:&animationDuration];
// Animate the resize of the text view's frame in sync with the keyboard's appearance.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:animationDuration];
createListingTextView.frame = newTextViewFrame;
[UIView commitAnimations];
NSLog(#"Textview resized: %#", newTextViewFrame);
}
My task i simple: Implement a View with a UITextView and the keyboard open and scroll the UITextView when ever the text exceeds.
Please advice...
There is a semi-colon after keyboardTop on the line
newTextViewFrame.size.height = keyboardTop; - (self.view.bounds.origin.y);
This would certainly prevent newTextViewFrame from getting the adjusted height.

UITableView won't scroll after editing view frame and origin

I'm trying to implement a UITextView in a table cell at the bottom of a table view.
I've tried the suggestions here Making a UITableView scroll when text field is selected, and other solutions as well, but they're a bit different because I have to artificially add extra height to the current view in order to create space for the keyboard.
Here's what I added to the previous solution in order to port it to my app.
-(void) keyboardWillShow:(NSNotification *)note {
CGRect frame = self.view.frame;
frame.size.height += keyboardHeight;
frame.origin.y -= keyboardHeight;
self.view.frame = frame;
}
-(void) keyboardWillHide:(NSNotification *)note
{
CGRect frame = self.view.frame;
frame.size.height -= keyboardHeight;
frame.origin.y += keyboardHeight;
}
Doing this will correctly add the height to the view and scroll to the cell, but after restoring the original view's height, scrolling beyond the current visible view becomes impossible, even though there is valid content outside of the boundaries (I see the text view before the scroll bar bounces back).
If I try to save the tableview's frame or bounds (not the view) in keyboardWillShow and restore them in keyboardWillHide, the scrolling will be restored, but the view will be cut in half.
Are there any remedies to this besides hard-coding the additional height to the bottom of the view?
I was able to solve my problem of the locked scrolling by removing the code that edits the view's origin. In addition, I implemented scrolling to the bottom cell by using the tableview's contentSize property in my calculations.
-(void) keyboardWillShow:(NSNotification *)note
{
if(!isKeyboardShowing)
{
isKeyboardShowing = YES;
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardBounds];
CGFloat keyboardHeight = keyboardBounds.size.height;
CGRect frame = self.view.frame;
frame.size.height += keyboardHeight;
self.view.frame = frame;
CGPoint scrollPoint = frame.origin;
scrollPoint.y += _tableView.contentSize.height - keyboardHeight;
[_tableView setContentOffset:scrollPoint animated:YES];
}
}