UIScrollView scrollRectToVisible:animated: not taking rect into account on iOS7 - ios

[self.scrollView scrollRectToVisible:rect animated:YES];
Does anyone have a clue of why this works perfectly fine on iOS6.1 and on iOS7.0.4 always scrolls to the UITextField that has become firstResponder no matter what kind of rect I send as an argument?
CGRect rect = CGRectMake(0, self.scrollView.frame.size.height - 1, 320, 1);
[self.scrollView scrollRectToVisible:rect animated:YES];
This code will scroll the UIScrollView to its bottom when the keyboard is showed due to a UITextField inside the UIScrollView has become first responder on iOS6.1 but on iOS7.0.4 it is scrolled so that the UITextFiled is visible instead.
As I figure this, the UIScrollView in the iOS7 SDK no matter what, autoscrolls to whatever has become the first responder inside of it when scrollRectToVisible:animated: is called.

I suspect that most of you developers are using scrollRectToVisible:Animated: in conjunction with system keyboard notifications as explained in the Apple Docs here. For me the sample code provided by Apple didn't work (well, only half of it did).
Putting the method call inside a dispatch block fixed the problem for me:
dispatch_async(dispatch_get_main_queue(), ^{
[self.scrollView scrollRectToVisible:rect animated:YES];
});
I don't fully understand why this works and I'm not sure if this is 100% safe but on the other hand it feels a lot safer than just delaying the call by 0.1 seconds as suggested in another answer by Rikkles.
I'm not an expert on threading issues (yet) but it seems like whatever hidden system method is overriding the scrolling behavior is already on the main queue when the UIKeyboardDidShowNotification is sent. So if we put our method call on the main queue as well it will be executed afterwards and therefor yield the desired effect. (But that's only a guess.)

On iOS 8 (and possibly 7), the OS autoscrolls to the UITextField at the tail end of the runloop operation, just before it goes back to listening to user input.
I haven't found any way to get in after the OS autoscroll and before the user input. Neither UIKeyboardWillShowNotification nor UIKeyboardDidShowNotification are hooks that will work.
However, what will always work is the good old trick of performing a selector after delay. Simply put the scrolling code in its own method, and call that method like this:
- (void)keyboardDidShow:(NSNotification*)aNotification {
// ... all code to choose the view you want ...
[self performSelector:#selector(moveToView:) withObject:visibleView afterDelay:0.1];
}
- (void)moveToView:(UIView *)aView {
[self.scView scrollRectToVisible:aView.frame animated:YES];
}
And that will run after the OS autoscrolls, and you're golden.

I met this problem before. Not an easy one, but boring for sure.
It was because I set contentSize to 0 (because you don't want it to scroll). And you should set at least 1.
[scrollView setContentSize: CGSizeMake(1, self.view.frame.size.height)];
I hope it's the solution ;)

I found a solution to this problem, but it is not a pretty one. In order to scroll the scrollview to the desired location, u must register for both the keyboardWillShow and keyboardDidShow notifications. Then code to set the scrollview's insets is placed in the keyboardWillShowNotification's observer's selector and the code to scroll the scrollview to the desired location is placed in the keyboardDidShowNotification's observer's selector. Here is what I have:
Inside viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
Notification Methods:
- (void) keyboardWillShow: (NSNotification*) aNotification;
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
float kbHeight = kbSize.height < kbSize.width ? kbSize.height : kbSize.width;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbHeight, 0.0);
_scrollView.contentInset = contentInsets;
_scrollView.scrollIndicatorInsets = contentInsets;
}
-(void)keyboardDidShow:(NSNotification*)notification
{
CGRect aRect = CGRectMake(0, 0, _scrollView.frame.size.width, _scrollView.frame.size.height - _scrollView.frame.origin.y - self.scrollView.contentInset.bottom);
CGRect scrollFrame = CGRectMake(self.loginView.frame.origin.x + self.loginButton.frame.origin.x, self.loginView.frame.origin.y + self.loginButton.frame.origin.y, self.loginButton.frame.size.width, self.loginButton.frame.size.height);
if (!CGRectContainsRect(aRect, scrollFrame)) {
[_scrollView scrollRectToVisible:scrollFrame animated:YES];
}
}

I was following the Apple docs but with no success. Then I tried calling setContentOffset(_:animated:) on my scrollView, instead of scrollRectToVisible(_:animated:), and that made it work.
The code below scrolls to myView if it is hidden under keyboard, supposing you call keyboardWillShow function when you receive a UIResponder.keyboardWillShowNotification.
Swift 5
#objc private func keyboardWillShow(_ notification: Notification) {
if let keyboardHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height {
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
let visibleViewFrame = myView.frame
var scrollViewFrame = scrollView.frame
scrollViewFrame.size.height -= keyboardHeight
if !scrollViewFrame.contains(visibleViewFrame) {
scrollView.setContentOffset(CGPoint(x: 0, y: visibleViewFrame.origin.y), animated: true)
}
}
}

Related

How to keep UIView's size when toggle in call status bar [duplicate]

I have an iOS app. It works great.
Except when the user has a hotspot on or is in a call but the call app is minimised
The extended height of the status bar pushes my ui down, making part of it disappear,
at the bottom.
I want this extended bar to overlay the top of the screen and not push the ui downwards.
How do I achieve that ?
The Simplest Solution is to make sure that your view's springs-and-struts or Autolayout properties allow for compression or expansion of the view , If you have some complex UI then you can implement UIApplicationWillChangeStatusBarFrameNotification observer.
You can handle the UIApplicationWillChangeStatusBarFrameNotification and UIApplicationDidChangeStatusBarOrientationNotification notifications which will tell you the new size of the status bar.
If you are intent on using a transform on your view to handle resizing, you can implement -viewWillLayoutSubviews in your view controllers (probably in a common base class) to set a transform on the root view of the view controller.
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(statusFrameChanged:)
name:UIApplicationWillChangeStatusBarFrameNotification
object:nil];
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationWillChangeStatusBarFrameNotification
object:nil];
}
- (void)statusFrameChanged:(NSNotification*)note
{
CGRect statusBarFrame = [note.userInfo[UIApplicationStatusBarFrameUserInfoKey] CGRectValue];
CGFloat statusHeight = statusBarFrame.size.height;
UIScreen *screen = [UIScreen mainScreen];
CGRect viewRect = screen.bounds;
viewRect.size.height -= statusHeight;
viewRect.origin.y = statusHeight;
self.view.frame = viewRect;
[self.view setNeedsLayout];
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
CGRect baseFrame = self.view.frame;
// 548.0 is the full height of the view. Update as necessary.
CGFloat scale = self.view.frame.size.height / 548.0;
[self.view setTransform:CGAffineTransformMakeScale(1.0, scale)];
self.view.frame = baseFrame;
}
I used use "Vertical space - Bottom layout Guide - Button". This way, a button I have on the bottom of the screen stays in the same place when there is an in call bar and if a different screen size is used (3.5inch or 4icnh).

Textfields under keyboard

In my app there are few forms. Sometimes keyboard hides fields so user can't see what he it typing. For that case I found way to move view or scrollview up so textfields stays above keyboard.
Problem is that on iPhone 5 I need to move view up for last 3 textfields but for iPhone 6 - only for the last textfield.
Of corse I can define all cases of fields and device screen height values.
But I want to find more elegant solution to detect is texfield is under the keyboard on current device and is it necessary to move view?
Use TPKeyboardAvoidingScrollView. Its easy to use
drop the TPKeyboardAvoidingScrollView.m and TPKeyboardAvoidingScrollView.h source files into your project, pop a UIScrollView into your view controller's xib, set the scroll view's class to TPKeyboardAvoidingScrollView, and put all your controls within that scroll view. You can also create it programmatically, without using a xib - just use the TPKeyboardAvoidingScrollView as your top-level view.
There is a great help guide by Apple here
You need to listen to keyboard notifications like
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (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 app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
[self.scrollView scrollRectToVisible:activeField.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
You can detect which UITextField is the "Active" one by using its delegate method - (void)textFieldDidBeginEditing:(UITextField *)textField.
Use textField.frame to calculate the offset you need to set for scrollView.contentOffset.
In the - textFieldDidBeginEditing: method you can reset the contentOffset = CGPointZero

How to make the incall overlay not push the ui downwards

I have an iOS app. It works great.
Except when the user has a hotspot on or is in a call but the call app is minimised
The extended height of the status bar pushes my ui down, making part of it disappear,
at the bottom.
I want this extended bar to overlay the top of the screen and not push the ui downwards.
How do I achieve that ?
The Simplest Solution is to make sure that your view's springs-and-struts or Autolayout properties allow for compression or expansion of the view , If you have some complex UI then you can implement UIApplicationWillChangeStatusBarFrameNotification observer.
You can handle the UIApplicationWillChangeStatusBarFrameNotification and UIApplicationDidChangeStatusBarOrientationNotification notifications which will tell you the new size of the status bar.
If you are intent on using a transform on your view to handle resizing, you can implement -viewWillLayoutSubviews in your view controllers (probably in a common base class) to set a transform on the root view of the view controller.
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(statusFrameChanged:)
name:UIApplicationWillChangeStatusBarFrameNotification
object:nil];
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationWillChangeStatusBarFrameNotification
object:nil];
}
- (void)statusFrameChanged:(NSNotification*)note
{
CGRect statusBarFrame = [note.userInfo[UIApplicationStatusBarFrameUserInfoKey] CGRectValue];
CGFloat statusHeight = statusBarFrame.size.height;
UIScreen *screen = [UIScreen mainScreen];
CGRect viewRect = screen.bounds;
viewRect.size.height -= statusHeight;
viewRect.origin.y = statusHeight;
self.view.frame = viewRect;
[self.view setNeedsLayout];
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
CGRect baseFrame = self.view.frame;
// 548.0 is the full height of the view. Update as necessary.
CGFloat scale = self.view.frame.size.height / 548.0;
[self.view setTransform:CGAffineTransformMakeScale(1.0, scale)];
self.view.frame = baseFrame;
}
I used use "Vertical space - Bottom layout Guide - Button". This way, a button I have on the bottom of the screen stays in the same place when there is an in call bar and if a different screen size is used (3.5inch or 4icnh).

How to set the text field in UITextView to go up and down when the keyboard comes?

I have set a UITextView in the app, and I want that whenever a user types and gets to the keyboard line so the text will bounce one line up so the user can keep typing...
How can I accomplish that? I added the textView in the Interface Builder.
It's fairly complicated.
I wrote up how to do this in a project I have on github called "RandomBlobs" (link).
That project includes working code that uses the technique described.
Here is the article from that project:
Shifting views to make room for the keyboard
When you use an input text field (UITextField or UITextView) in your application, tapping in the text field causes the iOS keyboard to animate up from the bottom of the screen. For a few situations like UITableViewControllers, the system shifts the contents up to make room for the keyboard, but in most cases it does not, and you have deal with this yourself. It's frankly a pain to do this well. You have to allow for fact that the keyboard hight is different for different countries and languages, screen and screen orientations, and can change with OS releases as well. Also Apple can change the timing of the keyboard animation.
Handling this properly involves several steps. The specific details depend on whether your app uses AutoLayout or the older "struts and springs" style resizing rules.
This application uses struts and springs, and shifts the view by altering the view's frame. If you use AutoLayout the details of how you shift the view are slightly different, but the basic idea is the same.
When you receive a UIKeyboardWillShowNotification, it includes a pointer to the NSNotification object. Notification objects include an optional userInfo property that can contain a dictionary with more info about the notification. In the case of keyboard notifications, the userInfo block contains a number of useful key/value pairs, including the frame of the keyboard,in screen coordinates, and the duration of the keyboard show/hide animation. Search on the string "Keyboard Notification User Info Keys" in the Xcode docs for more info on hte user dictionary that is passed to you for keyboard notifications.
Handling keyboard animations requires several steps:
Add observers for 2 different system notifications, UIKeyboardWillShowNotification and UIKeyboardWillHideNotification. If you're writing a single-view application (like the RandomBlobs application) you can add your notification observers in your viewDidLoad:animated method. If you are developing an app with multiple view controllers, though, you probably want to add your observers in your viewWillAppear:animated method. I like to use the new block-based addObserverForName:object:queue:usingBlock: method. You can also use the older `addObserver:selector:name:object:' method, which requires that you have a second method that gets called when the observer gets a notification. Both flavors of observer receive a pointer to the triggering notification object, which is important in handling the keyboard notification.
Add corresponding code to remove your UIKeyboardWillShowNotification and UIKeyboardWillHideNotification observers. For single-view applications, you can do this in your view controlller's dealloc method. For an app with multiple view controllers, you probably want to remove your observers in your viewWillDisappear:animated method.
In order to figure out how far to shift the text field, we need to know it's postion. In order to do that, we need the position of the text field. Sadly, the keyboard notifications don't give us any information about the field that is about to be edited. So, we have to somehow figure out which field is about to begin editing. To do that:
a. Tell the compiler your view controller conforms to the correct protocol (UITextViewDelegate protocol for a UITextView, or UITextFieldDelegate protocol for a UITextField.)
b. Add an instance variable to remember the about-to-be-edited view. (textFieldToEdit in the demo project.)
c. implement the "begin editing" method for your view type (textViewShouldBeginEditing: for a UITextView or textFieldShouldBeginEditing: for a UITextField). The code is simple:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField;
{
textFieldToEdit = textField;
return YES;
}
d. In IB, set your view controller as the delegate of your UITextView or UITextField.
In the UIKeyboardWillShowNotification code, fetch the keyboard frame and animation duration, as well as the animation curve. These values are provided in both the UIKeyboardWillShowNotification and the UIKeyboardWillHideNotification, but it's usually simpler to just record the information you need into instance variables, which you then use in the handler for the UIKeyboardWillHideNotification handler.
a. Extract the height of the keyboard (from the frame provided in the UIKeyboardFrameBeginUserInfoKey key/value pair) and use it to calculate the amount we need to shift the keybard. We need to figure ot the Y coorindate of the bottom of the input field, in screen coorindates, and then figure out the minimum we need to shift the view up to fully expose the view. (see the code in the demo app, below). In the demo app, we save this value to the instance variable keyboardShiftAmount.
b. Get the animation duration (from the UIKeyboardAnimationDurationUserInfoKey key/value pair) and save it to the float instance variable (called keyboardSlideDuration in the sample app)
c. Save the keyboard animation curve (from the UIKeyboardAnimationCurveUserInfoKey key/value pair) into an instance variable (called keyboardAnimationCurve in the demo project). The keyboard animation curve is a variable of type UIViewAnimationCurve, which ranges from 0 to 4, and is used by the older beginAnimations:context:… commitAnimations style of view animations. We want to use the newer block-based UIView animation method animateWithDuration:delay:options:animations:completion:, which takes animation curve information of enum type UIViewAnimationOptions. The animation curve info in the UIViewAnimationOptions is shifted up by 16 bits, so we have to convert the specified UIViewAnimationCurve to the corresponding UIViewAnimationOptions bit flags by shifting the values by 16 bits (as shown in the code)
Animate the view's frame by the (negative of the) specified keyboard shift amount, and using the duration and animation curve that we got in step 3, above. Some developers only shift the field that's being edited. I think this is confusing, since the field will float up and not longer be at the same position relative to the other fields in the form. Instead, I usually animate the view controller's entire content view up.
In the UIKeyboardWillHideNotification code, do the reverse of the previous step, and animate the view down again. Since we saved the keyboard shift amount, animation duration, and animation curve in the UIKeyboardWillShowNotification handler, this code is pretty simple.
Putting all this togther, let's look at the code from our demo app that adds observers for the UIKeyboardWillShowNotification and UIKeyboardWillHideNotification observers:
showKeyboardNotificaiton = [[NSNotificationCenter defaultCenter] addObserverForName: UIKeyboardWillShowNotification
object: nil
queue: nil
usingBlock: ^(NSNotification *note)
{
//Get the keyboard frame from the notificaiton's userinfo dictionary (in non-rotated screen coordinates)
CGRect keyboardFrame;
NSDictionary* userInfo = note.userInfo;
keyboardSlideDuration = [[userInfo objectForKey: UIKeyboardAnimationDurationUserInfoKey] floatValue];
keyboardFrame = [[userInfo objectForKey: UIKeyboardFrameBeginUserInfoKey] CGRectValue];
keyboardAnimationCurve = [[userInfo objectForKey: UIKeyboardAnimationCurveUserInfoKey] integerValue]<<16;
UIInterfaceOrientation theStatusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation];
CGFloat keyboardHeight;
//if we're in landscape, treat use the reported keyboard width as the height
if UIInterfaceOrientationIsLandscape(theStatusBarOrientation)
keyboardHeight = keyboardFrame.size.width;
else
keyboardHeight = keyboardFrame.size.height;
CGRect fieldFrame = textFieldToEdit.bounds;
fieldFrame = [self.view convertRect: fieldFrame fromView: textFieldToEdit];
CGRect contentFrame = self.view.frame;
CGFloat fieldBottom = fieldFrame.origin.y + fieldFrame.size.height;
keyboardShiftAmount= 0;
if (contentFrame.size.height - fieldBottom <keyboardHeight)
{
keyboardShiftAmount = keyboardHeight - (contentFrame.size.height - fieldBottom);
//----------------------------------------------------------------------------------------------
//This is the code to shift the view if we're using AutoLayout:
// keyboardConstraint.constant -= keyboardShiftAmount;
// keyboardBottomConstraint.constant += keyboardShiftAmount;
// [self.view layoutIfNeeded];
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//This is the code for handling the keyboard animations for strut-and-spring style view resizing
[UIView animateWithDuration: keyboardSlideDuration
delay: 0
options: keyboardAnimationCurve
animations:
^{
CGRect frame = self.view.frame;
frame.origin.y -= keyboardShiftAmount;
self.view.frame = frame;
}
completion: nil
];
//----------------------------------------------------------------------------------------------
}
}
];
hideKeyboardNotificaiton = [[NSNotificationCenter defaultCenter] addObserverForName: UIKeyboardWillHideNotification
object: nil
queue: nil
usingBlock: ^(NSNotification *note)
{
if (keyboardShiftAmount != 0)
{
//------------------------------------------------------------------------------------------
//This is the code for animating the view back down for strut-and-spring style view resizing
[UIView animateWithDuration: keyboardSlideDuration
delay: 0
options: keyboardAnimationCurve
animations:
^{
CGRect frame = self.view.frame;
frame.origin.y += keyboardShiftAmount;
self.view.frame = frame;
//------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//This is the code to shift the view back down if we're using AutoLayout
// keyboardConstraint.constant += keyboardShiftAmount;
// keyboardBottomConstraint.constant -= keyboardShiftAmount;
// [self.view setNeedsUpdateConstraints];
// [viewToShift layoutIfNeeded];
//----------------------------------------------------------------------------------------------
}
completion: nil
];
}
}
];
Note that if you're using AutoLayout, there are several more steps and the code is a little different. You need to add a top constraint on your view, with a constant offset from the top layout guide, and a bottom constraint to the view that's tied to the bottom layout guide. Then you need to link thise to IBOutlets in your view controller so you can change their offset amounts in code. In the code above, we've used constraints who's IBOutlets are called keyboardConstraint and keyboardBottomConstraint
Read the Apple documentation on handling the keyboard here.
Listen to UIKeyboardWillShowNotification and UIKeyboardWillHideNotification notifications and adjust the size of your UITextView accordingly.
- (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 *)notification {
CGSize keyboardSize = [[[notification.userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] size];
self.textView.frame = CGRectMake(tv.frame.origin.x, tv.frame.origin.y, tv.frame.size.width, self.view.frame.size.height - keyboardSize.height);
}
- (void)keyboardWillHide:(NSNotification *)notification {
self.textView.frame = self.view.bounds;
}
Add an observer:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillChange:)
name:UIKeyboardWillChangeFrameNotification
object:nil];
then:
- (void)keyboardWillChange:(NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
CGRect endFrame = [[userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
endFrame = [self.view convertRect:endFrame fromView:nil];
// yourView.rect = // endFrame...
}

UITextView keyboardDismissMode Bug

I've just found an annoying bug with the new keyboardDismissMode property of the scroll view. When using this with a text view with the value UIScrollViewKeyboardDismissModeInteractive and the keyboard is dismissed the scroll view seems to jump up to the top before it continues to decelerate.
I've filed a bug report with Apple but need a workaround. I've tried the DAKeyboardControl without the new iOS7 support which behind the scenes is using the keyboardDismissMode and it still does it which to me indicates this is a much deeper problem.
Any suggestions?
for this issue better you code with scrollviewDelegete and simply mention when you want dismiss keyboard through ResignFirstResponder
Does seem to be a bug or just a non-ideal default state. But based on the code in the test project something like the below may work after some finer tuning.
There are two problems with the sample code, one is that you aren't doing anything about the size of the text when the keyboard does appear, so you can't use or see the text under the keyboard. There are other solutions but a quick and dirty solution is to change the frame size (in a submission app I would also grab the animation info and animate the view frame change to match the keyboard animation which is beyond the scope of this question). You do that in 'willShow' or the like, and bring it back in 'didHide' or the like.
Then, the content offset is fudged when its hidden and there does appear to be some strange states while you are dragging it offscreen before and around your callbacks for hiding and scroll view changes. I just save the state and "fix" it once the keyboard goes away and I've updated the text view.
I created a few properties and an outlet in the storyboard to fudge with the text view.
- (void) viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
}
- (void) keyboardWillShow:(NSNotification *)notification
{
NSDictionary * info = [notification userInfo];
CGSize size = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect rect = self.textView.frame;
rect.size.height -= size.height;
self.textView.frame = rect;
}
- (void)keyboardDidHide:(NSNotification *)notification
{
NSLog(#"====== keyboardDidHide =======");
NSDictionary * info = [notification userInfo];
CGSize size = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect rect = self.textView.frame;
rect.size.height += size.height;
self.textView.frame = rect;
self.hidingKeyboard = YES;
}
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(#"%f", scrollView.contentOffset.y);
if(self.hidingKeyboard == YES)
{
scrollView.contentOffset = self.lastOffset;
self.hidingKeyboard = NO;
NSLog(#"====== reset =======");
}
else
self.lastOffset = scrollView.contentOffset;
}

Resources