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+,则不再需要自定义渲染器.参考:
我遇到这个问题只是因为我使用的是自定义渲染器。
我的解决方案代码如下:
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);
}
}
}
我正在使用 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+,则不再需要自定义渲染器.参考:
我遇到这个问题只是因为我使用的是自定义渲染器。 我的解决方案代码如下:
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);
}
}
}