Hi fellas,
I've a HybridWebview with handle JS Event Register and Evaluate JS fuctions and its works well. But i need some improvment on my hybridview whitch is js alert,promp, etc. support. I already find a way in Android side adding setWebCrome parametters. But still have problem with ios part. I find a solution on xamarin recepit called webview handle with js project but i couldn't understand how can i implament this.
Here is my HybridWebview code on X.Forms Side
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Paperwork.Mobile.CustomControls
{
public class PWHybridWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(
"Uri",
typeof(string),
typeof(PWHybridWebView),
default(string));
//public static BindableProperty DialogProperty =
// BindableProperty.Create(nameof(Dialog), typeof(IUserDialogs), typeof(PWHybridWebView), null);
//public IUserDialogs Dialog
//{
// get { return (IUserDialogs)GetValue(DialogProperty); }
// set { SetValue(UriProperty, value); }
//}
public static BindableProperty EvaluateJavascriptProperty =
BindableProperty.Create(nameof(EvaluateJavascript), typeof(Func<string, Task<string>>),
typeof(PWHybridWebView), null, BindingMode.OneWayToSource);
private Action<string> action;
public Func<string, Task<string>> EvaluateJavascript
{
get => (Func<string, Task<string>>) GetValue(EvaluateJavascriptProperty);
set => SetValue(EvaluateJavascriptProperty, value);
}
public string Uri
{
get => (string) GetValue(UriProperty);
set => SetValue(UriProperty, value);
}
public void RegisterAction(Action<string> callback)
{
action = callback;
}
public void Cleanup()
{
action = null;
}
public void InvokeAction(string data)
{
if (action == null || data == null) return;
action.Invoke(data);
}
}
}
And ios Renderer
using System;
using System.Threading;
using System.Threading.Tasks;
using Foundation;
using Paperwork.Mobile.CustomControls;
using Paperwork.Mobile.iOS.CustomRenderers;
using WebKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(PWHybridWebView), typeof(PWHybridWebViewRenderer))]
namespace Paperwork.Mobile.iOS.CustomRenderers
{
public class PWHybridWebViewRenderer : ViewRenderer<PWHybridWebView, WKWebView>, IWKScriptMessageHandler,
IWKNavigationDelegate
{
private const string JavaScriptFunction =
"function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
private WKUserContentController userController;
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
Element.InvokeAction(message.Body.ToString());
}
protected override void OnElementChanged(ElementChangedEventArgs<PWHybridWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
userController = new WKUserContentController();
var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd,
false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction");
var config = new WKWebViewConfiguration {UserContentController = userController};
var webView = new WKWebView(Frame, config);
SetNativeControl(webView);
}
if (e.OldElement != null)
{
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
var hybridWebView = e.OldElement;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.Uri)));
var webView = e.NewElement;
//webView.EvaluateJavascript.Invoke("Save()");
webView.EvaluateJavascript = async js =>
{
var reset = new ManualResetEvent(false);
Device.BeginInvokeOnMainThread(async () =>
{
try
{
await Control.EvaluateJavaScriptAsync(js);
}
catch (Exception exception)
{
//
}
});
await Task.Run(() => { reset.WaitOne(); });
return null;
};
}
}
}
}
and also here is a solution from xamarin recepit
using System;
using UIKit;
using WebKit;
using Foundation;
using System.IO;
namespace WKUIDelegateDemo
{
// We'll use the IWKUIDelegate protocol to conform to WKUIDelegate
public partial class ViewController : UIViewController, IWKUIDelegate
{
WKWebView webView;
public ViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Create a new WKWebView, assign the delegate and add it to the view
webView = new WKWebView(View.Frame, new WKWebViewConfiguration());
webView.UIDelegate = this;
View.AddSubview (webView);
// Find the Alerts.html file and load it into the WKWebView
string htmlPath = NSBundle.MainBundle.PathForResource ("Alerts", "html");
string htmlContents = File.ReadAllText (htmlPath);
webView.LoadHtmlString (htmlContents, null);
}
// Called when a Javascript alert() is called in the WKWebView
// The alert panel should display the message and a single "OK" button
[Foundation.Export ("webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:")]
public void RunJavaScriptAlertPanel (WebKit.WKWebView webView, string message, WebKit.WKFrameInfo frame, System.Action completionHandler)
{
// Create and present a native UIAlertController with the message
var alertController = UIAlertController.Create (null, message, UIAlertControllerStyle.Alert);
alertController.AddAction (UIAlertAction.Create ("Ok", UIAlertActionStyle.Default, null));
PresentViewController (alertController, true, null);
// Call the completion handler
completionHandler ();
}
// Called when a Javascript confirm() alert is called in the WKWebView
// The alert panel should display the message with two buttons - "OK" and "Cancel"
[Export ("webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:")]
public void RunJavaScriptConfirmPanel (WKWebView webView, string message, WKFrameInfo frame, Action<bool> completionHandler)
{
// Create a native UIAlertController with the message
var alertController = UIAlertController.Create (null, message, UIAlertControllerStyle.Alert);
// Add two actions to the alert. Based on the result we call the completion handles and pass either true or false
alertController.AddAction (UIAlertAction.Create ("Ok", UIAlertActionStyle.Default, okAction => {
completionHandler(true);
}));
alertController.AddAction (UIAlertAction.Create ("Cancel", UIAlertActionStyle.Default, cancelAction => {
completionHandler (false);
}));
// Present the alert
PresentViewController (alertController, true, null);
}
// Called when a Javascript prompt() alert is called in the WKWebView
// The alert panel should display the prompt, default placeholder text and two buttons - "OK" and "Cancel"
[Foundation.Export ("webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:")]
public void RunJavaScriptTextInputPanel (WebKit.WKWebView webView, string prompt, string defaultText, WebKit.WKFrameInfo frame, System.Action<string> completionHandler)
{
// Create a native UIAlertController with the message
var alertController = UIAlertController.Create (null, prompt, UIAlertControllerStyle.Alert);
// Add a text field to the alert, set the placeholder text and keep a refernce to the field
UITextField alertTextField = null;
alertController.AddTextField ((textField) => {
textField.Placeholder = defaultText;
alertTextField = textField;
});
// Pass the text to the completion handler when the "OK" button is tapped
alertController.AddAction (UIAlertAction.Create ("Ok", UIAlertActionStyle.Default, okAction => {
completionHandler (alertTextField.Text);
}));
// If "Cancel" is tapped, we can just return null
alertController.AddAction (UIAlertAction.Create ("Cancel", UIAlertActionStyle.Default, cancelAction => {
completionHandler (null);
}));
// Present the alert
PresentViewController (alertController, true, null);
}
}
}