Xamarin iOS webview underlap behind navigation bar

Xamarin iOS webview underlap behind navigation bar

我正在使用 xamarin 自定义 webview 在应用程序中加载我的页面。但面临网页标题隐藏在导航栏后面的问题。或者有时页面底部未显示。我尝试将滚动条添加到我的布局但仍然面临问题。同样适用于 android。是因为自定义 webview 吗?我只希望我的 webview 从导航栏下方开始并根据设备大小完全加载。

我的自定义网页视图代码 :

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 页数:

<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>

后面的代码:

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

以下是我尝试过但没有成功的解决方案

解决方案 1: 我尝试添加两个属性,但没有用

this.EdgesForExtendedLayout = UIRectEdge.None;

   this.ExtendedLayoutIncludesOpaqueBars = false;

解决方案 2:

尝试启用这个不安全区域属性,仍然没有成功

ios:Page.UseSafeArea="true"

解决方案 3:

尝试根据内容大小动态设置 webview 高度,但没有成功

 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;
            }

        }

已更新Xaml代码:

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

更新后的代码:

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

      }
    }

自定义 WebView 渲染器

    [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;
        }
    }
}

webview屏幕截图:

我正在加载此网页(https://docs.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android)。 如您所见,一半页脚被隐藏,我无法滚动它。

Screenshot of app

原因很简单,实际上您已将 WebView 添加到 scrollView 中,这反过来又导致问题 webview 有自己的滚动条,因此您所要做的就是:

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

此外,您不需要在平台上直接使用下面和您创建的自定义渲染器。

不需要 Height/Width 请求和布局选项 默认情况下 Webview 将捕获整个视口,实际上您甚至可以删除 StackLayouts,但这由您决定。

另外,您可能想阅读更多关于 webview

祝你好运

如果您有任何疑问,请随时回来

您可以使用最新的 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();

    }
}

在你后面的代码中,你可以直接设置源或设置你的绑定:

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

此外,从 xamarin.forms 4.5+ 开始,xamarin 在 iOS 中使用 WKWebview 作为默认控件,这意味着如果您使用 xamarin.forms 4.5+,则不再需要自定义渲染器.参考:

UIWebView Deprecation and App Store Rejection (ITMS-90809)

我遇到这个问题只是因为我使用的是自定义渲染器。 我的解决方案代码如下:

Xaml代码:

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

代码隐藏:

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

        }

身份验证渲染器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;
        }
    }



}

身份验证呈现器 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);
        }


    }
}