I would like to create a screen that has a behavior similar to common chat apps e.g. Hangouts where there's a scrollable section of the page and an Entry
with a Button
underneath that updates the scrollable section.
I currently have this implemented with a Grid
that has a *
row (with the scroller) and an Auto
row (with the Entry
).
On iOS, when I tap the Entry
, the keyboard popping up hides the lower half of the screen.
On Android, when I tap the Entry
, the keyboard slides up, but the UI slides up as well, getting rid of the top half of the UI.
I get that, yeah, this has been a bug for a while, it's not likely to be fixed any time soon, and so on. Is there a different way to approach this than having a Grid
lay out the sections of the page that will work for this use case?
Posts
Anyone have any ideas? Please?
On Android you could use WindowSoftInputMode = Android.Views.SoftInput.AdjustPan, then keyboard will be more similar to iOS behavior. Then use Entry Focused event and change layout based on it's current focus status.
I had this problem too. Works perfect on Android, but not on iPhone.
Now I found a possibility to get ride of this. I create a custom renderer for this page. In this, I connect to the KeyboardShow and KeyboardHide events of iOS and change the size by myself. All except the OnKeyboardChange function is standard.
When setting element.KeyboardHeight, I move the views on the page around by myself. ContentLayout is my upper ScrollView, BottomLayout is the StackLayout containing the Entry. The IOS part is for the line above the Entry.
I have some problems with update layout (content of the ScrollView isn't relayouted). But up to now, it's enough for me.
I hope, it helps a little bit.
Thanks! I made something similar based on your work which seems to work well:
This basically takes advantage of the existing mechanisms that are supposed to deal with layout changes but which aren't because the page isn't resizing when the keyboard is shown.
Still looking for a good fix for Android but that works perfectly for iOS. Thanks so much!
I figured out what I was doing wrong on Android. I'd copied some code that had specified:
I hadn't really realized the implications of
SoftInput.AdjustPan
though. When I changed it toSoftInput.AdjustResize
, it worked perfectly.@GeoffArmstrong: That's awsome! Your solution works much better than my. It is a smoother movement and much shorter code. Perfect!
@JasonSmith, @rmarinho: This is the right solution for handling keyboard when Entry isn't in a ScrollView. Do you think it is possible to integrate this in Xamarin.Forms?
There is only one thing left: if the keyboard is on the screen, scroll in the ScrollView and than leave the keyboard. The ScrollView is resized, but the content isn't correct. But for this, I try to make a new thread (https://forums.xamarin.com/discussion/30485/problems-with-layout-of-scrollview#latest)
Yeah, I'm not 100% sure that my code works for all the combinations of portrait and landscape and such; I made it in portrait and tested it there.
Glad you like it!
@GeoffArmstrong Thanks, that's an amazingly handy PageRenderer.
The Android side is interesting, changing WindowSoftInputMode for the whole activity messes up some of my other pages but I may just live with it. I'm looking into whether it's possible to modify SoftInput on the activity at runtime but no luck yet.
Turns out it is possible to use
Window.SetSoftInputMode
in an Android PageRenderer (I'm usingOnAttachedToWindow
andOnDetachedFromWindow
) to switch betweenSoftInput.AdjustPan
andSoftInput.AdjustResize
.Functional enough for now, but I haven't found a clean way of either getting a reference to the right instance of Window (I'm doing terrible static-y things for now) or of know what the "default" state of SoftInput should be (currently I'm just resetting it to AdjustPan regardless of what the original setting was).
@GeoffArmstrong Great answer! thanks for sharing your code. (For the iOS side)!!
@ShanePope No problem! This bug doesn't seem to happen in Android (since you can do
SoftInput.AdjustResize
) and I don't write for Windows Phone yet so I can't comment on the behavior there.I've submitted the code for this fix as a comment on a Bugzilla bug (at https://bugzilla.xamarin.com/show_bug.cgi?id=21915 ) so you might want to lend a supporting comment there if you want a cleaned-up version integrated into the framework.
Yours actually ended up being buggy w/ Grids (which is probably a grid + listview bug) but using your code to make the last row a fixed pixel height every time keyboard showed fixed it.
So instead of re-layingout which SHOULD WORK but doesn't with grid + listview I had to add an extra row to Grid and set it to Keyboard height on show or 0 on keyboard hide. But yeah yours got me there. Thanks!
Yeah, I've had some trouble with auto-expanding StackLayouts and Grids. StackLayouts sometimes expand the Expand bit too far, causing the fixed bit at the bottom to go off-screen, so I'll use a Grid. It seems to be a bit hit and miss for me whether the typical fixed-bit-at-top, fixed-bit-at-bottom, expanding-bit-in-middle can be expressed as an expandable StackLayout or a Grid with a Star row in the middle. But one or the other seems to work, usually.
This is nice.
Including me it'll help many people for sure.
Thanks for sharing.
Thanks @GeoffArmstrong , a simple solution to a common problem.
Looks like
Element.Layout(newBounds);
in a PageRenderer is no longer working in iOS since 1.4.0. I haven't had a chance to look further into it to see if there is an alternative approach yet, or if it's worth flagging as a defect.My iOS app, which uses this technique, seems to work fine in 1.4.1-pre2 on my phone...
@GeoffArmstrong thanks man ! this really helped me out !
Thanks for checking @GeoffArmstrong . I had a closer look and it turns out that whenever the keyboard resizer was setting Element.Layout, the page would immediately be returned to the full screen size.
... Xamarin.Forms.Page.LayoutChildren (x=0, y=20, width=320, height=460) in Xamarin.Forms.Page.UpdateChildrenLayout () in Xamarin.Forms.Page.OnSizeAllocated (width=320, height=480) in Xamarin.Forms.VisualElement.SizeAllocated (width=320, height=480) in Xamarin.Forms.VisualElement.SetSize (width=320, height=480) in Xamarin.Forms.VisualElement.set_Bounds (value=X=0, Y=0, Width=320, Height=480) in Xamarin.Forms.VisualElement.Layout (bounds=X=0, Y=0, Width=320, Height=480) in Xamarin.Forms.Platform.iOS.PageRenderer.SetElementSize (size=Width=320, Height=480) in Xamarin.Forms.Platform.iOS.ModalWrapper.ViewDidLayoutSubviews () in UIKit.UIApplication.UIApplicationMain () in ...
A quick test confirms that it's only happening to Modal pages.
I've noticed that if a layout pass is triggered while the keyboard is up, my solution doesn't work as well since the X.F layout system doesn't "know" about the keyboard.
Here is my updated version that updates padding instead of calling
Layout()
:Originally I thought I could just use the end frame's height for the bottom padding, but SwiftKey seems to like keeping its frame's height the same and just repositioning the frame.
Also, notice that this version takes into account that Xamarin.Forms will automatically take care of you if you have a
ScrollView
at the root by updating theScrollView
insets on keyboard showing.Thank you so much for this! (especially the updated version) I can't believe Xamarin doesn't do this by default...
I made an update that allows you to set whether Xamarin.Forms cancels touches for you or not on its keyboard-dismissal page-level tap gesture recognizer.
Usually the default behavior (cancel touches) is fine, but in some cases (in particular for me, when I had a
UICollectionView
on the page and I was trying to use its item selected behavior) Xamarin.Forms was too aggressive in its canceling of touches. But later, when I had aUICollectionView
as well as a keyboard input field on a page, I needed the touches canceled in order to dismiss the keyboard properly.To summarize...
If you cancel touches: tapping the page dismisses the keyboard and taps whatever you tapped on
If you don't cancel touches: the
ItemSelected
event of yourUICollectionView
s worksIn the cases where I needed items in the
UICollectionView
to be tappable as well as working with the keyboard, I attached tap gesture recognizers to theUICollectionView
items.By the way, major kudos to @adamkemp for going above and beyond and figuring out why touches weren't working for the
UICollectionView
.KeyboardResizingAwareContentPage.cs:
IosKeyboardFixPageRenderer.cs:
Just dropping a thanks here. This was exactly what I was looking for and has helped me greatly!
Hi,
I've just come across this and want to say thanks as this works perfectly to re-size the screen and push the entry box above the keyboard popup. However, whenever I use the keyboard on the page in which this code relates to and then go back to using a standard entry that pops up the keyboard elsewhere in my app, I get an error.
The error is a "System.ObjectDisposeException: The object was used after being disposed.". This occurs on the line:
It is worth mentioning that this error doesn't occur on device running iOS 8 or later, only on devices running versions previous to iOS 8. Has anyone else encountered the same issue?
Many thanks.
I seem Jason asking a while back if we wanted this ability added to Xamarin Forms but I never heard anything after that.
This is really useful
@GeoffArmstrong I owe you a beer! Works great.
Hi!
I've the same issue that Geoff, using Tamarin.Forms with a grid layout and the keyboard always cover the fields.
I found a simple solution:
Code with keyboard cover the fields :
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage.Content>
My grid code here....
</ContentPage.Content>
Code with keyboard scroll the screen :
I just put the grid inside a ScrollView and works fine for me.
Maybe this help..
@CssioScherer I guess it depends on whether your app works better as something where the container resizes when the keyboard is up or whether it scrolls when the keyboard is up. I have a couple things that I need to maintain at the top level (activity indicator, network out indicator) which mean that even when I have a
ScrollView
on screen, the root of my page hierarchy is always anAbsoluteLayout
.How do you set the height of your grid? Do you have any page elements that need to be stuck to the bottom of the screen, like a chat input field?
@GeoffArmstrong How can i use your code? Just create 2 new files and add the code from your example. Or do I have to add the code to a new file in Xamarin Forms?
Sorry for noobie question..
Thnx!
Hi @ArneDeCraemer !
Yes, you'll probably need two code files. You have to have
KeyboardResizingAwareContentPage
in your PCL project andIosKeyboardFixPageRenderer
in your iOS project. Then you have to subclassKeyboardResizingAwareContentPage
instead ofContentPage
.For convenience, I have posted this code at Stack Overflow:
http://stackoverflow.com/questions/31172518/how-do-i-keep-the-keyboard-from-covering-my-ui-instead-of-resizing-it/31172519
If it has helped you, I would appreciate an upvote. Thanks!
The problem of using your code @GeoffArmstrong is that when the keyboard appears and you throw the screen to top is been created a big white space in the screen above the keyboard ;(
@GeoffArmstrong, I am using your solution and I am having the same problem as @Waltricke, it appears some white space in the screen above the keyboard
Has somebody solve this issue?
I guess I haven't dealt with handling rotation while the keyboard is up.
For further help on android I had to set the "SoftInputMode" inside my custom Entry renderer:
(Forms.Context as Activity).Window.SetSoftInputMode(SoftInput.AdjustNothing );
Hope it helps.
@GeoffArmstrong do you know about this issue:
System.ObjectDisposedException: The object was used after being disposed.
this appears when the user touches inside a Entry that have a custom renderer....
this exception throw in the: if (!IsViewLoaded) return;
then when verification is removed the error disappear but i dont know about the problem if we remove this...
thanks
I am also facing this:
@GeoffArmstrong See this video for the WhiteSpace. I enabled Slow Animations to show you the issue.
I think everyone is facing this problem at the moment.. I hope I or someone else can fix this in the future :-)
Same issue on me.
Hi guys, sorry, I haven't been doing much with Xamarin.Forms recently. Around 1.5 we decided to do a major version that resulted in us taking two months to make a cross-platform framework using our own cross-platform rendering tech.
The whitespace thing isn't something I saw as a huge deal. If you figured out a way to fix it, you'd end up with X.F doing a giant pile of layout calculations for each frame in the animation, which could be slow. Here's how I solve that problem in my current project:
That uses iOS animation grabbing the iOS curve and setting the final values inside
UIView.AnimateNotify
. What might work would be if you set the Xamarin.Forms bottom padding within aUIView.AnimateNotify
, resulting in the iOSFrame
changes being captured and animated, but I can't guarantee that as I don't have access to test my idea.