I've been using the following code in my view controller to update the content offset of a UITextView when the keyboard gets displayed:
- (void)keyboardWasShown:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIEdgeInsets contentInsets = UIEdgeInsetsMake( 0.0, 0.0, keyboardRect.size.height, 0.0 );
self.textView.contentInset = contentInsets;
self.textView.scrollIndicatorInsets = contentInsets;
}
With the keyboard showing, manually scrolling the content of the UITextView to the bottom has it properly ending just above the top of the keyboard. -[UITexView scrollRangeToVisible:], however, doesn't seem to take into account the presence of the keyboard any more.
Visually, here's what's happening. I built an inline search for my text view with controls to jump between the results (similar to searching in Safari). So in the text view shown here with search results as the user tapped the "next" button, the cyan selection would cycle down through the results. When the user went to the seventh result, the view would scroll until it was visible.
With the keyboard (from the UISearchBar) up on the same search results when the user went to the fifth search result, it would scroll to be just above the keyboard. But only in iOS 6. In iOS 7 no scrolling happens until going to the seventh search result like in the non-keyboard situation, and even then it scrolls the same amount so it's just visible below the bottom of the text view's frame.
Is this a known change in iOS 7? I'm using auto-layout so the next thing I'm going to try is to adjust the text view's bottom spacing constraint to shrink the entire view to avoid the problem, but want to check if there's way to still use my existing code under iOS 7.
To do that, open the Settings app on your iPhone. Inside Settings, select Display & Brightness. On the next screen, tap on Display Zoom under “Display”. Here, select Zoomed to switch to a larger view and then tap on Set at the top right corner.
iPhone keyboards typically stop working for one of three reasons: The app you're trying to use the iPhone keyboard in has crashed. Your iPhone is experiencing a more advanced software problem. Your iPhone's display isn't working properly or has become unresponsive.
Go to Settings > General > Keyboard > Keyboards. Tap a language at the top of the screen, then select an alternative layout from the list.
Even though this has been already answered, I had the same problem while building my own UITextView
subclass with search highlighting (it's available on my GitHub, if you're interested) and came up with a custom implementation of the scrollRangeToVisible:
method. All you need to do is to adjust the contentInset
and scrollIndicatorInset
properties of your UITextView
as you're already doing (related answer for casual Googlers reading this), then call:
[textView scrollRangeToVisible:range consideringInsets:YES];
I wrapped up the relevant code in a category, which also has a couple other useful methods to account for insets in iOS 7:
Note: you need them all due to how I organized this code in my subclass. Feel free to reorganize it to your liking.
@interface UITextView (insets)
// Scrolls to visible range, eventually considering insets
- (void)scrollRangeToVisible:(NSRange)range consideringInsets:(BOOL)considerInsets;
// Scrolls to visible rect, eventually considering insets
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated consideringInsets:(BOOL)considerInsets;
// Returns visible rect, eventually considering insets
- (CGRect)visibleRectConsideringInsets:(BOOL)considerInsets;
@end
@implementation UITextView (insets)
// Scrolls to visible range, eventually considering insets
- (void)scrollRangeToVisible:(NSRange)range consideringInsets:(BOOL)considerInsets
{
if (considerInsets && (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1))
{
// Calculates rect for range
UITextPosition *startPosition = [self positionFromPosition:self.beginningOfDocument offset:range.location];
UITextPosition *endPosition = [self positionFromPosition:startPosition offset:range.length];
UITextRange *textRange = [self textRangeFromPosition:startPosition toPosition:endPosition];
CGRect rect = [self firstRectForRange:textRange];
// Scrolls to visible rect
[self scrollRectToVisible:rect animated:YES consideringInsets:YES];
}
else
[self scrollRangeToVisible:range];
}
// Scrolls to visible rect, eventually considering insets
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated consideringInsets:(BOOL)considerInsets
{
if (considerInsets && (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1))
{
// Gets bounds and calculates visible rect
CGRect bounds = self.bounds;
UIEdgeInsets contentInset = self.contentInset;
CGRect visibleRect = [self visibleRectConsideringInsets:YES];
// Do not scroll if rect is on screen
if (!CGRectContainsRect(visibleRect, rect))
{
CGPoint contentOffset = self.contentOffset;
// Calculates new contentOffset
if (rect.origin.y < visibleRect.origin.y)
// rect precedes bounds, scroll up
contentOffset.y = rect.origin.y - contentInset.top;
else
// rect follows bounds, scroll down
contentOffset.y = rect.origin.y + contentInset.bottom + rect.size.height - bounds.size.height;
[self setContentOffset:contentOffset animated:animated];
}
}
else
[self scrollRectToVisible:rect animated:animated];
}
// Returns visible rect, eventually considering insets
- (CGRect)visibleRectConsideringInsets:(BOOL)considerInsets
{
CGRect bounds = self.bounds;
if (considerInsets)
{
UIEdgeInsets contentInset = self.contentInset;
CGRect visibleRect = self.bounds;
visibleRect.origin.x += contentInset.left;
visibleRect.origin.y += contentInset.top;
visibleRect.size.width -= (contentInset.left + contentInset.right);
visibleRect.size.height -= (contentInset.top + contentInset.bottom);
return visibleRect;
}
return bounds;
}
@end
This seems to be a bug in iOS7. I am using following code as a work around (heavily inspired by the answers to following questions: How to re-size UITextView when keyboard shown with iOS 7).
CGRect caret_rect = [_editTextView caretRectForPosition:_editTextView.selectedTextRange.end];
UIEdgeInsets insets = _editTextView.contentInset;
CGRect visible_rect = _editTextView.bounds;
visible_rect.size.height -= (insets.top + insets.bottom);
visible_rect.origin.y = _editTextView.contentOffset.y;
if(!CGRectContainsRect(visible_rect, caret_rect)) {
CGFloat new_offset = MAX((caret_rect.origin.y + caret_rect.size.height) - visible_rect.size.height - _editTextView.contentInset.top, - _editTextView.contentInset.top);
[_editTextView setContentOffset:CGPointMake(0, new_offset) animated:NO];
}
Oddly, it is not possible to change animated to YES in the last call.
I will file a bug report with Apple.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With