90hex
90hex

Reputation: 170

Xamarin Webview with form scrolls out of view when clicking textbox

Using a webview to load a simple html form, the form displays ok. When clicking an input field the entire form disappears showing arrows up and down and done button at bottom part of display. This behavior does not happen in a normal Chrome browser. Any idéas?

Best regards Andreas

Xaml:

            <WebView Grid.Row="0" IsEnabled="false" 
                    x:Name="webview"  
                Navigating="webViewNavigating"></WebView>

        </Grid>
    </ContentPage.Content>
        <ContentPage.ToolbarItems>
    <ToolbarItem Text="Byt Betalsätt" Activated="BtnBack_Clicked" Priority="0" Order="Primary" />
</ContentPage.ToolbarItems>

</ContentPage>

HTML:

  <!-- New section -->
  <script type="text/javascript">
    // Fill in your publishable key
    Stripe.setPublishableKey('pk_test_1GitP47uZiwo4PKrDDSo8P3X');

    var stripeResponseHandler = function(status, response) {
      var $form = $('#payment-form');

      if (response.error) {
        // Show the errors on the form
        $form.find('.payment-errors').text(response.error.message);
        $form.find('button').prop('disabled', false);
      } else {
        // token contains id, last4, and card type
        var token = response.id;
        // Insert the token into the form so it gets submitted to the server
        $form.append($('<input type="hidden" name="stripeToken" />').val(token));
        // and re-submit
        $form.get(0).submit();
      }
    };

    jQuery(function($) {
      $('#payment-form').submit(function(e) {
        var $form = $(this);

        // Disable the submit button to prevent repeated clicks
        $form.find('button').prop('disabled', true);

        Stripe.card.createToken($form, stripeResponseHandler);

        // Prevent the form from submitting with the default action
        return false;
      });
    });
  </script>
</head>

<body>
<form action="/your-charge-code" method="POST" id="payment-form">
  <span class="payment-errors"></span>

  <div class="form-row">
    <label>
      <span>Card Number</span>
      <input type="text" size="20" data-stripe="number">
    </label>
  </div>

  <div class="form-row">
    <label>
      <span>Expiration (MM/YY)</span>
      <input type="text" size="2" data-stripe="exp_month">
    </label>
    <span> / </span>
    <input type="text" size="2" data-stripe="exp_year">
  </div>

  <div class="form-row">
    <label>
      <span>CVC</span>
      <input type="text" size="4" data-stripe="cvc">
    </label>
  </div>

  <div class="form-row">
    <label>
      <span>Billing Postal Code</span>
      <input type="text" size="6" data-stripe="address_zip">
    </label>
  </div>

  <input type="submit" class="submit" value="Submit Payment">
</form>

</body>

Upvotes: 1

Views: 2633

Answers (1)

jgoldberger - MSFT
jgoldberger - MSFT

Reputation: 6098

Odd, so I made a web page using your HTML code, and then created a new Xamarin.Forms solution with a WebView pointing to the page I created from your HTML. When I clicked any of the input fields the soft keyboard showed as expected. However if I made sure the WebView was at the bottom of the screen, under where the keyboard shows, the keyboard does hide the WebView. To fix this, you would need a custom page renderer [1] to scroll the view up when the keyboard shows up and scroll the view back down when the keyboard goes away.

Here is some sample code to do this. I assume you would only want to do this on pages that have that WebView, so first in the Forms PCL project create an empty subclass of ContentPage called WebViewContentPage:

public class WebViewContentPage : ContentPage {}

Then you inherit from the WebViewContentPage for the actual page that has the WebView, in my test I called it TestWebInputPage:

public partial class TestWebInputPage : WebViewContentPage
{ 
    public TestWebInputPage()
    {
        InitializeComponent();
    }
}

My Xaml is (change UrlToWebPageHere to the actual url of your web page):

<?xml version="1.0" encoding="utf-8"?>
<local:WebViewContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:TestWebInput" x:Class="TestWebInput.TestWebInputPage">
    <StackLayout>
        <Label 
            Text="Take up space" 
            HeightRequest="400" />
        <WebView 
            x:Name="webView" 
            Navigating="Handle_Navigating" 
            Source="UrlToWebPageHere" 
            VerticalOptions="FillAndExpand" />
    </StackLayout>
</local:WebViewContentPage>

And finally the custom page renderer code. This goes in the iOS app project:

using System;
using Xamarin.Forms;
using TestWebInput.iOS;
using Xamarin.Forms.Platform.iOS;
using Foundation;
using UIKit;
using TestWebInput;

[assembly: ExportRenderer(typeof(WebViewContentPage), typeof(ContentPageRenderer))]
namespace TestWebInput.iOS
{
    public class ContentPageRenderer : PageRenderer
    {   
        private NSObject keyBoardWillShow;
        private NSObject keyBoardWillHide;
        private nfloat scrollAmout;
        private double animDuration;
        private UIViewAnimationCurve animCurve;
        private bool keyboardShowing;

        public override void ViewDidLoad()
        {

            base.ViewDidLoad();

            keyBoardWillShow = UIKeyboard.Notifications.ObserveWillShow(KeyboardWillShow);

            keyBoardWillHide = UIKeyboard.Notifications.ObserveWillHide(KeyboardWillHide);
        }

        void KeyboardWillShow(object sender, UIKeyboardEventArgs args)
        {
            if (!keyboardShowing)
            {
                keyboardShowing = true;
                animDuration = args.AnimationDuration;
                animCurve = args.AnimationCurve;

                var r = UIKeyboard.FrameBeginFromNotification(args.Notification);
                scrollAmout = r.Height;
                ScrollTheView(true);
            }
        }

        void KeyboardWillHide(object sender, UIKeyboardEventArgs args)
        {
            if (keyboardShowing)
            {
                keyboardShowing = false;
                animDuration = args.AnimationDuration;
                animCurve = args.AnimationCurve;

                var r = UIKeyboard.FrameBeginFromNotification(args.Notification);
                scrollAmout = r.Height;
                ScrollTheView(false);
            }
        }

        private void ScrollTheView(bool scale)
        {
            UIView.BeginAnimations(string.Empty, IntPtr.Zero);
            UIView.SetAnimationDuration(animDuration);
            UIView.SetAnimationCurve(animCurve);

            var frame = View.Frame;

            if (scale)
                frame.Y -= scrollAmout;
            else
                frame.Y += scrollAmout;
            View.Frame = frame;
            UIView.CommitAnimations();
        }
    }
}

Since this renderer actually scrolls the entire native page up and back down when the keyboard shows and hides, it should not matter how you layout the page in the Forms Xaml. All that matters is that your Forms page inherits from WebViewContentPage.

I hope this helps!

[1] https://developer.xamarin.com/guides/xamarin-forms/custom-renderer/contentpage/

Upvotes: 2

Related Questions