Is there something include in Xamarin for Scrolling the View with Keyboard when it Shows and Hides ?
Or should I have to do it on my own, like Obj-C.
Thx
Based on FieldService Sample I implemented this mechanism in my parent view controller:
#region Keyboard adjust /// <summary> /// Set this field to any view inside the scroll view to center this view instead of the current responder /// </summary> protected UIView ViewToCenterOnKeyboardShown; public virtual bool HandlesKeyboardNotifications() { return false; } protected virtual void RegisterForKeyboardNotifications() { NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, OnKeyboardNotification); NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, OnKeyboardNotification); } /// <summary> /// Gets the UIView that represents the "active" user input control (e.g. textfield, or button under a text field) /// </summary> /// <returns> /// A <see cref="UIView"/> /// </returns> protected virtual UIView KeyboardGetActiveView() { return View.FindFirstResponder(); } private void OnKeyboardNotification (NSNotification notification) { if (!IsViewLoaded) return; //Check if the keyboard is becoming visible var visible = notification.Name == UIKeyboard.WillShowNotification; //Start an animation, using values from the keyboard UIView.BeginAnimations ("AnimateForKeyboard"); UIView.SetAnimationBeginsFromCurrentState (true); UIView.SetAnimationDuration (UIKeyboard.AnimationDurationFromNotification (notification)); UIView.SetAnimationCurve ((UIViewAnimationCurve)UIKeyboard.AnimationCurveFromNotification (notification)); //Pass the notification, calculating keyboard height, etc. bool landscape = InterfaceOrientation == UIInterfaceOrientation.LandscapeLeft || InterfaceOrientation == UIInterfaceOrientation.LandscapeRight; var keyboardFrame = visible ? UIKeyboard.FrameEndFromNotification(notification) : UIKeyboard.FrameBeginFromNotification(notification); OnKeyboardChanged (visible, landscape ? keyboardFrame.Width : keyboardFrame.Height); //Commit the animation UIView.CommitAnimations (); } /// <summary> /// Override this method to apply custom logic when the keyboard is shown/hidden /// </summary> /// <param name='visible'> /// If the keyboard is visible /// </param> /// <param name='keyboardHeight'> /// Calculated height of the keyboard (width not generally needed here) /// </param> protected virtual void OnKeyboardChanged (bool visible, float keyboardHeight) { var activeView = ViewToCenterOnKeyboardShown ?? KeyboardGetActiveView(); if (activeView == null) return; var scrollView = activeView.FindSuperviewOfType(View, typeof(UIScrollView)) as UIScrollView; if (scrollView == null) return; if (!visible) RestoreScrollPosition(scrollView); else CenterViewInScroll(activeView, scrollView, keyboardHeight); } protected virtual void CenterViewInScroll(UIView viewToCenter, UIScrollView scrollView, float keyboardHeight) { var contentInsets = new UIEdgeInsets(0.0f, 0.0f, keyboardHeight, 0.0f); scrollView.ContentInset = contentInsets; scrollView.ScrollIndicatorInsets = contentInsets; // Position of the active field relative isnside the scroll view RectangleF relativeFrame = viewToCenter.Superview.ConvertRectToView(viewToCenter.Frame, scrollView); bool landscape = InterfaceOrientation == UIInterfaceOrientation.LandscapeLeft || InterfaceOrientation == UIInterfaceOrientation.LandscapeRight; var spaceAboveKeyboard = (landscape ? scrollView.Frame.Width : scrollView.Frame.Height) - keyboardHeight; // Move the active field to the center of the available space var offset = relativeFrame.Y - (spaceAboveKeyboard - viewToCenter.Frame.Height) / 2; scrollView.ContentOffset = new PointF(0, offset); } protected virtual void RestoreScrollPosition(UIScrollView scrollView) { scrollView.ContentInset = UIEdgeInsets.Zero; scrollView.ScrollIndicatorInsets = UIEdgeInsets.Zero; } /// <summary> /// Call it to force dismiss keyboard when background is tapped /// </summary> protected void DismissKeyboardOnBackgroundTap() { // Add gesture recognizer to hide keyboard var tap = new UITapGestureRecognizer { CancelsTouchesInView = false }; tap.AddTarget(() => View.EndEditing(true)); View.AddGestureRecognizer(tap); } #endregion
Subclasses override HandlesKeyboardNotifications
if they want to handle keyboard notifications. It requires a that a UIScrollView in the view hierarchy, otherwise it won't work.
public override bool HandlesKeyboardNotifications() { return true; }
To dismiss the keyboard automatically when the user taps anywhere else in the screen, call DismissKeyboardOnBackgroundTap
on ViewDidLoad
.
It also requires this UIView extension class:
public static class ViewExtensions { /// <summary> /// Find the first responder in the <paramref name="view"/>'s subview hierarchy /// </summary> /// <param name="view"> /// A <see cref="UIView"/> /// </param> /// <returns> /// A <see cref="UIView"/> that is the first responder or null if there is no first responder /// </returns> public static UIView FindFirstResponder(this UIView view) { if (view.IsFirstResponder) { return view; } foreach (UIView subView in view.Subviews) { var firstResponder = subView.FindFirstResponder(); if (firstResponder != null) return firstResponder; } return null; } /// <summary> /// Find the first Superview of the specified type (or descendant of) /// </summary> /// <param name="view"> /// A <see cref="UIView"/> /// </param> /// <param name="stopAt"> /// A <see cref="UIView"/> that indicates where to stop looking up the superview hierarchy /// </param> /// <param name="type"> /// A <see cref="Type"/> to look for, this should be a UIView or descendant type /// </param> /// <returns> /// A <see cref="UIView"/> if it is found, otherwise null /// </returns> public static UIView FindSuperviewOfType(this UIView view, UIView stopAt, Type type) { if (view.Superview != null) { if (type.IsAssignableFrom(view.Superview.GetType())) { return view.Superview; } if (view.Superview != stopAt) return view.Superview.FindSuperviewOfType(stopAt, type); } return null; } }
Hope it helps.
Answers
@MarcCayer,
AFAIK, you have to usually handle this yourself. I know that Monotouch.Dialog does seem to handle this on its own though. There is a nice example pattern of a BaseController that gives keyboard notifications, which you can override in the derived classes to move your views in the Field Service example.
Any open source ?
Trying to save time hehe
@MarcCayer,
Take a look at this:
https://github.com/xamarin/prebuilt-apps/blob/master/FieldService/FieldService.iOS/Controllers/BaseController.cs
Implemented here:
https://github.com/xamarin/prebuilt-apps/blob/master/FieldService/FieldService.iOS/Controllers/LoginController.cs
at the very bottom, OnKeyboardChanged.
I'm looking for this implementation as well.
Can't find it and can't make the above sample work.
Anyone?
Edit: this is how I do it now, but I wonder if there isn't a better way?
Unfortunately, No!
But the hardwork is already done using Dialog.
Otherwise, you have to do it on your own.
Set Notifications :
... For my own, I am showing a UIScrollView, so on the Keyboard Notifications Listener, I increase / decrease the heigth of my scrollview, then scrolling with animation on the editing text label.
Based on FieldService Sample I implemented this mechanism in my parent view controller:
Subclasses override
HandlesKeyboardNotifications
if they want to handle keyboard notifications. It requires a that a UIScrollView in the view hierarchy, otherwise it won't work.To dismiss the keyboard automatically when the user taps anywhere else in the screen, call
DismissKeyboardOnBackgroundTap
onViewDidLoad
.It also requires this UIView extension class:
Hope it helps.
Hi Guillermo,
thanks for the great and complete example, you don't mention but I had to call from the RegisterForKeyboardNotifications to make this work from the subclass; do you call this method in any other way?
Besides this everything else worked great, just had to tweak the offset value a bit and everything works great!!
Thanks again for sharing your code,
Luís Pinho
Hey @Ipinho you are right, this code was missing:
Subclasses override
HandlesKeyboardNotifications
to activate it:Your post was very helpful Guillermo; especially once I found the BaseController class you were referring to: https://github.com/xamarin/prebuilt-apps/blob/master/FieldService/FieldService.iOS/Controllers/BaseController.cs
One small change I did make was to ensure the calculated offset wouldn't cause the scroll view to reposition itself outside of the bounds of the view itself.
I added this code after the "float offset = ..." line:
Thanks @MilkyJoe, I did a small change some time ago to calculate the size of the overlapped frame of the scroll view bellow the keyboard, this change should avoid the issue that you are mentioning. I also moved all the
UIScrollView
related methods to a extension class. One thing left in the inker is to avoid moving the view if it's already fully visible. If anyone is interested I can post it to Gist.I'm definitely interested in taking a look.
And thanks again for your invaluable help!
@MilkyJoe here's the gist to the classes that I'm using. If you feel that something can be improved I'm open for suggestions.
Cheers!
Really like this, works a charm @GuillermoGutierrez. Love when something just works
Thanks @InitLipton , you just reminded me a small change in
ScrollExtensions.cs
that I didn't upload yet. Now the textfield won't be moved down. It was a little bit annoying that the textfield was completely visible and the scroll moved anyway. So feel free to update that class from the GIST.Cheers!
Hi @GuillermoGutierrez Just having a small issue with the scrolling, I've played around with a few values but have made a complete mess of things. After I use my TextEdits and the keyboard pops up with the keyboard. After i lose part of the screen to the top.
http://tinypic.com/r/11ierh2/8
Sorry Figured it out there
I just added the following lines to the BaseView
Then in the ScrollView extension class
Sorry for the silly question
Thank you for the help @GuillermoGutierrez however I am having an issue where the view tries to center on the textfield but it resets to the way it was before I saw you mentioned the issue but could you help me find where to put that change.
Thanks
@InitLipton thanks for pointing that out, I was not taking into account the initial ContentInset of the scroll.
@KyleKowalski I'm not sure what's the issue that you are referring, can you provide a sample project? I will fix it if I can.
figured it out wasn't increasing the bounds of the scroll view. Thanks for the help great tutorial
Belated response but I have just written a free library called "LazyBind" which has has a built-in Keyboard avoiding feature. Just ensure you provide the UIViewController when you instantiate: new LazyBind(your_object, provide_controller_here)
Library at http://components.xamarin.com/view/gmgsoftware
Does anybody have the same problem with this solution on iOS 8 or is it just me? It seems like scrollView.ContentOffset is always 0,0 even after scrolling down manually
@Guillermo Thanks a lot for seamless Implementation. Your code worked really well.
on iOS 8 and device (iPad4) I had to change
OnKeyboardChanged (visible, landscape ? keyboardFrame.Width : keyboardFrame.Height);
to
OnKeyboardChanged (visible, keyboardFrame.Height);
This doesn't work for me when using a subview. I register the events but the methods never get called.
@PavelPenkava I had the same issue! It turns out that Width and Height are flipped in the Frame for UIKeyboard (KeyboardFrame) in iOS8.
@JeffreyStuij.1290: Exactly, but I think it was flipped just in emulator. I'm not sure about it right now.
@PavelPenkava,@GuillermoGutiérrez, @InitLipton,@KyleKowalski
Hi,
I have created app using Xamain.Forms 1.3.4.
I have textbox at the end of screen. When I tap on textbox the keyboard swipe up and content of screen gets up. While I am scrolling up and down the page content when keyboard is on I cannot able to see the top content and some sort of content I can see.
Can you please help what to do here that can show whole page with all content even if keyboard is swipe up?
Thanks
@ErikFAndersen On iOS 8 the UIWindow now rotates, so there's no need to use the width of the keyboard instead of the height when on landscape. I've updated the GIST with this change to also maintain iOS 7 compatibility.
The result is something like this in
ScrollExtensions
class:@GuillermoGutierrez
your classses only works in Mono project? unfield project shows many errors here....
do you know about compatibility with Unfield projects? i need only to make my view a child of yours ParentViewController.Keyboard exactly?
@Guilherme.W Unified API migration is pretty straight forward, just change the namespaces and fix the errors. I've updated the GIST with the migration prepared.
@GuillermoGutierrez
I tried and the view was not scrolled no shows errors but an alert:

Here is my C# file...
i have a UIViewController > UIView > UISCrollView > UIView(contentView)
@Guilherme.W Those are warnings, if it's not working the problem must be elsewhere. That code was prepared to be used as a parent view controller. Child classes could set those values to customize the behavior of the scroll. I think that the variable names and the comments are pretty self-explanatory.
Try to debug the
OnKeyboardChanged
method to see why it's not changing the scroll position.@GuillermoGutierrez
Now it works pretty good, the problem was that in your GIST HandlesKeyboardNotifications() was false, i changed to true and it worked! thanks
Hi,
I'm facing the same problem that @NADIYADAMANI reported. In iOS 8, the handlers I register for UIKeyboard.WillHideNotification and UIKeyboard.WillShowNotification are never called. I also tried UIKeyboard.WillChangeFrameNotification, same thing. Any ideas?
I'm not using the designer and/or auto layout. My views are all built manually through code.
@SpyrosBoutsis The code is pretty self-explanatory (see the comments):
Have you added a call to
InitKeyboardHandling
inViewDidLoad
orViewWillAppear
methods?Have you overridden
HandlesKeyboardNotifications
to returntrue
in the view controllers that require keyboard notifications?Remember that you can modify the code to fit your needs. I just shared what I'm using in my projects.
Thanks for your reply. I used your code in the past and it worked fine under iOS 7, so I thought my problem had to do with some underlying change in iOS 8.
Well, it turns out that it was just the iOS 8 simulator not showing the soft keyboard by default (I had to explicitly enable it). I spent two days trying to figure out why the keyboard does not show up and why I'm not getting any notifications...
Thanks anyway!
@SpyrosBoutsis You can toggle Software / Hardware keyboards in the new simulator by pressing ⌘+K.
hi
i used the above code for autoscrolling when keyboard hide and show.
My problem is after the key board appears the first half in the scrollview is interacting to the user , the below half in the scroll view is not interacting to the user. Nothing is happening in the below half when user touches
Note : i got this problem only when keyboard appears.
Please tell me a solution for this.....
@GuillermoGutierrez Works like a charm!! Thanks!!
Still can't find a good generic solution (if exists) that it will work with Auto Layout. All the solutions for Xamarin and iOS Obejctive-C don't apply today since you most probably will use Auto Layout.