如何使用 CustomWebViewRenderer 使 WKWebView 的高度适合其在 Xamarin 中的内容?
How do I make WKWebView's height fit its contents in Xamarin with a CustomWebViewRenderer?
我为 iOS 制作了自定义 webview 渲染器,我在其中手动将自定义 WebView 替换为 WKWebView。但是,我的主要目的是让 WKWebView 根据它的内容调整它的高度。它的内容由预定义的 HTML 字符串和正文组成。
现在它切断了 Webview,其余内容都塞进了 scrollview。我放在 webview 之前的图像和文本也保持固定,同时我希望它们也向下滚动。我不想要 webview 的滚动条,我只希望 webview 的大小与其内容一致,以便用户可以向上滑动以查看整个文本。
CustomWebViewRenderer.cs
[assembly: ExportRenderer(typeof(PostWebView), typeof(Yoors.iOS.CustomWebViewRenderer))]
namespace Yoors.iOS
{
public class CustomWebViewRenderer : ViewRenderer<PostWebView, WKWebView>
{
WKWebView _wkWebView;
protected override void OnElementChanged(ElementChangedEventArgs<PostWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
WKWebViewConfiguration config = new WKWebViewConfiguration();
_wkWebView = new WKWebView(Frame, config);
SetNativeControl(_wkWebView);
}
if (e.NewElement != null)
{
HtmlWebViewSource source = (Xamarin.Forms.HtmlWebViewSource)Element.Source;
string headerString = "<header><meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0'></header>";
string html= headerString + source.Html;
Console.WriteLine("Height" + Element);
_wkWebView.LoadHtmlString(html, baseUrl: null);
_wkWebView.ScrollView.ScrollEnabled = false;
_wkWebView.SizeToFit();
}
}
}
PostWebView.xaml
<?xml version="1.0" encoding="UTF-8"?>
<WebView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Yoors.Views.Templates.PostWebView" x:Name="WebViewer" BackgroundColor="White" Margin="0, 10, 0, 0" VerticalOptions="FillAndExpand" HeightRequest="1000">
<WebView.Source>
<HtmlWebViewSource Html="{Binding Data.Content}" />
</WebView.Source>
</WebView>
PostView.xaml
<ContentPage.Content>
<ScrollView>
<StackLayout BackgroundColor="White" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<!-- Header of the post-->
<Image HeightRequest="125" Aspect="AspectFill" Source="{Binding Post.ImageUrl}" />
<StackLayout BackgroundColor="White" Padding="1" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Margin="10, 0,10, 0">
<Label Text="{Binding Post.Title}" FontFamily="{StaticResource BoldFont}" FontSize="20" />
<Label Text="{Binding Post.CreatedOn.DisplayText}" FontSize="12" />
<!-- Content of the post in a HTML view-->
</StackLayout>
<templates:PostWebView VerticalOptions="FillAndExpand" BindingContext="{Binding Post}">
</templates:PostWebView>
<templates:CommentView BindingContext="{Binding CommentsViewModel}">
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
</templates:CommentView>
</StackLayout>
</ScrollView>
</ContentPage.Content>
我认为为 PostWebView 放置 VerticalOptions= "FillAndExpand" 会使其适合其内容的大小,而无需创建 scollbar,因此不会固定其他内容,但它只是这样做: 有人知道怎么帮忙吗?
我通过创建一个在 Webview 完成加载时注册的 CustomNavigationDelegate 回答了我自己的问题。
CustomNavigationDelegate.cs
public class CustomWKNavigationDelegate : WKNavigationDelegate
{
CustomWebViewRenderer _webViewRenderer;
public CustomWKNavigationDelegate(CustomWebViewRenderer webViewRenderer)
{
_webViewRenderer = webViewRenderer;
}
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
var wv = _webViewRenderer.Element as PostWebView;
if (wv != null)
{
await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
wv.HeightRequest = (double)webView.ScrollView.ContentSize.Height;
}
}
}
然后在上面显示的 CustomWebViewRenderer 的 OnElementChanged 中,我将 Navigation Delegate 分配给 WKWebView。
CustomWebViewRenderer.cs
...
protected override void OnElementChanged(ElementChangedEventArgs<PostWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
WKWebViewConfiguration config = new WKWebViewConfiguration();
_wkWebView = new WKWebView(Frame, config);
_wkWebView.NavigationDelegate = new CustomNavigationDelegate(this);
...
这将使 WKWebView 具有其内容的大小!!
上面的答案是正确的,但前提是您最初没有在 xaml / 后面的代码中为 CustomWebView 设置更高的值。
我必须在后面的代码中设置一些初始大小,否则 webview 根本不会显示:
var myDisplay = DeviceDisplay.MainDisplayInfo;
mywebView.HeightRequest = myDisplay.Height;
mywebView.WidthRequest = myDisplay.Width;
但是使用此设置,CustomNavigationDelegate 将始终报告与初始化中设置的设备显示高度相同的大小 (2208px)。这似乎覆盖了以后的重新计算。
我通过设置较低的初始高度来修复它,然后动态调整到内容所需的更大高度:
var myDisplay = DeviceDisplay.MainDisplayInfo;
mywebView.HeightRequest = 800;
mywebView.WidthRequest = myDisplay.Width;
以前我在 webview 区域有白屏,动态高度也不起作用。但现在这很好用!也张贴在这里 - https://github.com/xamarin/Xamarin.Forms/issues/1711
[assembly: ExportRenderer(typeof(ExtendedWebView), typeof(ExtendedWebViewRenderer))]
namespace Project.iOS.Renderers
{
class ExtendedWebViewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
try
{
base.OnElementChanged(e);
NavigationDelegate = new ExtendedWKWebViewDelegate(this);
// For fixing white flash issue in webview on dark themes/backgrounds and disable webview's scrolling
if (NativeView != null)
{
var webView = (WKWebView)NativeView;
webView.Opaque = false;
webView.BackgroundColor = UIColor.Clear;
webView.ScrollView.ScrollEnabled = false;
}
}
catch (Exception ex)
{
Console.WriteLine("Error at ExtendedWebViewRenderer OnElementChanged: " + ex.Message);
}
}
}
class ExtendedWKWebViewDelegate : WKNavigationDelegate
{
ExtendedWebViewRenderer webViewRenderer;
public ExtendedWKWebViewDelegate(ExtendedWebViewRenderer _webViewRenderer)
{
webViewRenderer = _webViewRenderer ?? new ExtendedWebViewRenderer();
}
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
var wv = webViewRenderer.Element as ExtendedWebView;
if (wv != null && webView != null)
{
await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
if (webView.ScrollView != null && webView.ScrollView.ContentSize != null)
{
wv.HeightRequest = (double)webView.ScrollView.ContentSize.Height;
}
}
}
我为 iOS 制作了自定义 webview 渲染器,我在其中手动将自定义 WebView 替换为 WKWebView。但是,我的主要目的是让 WKWebView 根据它的内容调整它的高度。它的内容由预定义的 HTML 字符串和正文组成。
现在它切断了 Webview,其余内容都塞进了 scrollview。我放在 webview 之前的图像和文本也保持固定,同时我希望它们也向下滚动。我不想要 webview 的滚动条,我只希望 webview 的大小与其内容一致,以便用户可以向上滑动以查看整个文本。
CustomWebViewRenderer.cs
[assembly: ExportRenderer(typeof(PostWebView), typeof(Yoors.iOS.CustomWebViewRenderer))]
namespace Yoors.iOS
{
public class CustomWebViewRenderer : ViewRenderer<PostWebView, WKWebView>
{
WKWebView _wkWebView;
protected override void OnElementChanged(ElementChangedEventArgs<PostWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
WKWebViewConfiguration config = new WKWebViewConfiguration();
_wkWebView = new WKWebView(Frame, config);
SetNativeControl(_wkWebView);
}
if (e.NewElement != null)
{
HtmlWebViewSource source = (Xamarin.Forms.HtmlWebViewSource)Element.Source;
string headerString = "<header><meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0'></header>";
string html= headerString + source.Html;
Console.WriteLine("Height" + Element);
_wkWebView.LoadHtmlString(html, baseUrl: null);
_wkWebView.ScrollView.ScrollEnabled = false;
_wkWebView.SizeToFit();
}
}
}
PostWebView.xaml
<?xml version="1.0" encoding="UTF-8"?>
<WebView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Yoors.Views.Templates.PostWebView" x:Name="WebViewer" BackgroundColor="White" Margin="0, 10, 0, 0" VerticalOptions="FillAndExpand" HeightRequest="1000">
<WebView.Source>
<HtmlWebViewSource Html="{Binding Data.Content}" />
</WebView.Source>
</WebView>
PostView.xaml
<ContentPage.Content>
<ScrollView>
<StackLayout BackgroundColor="White" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<!-- Header of the post-->
<Image HeightRequest="125" Aspect="AspectFill" Source="{Binding Post.ImageUrl}" />
<StackLayout BackgroundColor="White" Padding="1" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Margin="10, 0,10, 0">
<Label Text="{Binding Post.Title}" FontFamily="{StaticResource BoldFont}" FontSize="20" />
<Label Text="{Binding Post.CreatedOn.DisplayText}" FontSize="12" />
<!-- Content of the post in a HTML view-->
</StackLayout>
<templates:PostWebView VerticalOptions="FillAndExpand" BindingContext="{Binding Post}">
</templates:PostWebView>
<templates:CommentView BindingContext="{Binding CommentsViewModel}">
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
</templates:CommentView>
</StackLayout>
</ScrollView>
</ContentPage.Content>
我认为为 PostWebView 放置 VerticalOptions= "FillAndExpand" 会使其适合其内容的大小,而无需创建 scollbar,因此不会固定其他内容,但它只是这样做:
我通过创建一个在 Webview 完成加载时注册的 CustomNavigationDelegate 回答了我自己的问题。
CustomNavigationDelegate.cs
public class CustomWKNavigationDelegate : WKNavigationDelegate
{
CustomWebViewRenderer _webViewRenderer;
public CustomWKNavigationDelegate(CustomWebViewRenderer webViewRenderer)
{
_webViewRenderer = webViewRenderer;
}
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
var wv = _webViewRenderer.Element as PostWebView;
if (wv != null)
{
await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
wv.HeightRequest = (double)webView.ScrollView.ContentSize.Height;
}
}
}
然后在上面显示的 CustomWebViewRenderer 的 OnElementChanged 中,我将 Navigation Delegate 分配给 WKWebView。
CustomWebViewRenderer.cs
...
protected override void OnElementChanged(ElementChangedEventArgs<PostWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
WKWebViewConfiguration config = new WKWebViewConfiguration();
_wkWebView = new WKWebView(Frame, config);
_wkWebView.NavigationDelegate = new CustomNavigationDelegate(this);
...
这将使 WKWebView 具有其内容的大小!!
上面的答案是正确的,但前提是您最初没有在 xaml / 后面的代码中为 CustomWebView 设置更高的值。
我必须在后面的代码中设置一些初始大小,否则 webview 根本不会显示:
var myDisplay = DeviceDisplay.MainDisplayInfo;
mywebView.HeightRequest = myDisplay.Height;
mywebView.WidthRequest = myDisplay.Width;
但是使用此设置,CustomNavigationDelegate 将始终报告与初始化中设置的设备显示高度相同的大小 (2208px)。这似乎覆盖了以后的重新计算。 我通过设置较低的初始高度来修复它,然后动态调整到内容所需的更大高度:
var myDisplay = DeviceDisplay.MainDisplayInfo;
mywebView.HeightRequest = 800;
mywebView.WidthRequest = myDisplay.Width;
以前我在 webview 区域有白屏,动态高度也不起作用。但现在这很好用!也张贴在这里 - https://github.com/xamarin/Xamarin.Forms/issues/1711
[assembly: ExportRenderer(typeof(ExtendedWebView), typeof(ExtendedWebViewRenderer))]
namespace Project.iOS.Renderers
{
class ExtendedWebViewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
try
{
base.OnElementChanged(e);
NavigationDelegate = new ExtendedWKWebViewDelegate(this);
// For fixing white flash issue in webview on dark themes/backgrounds and disable webview's scrolling
if (NativeView != null)
{
var webView = (WKWebView)NativeView;
webView.Opaque = false;
webView.BackgroundColor = UIColor.Clear;
webView.ScrollView.ScrollEnabled = false;
}
}
catch (Exception ex)
{
Console.WriteLine("Error at ExtendedWebViewRenderer OnElementChanged: " + ex.Message);
}
}
}
class ExtendedWKWebViewDelegate : WKNavigationDelegate
{
ExtendedWebViewRenderer webViewRenderer;
public ExtendedWKWebViewDelegate(ExtendedWebViewRenderer _webViewRenderer)
{
webViewRenderer = _webViewRenderer ?? new ExtendedWebViewRenderer();
}
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
var wv = webViewRenderer.Element as ExtendedWebView;
if (wv != null && webView != null)
{
await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
if (webView.ScrollView != null && webView.ScrollView.ContentSize != null)
{
wv.HeightRequest = (double)webView.ScrollView.ContentSize.Height;
}
}
}