0
votes

I am using xamarin custom webview to load my page in app. But facing issue that title of webpage hides behind navigation bar . Or sometimes bottom of page not shown. I have tried adding scrollbar to my layout but still facing issue. Same works perfectly on android. Is it due to custom webview? I just want my webview to start below navigation bar and load completely according to device size.

my custom webview code :

public class CustomWebView : WebView
    {
        public static readonly BindableProperty UriProperty = BindableProperty.Create(
        propertyName: "Uri",
        returnType: typeof(string),
        declaringType: typeof(CustomWebView),
        defaultValue: default(string));

        public string Uri
        {
            get { return (string)GetValue(UriProperty); }
            set { SetValue(UriProperty, value); }
        }
    }

Xaml Page :

<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand">
            <StackLayout>
                <Label x:Name="type" Text="Loading..." FontSize="Medium"/>
            </StackLayout>
            <StackLayout  VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
                <ScrollView Orientation="Vertical" FlowDirection="MatchParent" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand" Visual="Material" VerticalScrollBarVisibility="Always">
                    <OnPlatform x:TypeArguments="View">
                        <On Platform="Android">
                            <WebView  x:Name="dashboard_android"  HeightRequest="1000" WidthRequest="1000"  />
                        </On>
                        <On Platform="iOS">
                            <local:CustomWebView  x:Name="dashboard_ios" VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand"  WidthRequest="1000"  HeightRequest="1000"/>
                        </On>
                    </OnPlatform>
                </ScrollView>

            </StackLayout>
        </StackLayout>

code behind :

dashboard_android.Source = url;
  dashboard_ios.Uri = url;

Following are solutions i have tried but no success

Solution 1 : I have tried adding two properties, but no use

this.EdgesForExtendedLayout = UIRectEdge.None;

   this.ExtendedLayoutIncludesOpaqueBars = false;

Solution 2 :

Tried enabling this unsafe area property , still no success

ios:Page.UseSafeArea="true"

Solution 3 :

Tried setting webview height on content size dynamically , but no success

 public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
        {
            // base.DidFinishNavigation(webView, navigation);

            var wv = _webViewRenderer.Element as CustomWebView;
            if (wv != null)
            {
                await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
                wv.HeightRequest = (double)webView.Frame.Size.Height; // ScrollView.ContentSize.Height;
            }

        }

Updated Xaml Code :

 <StackLayout  VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
 <local:CustomWebView  x:Name="dashboard"  HeightRequest="1000" WidthRequest="1000" />
</StackLayout>

Updated Code behind :

public partial class DashboardView : ContentPage
    {
        string url;
        public DashboardView()
        {
            InitializeComponent();
            url= ""; //adding url to load here
           dashboard.Uri = url;

      }
    }

Custom WebView Renderer

    [assembly: ExportRenderer(typeof(CustomWebView), typeof(MyCustomWebViewRenderer))]
    namespace Report.iOS
    {


        public class MyCustomWebViewRenderer : ViewRenderer<CustomWebView, WKWebView> 
        {
            WKWebView webView;
            protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView> e)
            {
                base.OnElementChanged(e);

                if (Control == null)
                {
                    webView = new WKWebView(Frame, new WKWebViewConfiguration());

                    webView.NavigationDelegate = new WebViewDelegate();


                    SetNativeControl(webView);     

                }
                if (e.NewElement != null)
                {

                   Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.Uri)));

                }
            }

    }


    public  class WebViewDelegate : WKNavigationDelegate, INSUrlConnectionDataDelegate 
    {
        string uname = null;
        string pass = null;




        public override async void DidReceiveAuthenticationChallenge(WKWebView webView, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
        {
            try
            {
                uname = Xamarin.Forms.Application.Current.Properties.ContainsKey("Username") ? Convert.ToString(Xamarin.Forms.Application.Current.Properties["Username"]) : null;
                pass = await SecureStorage.GetAsync("Password");
            }
            catch (Exception ex)
            {

            }
            completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, new NSUrlCredential(uname, pass, NSUrlCredentialPersistence.ForSession));

            return;
        }
    }
}

Screenshot of webview screen :

Here i am loading this webpage(https://docs.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android). As you can see half of footer is hidden and i am not able to scroll it.

Screenshot of app

3
Move the scrollView maybe work. I use you code and I can't reproduce the issue you said in your question. Can you please add more codes or a sample project? - Jack Hua
I have removed scrollview and updated my latest code. can you please check ? - Sakshi Bole
Your code still works well on my side. Can you add a screenshot or is there any other codes? A sample project would be better. - Jack Hua
Hi, i have added image. As you can see half footer is hidden and not able to scroll. Please help - Sakshi Bole

3 Answers

0
votes

The reason for it quite simple actually you have added the WebView inside a scrollView which is, in turn, causing the issue webview has its own scroll so all you have to do is something like:

        <StackLayout  VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">               
           <local:CustomWebView  x:Name="dashboard" />
        </StackLayout>

Also, you do not need the on the platform you can directly use the below and the custom renderer you have created.

The Height/Width request & layout options are not needed Webview by default will capture the whole viewport, You could actually even remove the StackLayouts, But that's on you.

Also, you might wanna read more about the webview

Good luck

Feel free to get back if you have queries

0
votes

You can use latest WkWebViewRenderer:

public class MyCustomWebViewRenderer : WkWebViewRenderer
{
    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);

        //this.LoadUrl("https://docs.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android");

        this.NavigationDelegate = new WebViewDelegate();

    }
}

In your code behind, you can directly set the source or set your binding:

dashboard.Source = "https://docs.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android";

Also, start from xamarin.forms 4.5+, xamarin use WKWebview as the default control in iOS and that means you no longer need a custom renderer if you use xamarin.forms 4.5+. Refer:

UIWebView Deprecation and App Store Rejection (ITMS-90809)

0
votes

I was facing that issue just beacuse i was using custom renderer. My solution code is as follows :

Xaml Code :

 <ContentPage.Content>
        <StackLayout  VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
            <WebView  x:Name="dashboard" HeightRequest="1000" WidthRequest="1000"/>
        </StackLayout>
    </ContentPage.Content>

Code Behind :

 public partial class DashboardView : ContentPage
        {
            public DashboardView()
            {
                InitializeComponent();
                dashboard.Source = "url"; 
            }

        }

Authentication Renderer iOS :

[assembly: ExportRenderer(typeof(WebView), typeof(Report.iOS.WebViewRenderer))]
namespace Report.iOS
{
    class WebViewRenderer : WkWebViewRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);
            this.NavigationDelegate = new WebViewDelegate();

        }
    }

    public class WebViewDelegate : WKNavigationDelegate, INSUrlConnectionDataDelegate
    {
        string uname = null;
        string pass = null;

        public override async void DidReceiveAuthenticationChallenge(WKWebView webView, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
        {
            try
            {
                uname = Xamarin.Forms.Application.Current.Properties.ContainsKey("Username") ? Convert.ToString(Xamarin.Forms.Application.Current.Properties["Username"]) : null;
                pass = await SecureStorage.GetAsync("Password");
            }
            catch (Exception ex)
            {

            }
            completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, new NSUrlCredential(uname, pass, NSUrlCredentialPersistence.ForSession));

            return;
        }
    }



}

Authentication Renderer Android :

[assembly: ExportRenderer(typeof(WebView), typeof(AuthWebViewRenderer))]

namespace Report.Droid
{

    public class AuthWebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
    {
        AuthWebViewClient _authWebClient = null;


        public  AuthWebViewRenderer(Context context) : base(context)
        {

        }


        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);
            if (_authWebClient == null)
            {
                _authWebClient = new AuthWebViewClient();

            }
            Control.SetWebViewClient(_authWebClient);
        }
    }
    public class AuthWebViewClient : WebViewClient
    {

        public AuthWebViewClient()
        {

        }


        public override async  void OnReceivedHttpAuthRequest(global::Android.Webkit.WebView view, HttpAuthHandler handler, string host, string realm)
        {
            string uname = null;
            string pass = null;
            try
            {
                uname = Application.Current.Properties.ContainsKey("Username") ? Convert.ToString(Application.Current.Properties["Username"]) : null;
                pass = await SecureStorage.GetAsync("Password");
            }
            catch (Exception ex)
            {
                Log.Error("Apprise :", "Error Occurred while getting login credentials " + ex);
            }
            handler.Proceed(uname, pass);
        }


    }
}