The keyboard on a phone normally covers around half the screen when it appears. That means any control such as a UITextField in the lower half of the screen will be covered. To prevent this we need to dynamically move the view up and down if a control is being hidden.
There are a variety of methods to handle this including placing items in a UITableView. The method I will describe is a more of a manual method and is similar to the method recommended by apple here.
Here is my solution.
Variables for the controller that houses the textboxes
private UIView activeview; // Controller that activated the keyboard private float scroll_amount = 0.0f; // amount to scroll private float bottom = 0.0f; // bottom point private float offset = 10.0f; // extra offset private bool moveViewUp = false; // which direction are we moving
Keyboard observers for ViewDidLoad().
// Keyboard popup NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.DidShowNotification,KeyBoardUpNotification); // Keyboard Down NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillHideNotification,KeyBoardDownNotification);
First up is the KeyboardUpNotification method. Essentially you calculate if the control will be hidden by the keyboard and if so calculate how much the view needs to be moved to show the control, and then move it.
private void KeyBoardUpNotification(NSNotification notification) { // get the keyboard size RectangleF r = UIKeyboard.BoundsFromNotification (notification); // Find what opened the keyboard foreach (UIView view in this.View.Subviews) { if (view.IsFirstResponder) activeview = view; } // Bottom of the controller = initial position + height + offset bottom = (activeview.Frame.Y + activeview.Frame.Height + offset); // Calculate how far we need to scroll scroll_amount = (r.Height - (View.Frame.Size.Height - bottom)) ; // Perform the scrolling if (scroll_amount > 0) { moveViewUp = true; ScrollTheView (moveViewUp); } else { moveViewUp = false; } }
The keyboard down event is simple. If the View has been moved just reverse it.
private void KeyBoardDownNotification(NSNotification notification) { if(moveViewUp){ScrollTheView(false);} }
Scrolling the view will scroll the view in an animated manner.
private void ScrollTheView(bool move) { // scroll the view up or down UIView.BeginAnimations (string.Empty, System.IntPtr.Zero); UIView.SetAnimationDuration (0.3); RectangleF frame = View.Frame; if (move) { frame.Y -= scrollamount; } else { frame.Y += scrollamount; scrollamount = 0; } View.Frame = frame; UIView.CommitAnimations(); }
Here is what happens without this code on a normal UIVIew.
Going to edit the 444.4 field.
But the keyboard covers it up
Now with the code added the view is scrolled.
Note in this example i have moved a UIView. The same effect could be had by scrolling a UIScrollView. The benefit of the method I described is all new items that bring up the keyboard will automatically be scrolled into view.
BoundsFromNotification is deprecated and could result in an app being rejected. Can you update this code so that its up to current standards?
Why did you not leave what the depreciated method had been replaced by, would have been more helpful
Works great, thanks for posting this!
The below code replaces the deprecated BouindsFromNotification:
var val = new NSValue(notification.UserInfo.ValueForKey(UIKeyboard.FrameBeginUserInfoKey).Handle);
RectangleF r = val.RectangleFValue;
Thank you for the the replacement code!
Hi John, when trying to use your code I get this: “Foundation.NSValue.NSValue(System.IntPtr)’ is inaccessible due to its protection level (CS0122)”
“Foundation.NSValue.NSValue(System.IntPtr)’ via a qualifier of type `Foundation.NSValue’. The qualifier must be of type `Pasalibro.Touch.Views.LoginView’ or derived from it (CS1540)”
Any workaround?
Hey Diego, this is what I use in place of that and it works great:
var val = (NSValue)notification.UserInfo.ValueForKey(UIKeyboard.FrameEndUserInfoKey);
CGRect r = val.CGRectValue;
Thank you very much for this great solution!
This is a simple and straightforward solution, thanks very much for sharing it.
Typo: In the ScrollTheView() method, I think that you meant to put scroll_amount rather than scrollamount.
thanks for sharing, man. your solution saved me a lot of time.
This is working perfectly. Thanks you so much
Hi, great solution, do you have a sample code to download?
Hi , how can we make this solution common for whole project , instead of just one viewcontroller
hi, thanks for the solutions , but how can we made this solution common for all the view in the app, as i tried and i am facing problem in getting first responder of the current view , thanks
Thank you all so much, this has really helped me out.
Thank you, really helpful article
Thank you for this greate article, this is was really helpful to me.
Thank you so much for this great article, it works like a charm and definitely helped me to get started.
Though I have a few small improvements:
1. Maybe you could also mention to make sure you unsubscribe from the NSNotificationCenter (in the ViewDidDisappear method) to prevent memory leaks.
2. There is a small bug when using multiple textfields stacked on top of each other (e.g. a username and password field). When first tapping into the bottom field, next into a field to the top and then hiding the keyboard, the view is not put back into its original position (most likely because the ‘scroll_amount’ gets calculated wrongly when you tab into the second textfield.
Do you have a solution for when there is multiple textfield?
Hey Maurits, did you ever find a solution to this bug? I am trying to fix this issue as well.
It’s a great article, helped me a lot. Thanks everybody
Awesome Mate! good job!
Excelent! Thank’s a lot.
Hi!
First of all thx for the great article. I ran into the following problem with your implementation and hope that you could help me out.
Whenever i calculate
scroll_amount = (r.Height – (View.Frame.Size.Height – bottom)) ;
my scroll_amoutn is negativ… A snipped of the values for my variables
r.Height = 297
View.Frame.Size.Height = 568
bottom = 40
when we use this values with your formula the result is -231. So the code will never the if state in the next line.
Do you have any idea what I am doing wrong?
Does anyone have a follow up solution to Maurits van Beusekom’s bug message?