I have a cross-platform mobile app (Android/iOS) which implements the generic WebView control. This works well for most circumstances, but some iOS users complain that, when attempting to load a certain resource-intensive web page, the app "goes black" and then focus returns to the Menu view. My suspicion is that the app is choking due to the amount of content and processing overhead of the web page, but frankly this is a blind guess and I don't have the resources (such as an iPhone at my disposal) in order to verify this. Using an iPhone simulator on a Mac does not reproduce the "black screen" issue.
Therefore, I am attempting to implement in parallel WkWebView for iOS devices at version 8.0 and above as this is presumably more performant and might alleviate the problem. It is just about working, but there seems to be a disconnect between the ViewController and ContentPage which is supposed to host the WkWebView control which I have been unable to rectify.
Below is the general implementation:
[XamlCompilation(XamlCompilationOptions.Compile)] public partial class WkWebViewPage : ContentPage { public WkWebViewPage (string url, string title) { InitializeComponent(); Title = title; App.GetWkWebView(this, url); } }
The markup for WkWebPageView has no inner content.
The method App.GetWkWebView is a delegate of type Action<Page, string> implemented as a static property in the main App class. This is assigned in the FinishedLaunching method of the AppDelegate class (iOS project) to a static method in the static class I am using to manage invoking the WkWebView. This class is implemented as such:
public static class WkWebViewController { private static WKWebView _wkWebView; public static void GetWkWebView(Page parentView, string url) { if(_wkWebView == null) { // INSERT ATTEMPTED APPROACHES BELOW HERE var frame = view.Frame; var cgRect = new CoreGraphics.CGRect(frame.X, frame.Y, frame.Width, frame.Height); _wkWebView = new WKWebView(cgRect, new WKWebViewConfiguration()); view.AddSubview(_wkWebView); // NavigationDelegate is a custom class; not germane to the issue _wkWebView.NavigationDelegate = new NavigationDelegate(); } var nsUrl = new NSUrl(url); var request = new NSUrlRequest(nsUrl); _wkWebView.LoadRequest(request); } }
Here is where the trouble begins. I have tried two approaches to obtaining the appropriate ViewController -- and more pertinently, the UIView object:
1)
var renderer = Platform.GetRenderer(parentView); if (renderer == null) { renderer = Platform.CreateRenderer(parentView); Platform.SetRenderer(parentView, renderer); } var view = renderer.ViewController.View;
This results in:
I WOULD POST AN IMAGE HERE, BUT AM PREVENTED FROM DOING SO; THEREFORE, PLEASE USE YOUR IMAGINATION.
The content area is white/blank. The http request is submitted successfully as a 200 response is received. Note that the navigation bar above the content area properly displays.
2)
var window = UIApplication.SharedApplication.KeyWindow; var vc = window.RootViewController; while (vc.PresentedViewController != null) { vc = vc.PresentedViewController; } var view = vc.View;
which results in:
I WOULD POST ANOTHER IMAGE HERE, BUT AM PREVENTED FROM DOING SO; THEREFORE, PLEASE USE YOUR IMAGINATION FOR A SECOND TIME.
In this case, the web page displays; however, the WkWebView control takes up the entire screen, obscuring the navigation bar (and seemingly the Status Bar).
Any suggestions would be greatly appreciated!
NOTE: Links to images for approaches 1 and 2 can be provided upon request.
If you want to use WKWebView instead of UIWebView, why not use Custom Renderer directly?
Firstly, create a new class inherited from WebView in your PCL. And define a bindable property pointing what url should be loaded on iOS. Here is my custom WebView:
public class MyWebView : WebView { public static readonly BindableProperty UrlProperty = BindableProperty.Create( propertyName: "Url", returnType: typeof(string), declaringType: typeof(MyWebView), defaultValue: default(string)); public string Url { get { return (string)GetValue(UrlProperty); } set { SetValue(UrlProperty, value); } } }
The custom renderer for this class on iOS:
[assembly: ExportRenderer(typeof(MyWebView), typeof(MyWebViewRenderer))] namespace WKWebViewDemo.iOS { public class MyWebViewRenderer : ViewRenderer<MyWebView, WKWebView> { WKWebView _wkWebView; protected override void OnElementChanged(ElementChangedEventArgs<MyWebView> e) { base.OnElementChanged(e); if (Control == null) { var config = new WKWebViewConfiguration(); _wkWebView = new WKWebView(Frame, config); SetNativeControl(_wkWebView); } if (e.NewElement != null) { Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.Url))); } } } }
At last you can use this custom control in the PCL's page, here is the usage:
<StackLayout> <local:MyWebView Url="https://www.microsoft.com" VerticalOptions="FillAndExpand"/> </StackLayout>
Answers
If you want to use WKWebView instead of UIWebView, why not use Custom Renderer directly?
Firstly, create a new class inherited from WebView in your PCL. And define a bindable property pointing what url should be loaded on iOS. Here is my custom WebView:
The custom renderer for this class on iOS:
At last you can use this custom control in the PCL's page, here is the usage:
This is a simple, clear, and elegant solution. Thank you for your help!
@LandLu Hi, I also need to implement the
WKWebView
as there are problems in rendering HTML withUIWebView
, I don't want to create custom control in the shared project, I want to use theWebView
directly and utilize the Source property where it can be an HTML or a URL, also I don't want to break the current code where theWebView
is used heavily.I tried this:
but
new NSUrl(Element.Source)
is not valid!@mshwf Change "Element.Source" to "(Element.Source as UrlWebViewSource).Url"
@LandLu I tried your code but getting

System.ArumentNullException
.Screenshot
For WKWebView , Navigated and navigating methods
step 1:
public class ExtendedWebView: WebView
{
public ExtendedWebView()
{
}
.......................
step -2
namespace *** .iOS.Renderer
{
public class ExtendedWebViewRenderer : ViewRenderer<ExtendedWebView, WKWebView>
{
public ExtendedWebViewRenderer()
{
}
WKWebView webView;
}
step 3:
step 3:
** **
POST Request Over Webview
For WkWebview
[assembly: ExportRenderer(typeof(PaymentWebview), typeof(PaymentWebViewRenderer))]
namespace MMFInvestorApp.iOS.Utils
{
public class PaymentWebViewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
}
For UIWebview (Deprecated from April 2020
[assembly: ExportRenderer(typeof(PaymentWebview), typeof(PaymentWebViewRenderer))]
namespace MMFInvestorApp.iOS.Utils
{
public class PaymentWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
}