Я внедрил Hybrid WebView в Xamarin IOS, следуя документации на официальном сайте Microsoft Xamarin.https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/hybridwebview.
Мой код точно такой же, как приведенный в документации, но я все еще публикую его для целей отладки.Постановка проблемы: функция DidReceiveScriptMessage иногда не вызывается, когда я обновляю URI веб-страницы и загружаю новую HTML-страницу.Он прекрасно работает, когда тот же обновленный URI загружается в качестве начальной страницы в веб-представлении.
По умолчанию URI (рабочий)
Обновлено URI [URI1 - URI2] (не работает)
Шаги выполнены:
1: очистить сборку и развернуть
2. Обновлены формы Xamrinдо 3,6
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>, IWKScriptMessageHandler
{
const string JavaScriptFunction = "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
WKUserContentController userController;
ILogUtils log = DependencyService.Get<ILogUtils>();
protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> 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);
//custom delegate for enabling alert
webView.UIDelegate = new MyWKWebViewDelegate();
SetNativeControl(webView);
}
if (e.OldElement != null)
{
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
e.OldElement.PageChange += UpdateUri;
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
e.NewElement.PageChange += UpdateUri;
}
}
//this method gets called when invokeCSharpAction(data) in javascript gets called
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
//send data to arduino
Console.WriteLine("Data Send to Arduino "+ message.Body.ToString());
BluetoothManager.SendData(message.Body.ToString());
}
public void UpdateUri(object sender, PageChangeEventArgs e)
{
try
{
string filePath = Path.Combine(AppConstants.IOS_DIRECTORY, e.Uri);
//this is the path for the assets
//string filePath = Path.Combine(NSBundle.MainBundle.BundlePath, string.Format("Content/{0}", e.Uri));
//this method tests if all files exist
//bool allFilesExist = DoAllFilesExist();
//if (!allFilesExist)
//{
// log.Log("HybridWebViewRenderer.UpdateUri: Error - Not all files exist locally!");
//}
//this method tests if you can iterate from the .../index.html to the .../js/ files
//IterateDirectories(filePath);
bool fileExist = File.Exists(filePath);
if (fileExist)
{
/* If you want to use external local files(js, css, ...) you have to give the file the access to read the whole directory
--> LoadFileUrl( url: filePath , allowingReadAccessTo: AppConstants.IOS_DIRECTORY
*/
Control.LoadFileUrl(new NSUrl(filePath, false), new NSUrl(AppConstants.IOS_DIRECTORY, true));
}
else
{
Control.LoadHtmlString("<p>Error: File doesn't exist</p>", null);
log.Log("HybridWebViewRenderer.UpdateUri: Error - File does not exist");
}
}
catch (Exception ex)
{
//When there is a Error loading the file, load the following HTML-ErrorMessage
//log.Log("HybridWebViewRenderer.UpdateUri: " + ex.Message);
Control.LoadHtmlString("<p>Error</p>", null);
log.Log("HybridWebViewRenderer.UpdateUri: " + ex.Message);
}
}
public bool DoAllFilesExist()
{
iOSFileHandler handler = new iOSFileHandler();
return handler.checkIfAllFilesAvailable(AppConstants.IOS_DIRECTORY);
}
public void IterateDirectories(string filePath)
{
try
{
string abc = Path.GetDirectoryName(filePath);
string[] abcFiles = Directory.GetFiles(abc);
string documents = Path.GetDirectoryName(abc);
string[] documentChildFolders = Directory.GetDirectories(documents);
string js = Path.Combine(documents, "js");
string[] jsFiles = Directory.GetFiles(js);
for(int i=0; i < jsFiles.Length; i++)
{
string content = File.ReadAllText(jsFiles[i]);
}
}
catch(Exception e)
{
}
}
}
Еще один класс здесь
public class PageChangeEventArgs : EventArgs
{
public string Uri { get; set; }
}
public class HybridWebView : View
{
Action<string> action;
//public AppDelegate UpdateApp;
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public static readonly BindableProperty AppProperty = BindableProperty.Create(
propertyName: "App",
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
public event EventHandler<PageChangeEventArgs> PageChange;
public virtual void OnPageChange(PageChangeEventArgs e)
{
PageChange?.Invoke(this, e);
}
public void RegisterAction(Action<string> callback)
{
action = callback;
}
public void Cleanup()
{
action = null;
}
public void Pair()
{
}
public void InvokeAction(string data)
{
if (action == null || data == null)
{
return;
}
action.Invoke(data);
}
}
Главная страница здесь.
public partial class MainPage : ContentPage, IMainPage
{
IDownloader downloader = DependencyService.Get<IDownloader>();
IFileHandler fileHandler = DependencyService.Get<IFileHandler>();
ILogUtils log = DependencyService.Get<ILogUtils>();
private string StartPageId
{
get { return "ac"; }
}
public MainPage(bool firstStart)//if the app starts the first time --> true; else --> false
{
InitializeComponent();
//initialize downloadEvent
downloader.OnFileDownloaded += OnFileDownloaded;
//update Connectionstatus
UpdateConnectionStatusFooter();
//labelOS.TextColor = Xamarin.Forms.Color.FromHex("#FF6600");
//labelOS.FontAttributes = FontAttributes.Bold;
NavigationPage.SetHasNavigationBar(this, false);
if (firstStart == true)
{
//copy assets only when the app starts the first time
CopyAssets();
//you have to reset the dictionary because otherwise the id from the previous demonstrator is still saved(dictionary is persistent!)
ResetDictionary();
}
}
private void CopyAssets()
{
fileHandler.CopyAssetsToFolder().ContinueWith(t =>
{
Device.BeginInvokeOnMainThread(() =>
{
hybridWebView.OnPageChange(new PageChangeEventArgs() { Uri = AppConstants.START_PAGE_ID + "/index.html" });
//this method has to be called after the connection with the demo was successful. Its here for testing purposes and because the Bluetooth is not implemented yet
downloader.test(AppConstants.START_PAGE_ID);
//this method reads the log data to check if the log works
string logtest = readLogData();
});
//calling this code after CopyAssetsToFolder finished
if (t.Status != TaskStatus.RanToCompletion)
return;
});
}
private string readLogData()
{
string logPath = Path.Combine(AppConstants.IOS_DIRECTORY, "Log/log.txt");
if (File.Exists(logPath))
{
string logContent = File.ReadAllText(logPath);
return logContent;
}
else
{
return "";
}
}
protected override void OnAppearing()
{
base.OnAppearing();
UpdateConnectionStatusFooter();
//updates the Html only when there is an id in the dictionary(after connecting to arduino)
try
{
string id = Application.Current.Properties["id"].ToString();
UpdateHTML(id);
}
catch (KeyNotFoundException e)
{
Console.WriteLine("Error in MainPage.OnAppearing: " + e.Message);
}
}
public bool StartCheckingConnectionLoop()
{
//check every second if you are disconnected
//if the connectionState change from true to false-->update UI and stop loop
//you only get false here, when the following code is executed: BthSocket.Close(); (in Bluetooth.Disconnect)
if (BluetoothManager.GetConnectionStatus() == false)
{
Device.BeginInvokeOnMainThread(() =>
{
UpdateConnectionStatusFooter();
});
//stop the loop
return false;
}
////if connectionState is the same -->continue with loop
return true;
}
private void ResetDictionary()
{
try
{
//delete all key-values that were saved persistent
Application.Current.Properties.Clear();
Application.Current.SavePropertiesAsync();
}
catch (Exception e)
{
Console.WriteLine("Error in MainPage.resetDictionary: " + e.Message);
log.Log("MainPage.ResetDictionary: " + e.Message);
}
}
public void UpdateHTML(string id)
{
try
{
string savedId = Application.Current.Properties["id"].ToString();
hybridWebView.OnPageChange(new PageChangeEventArgs() { Uri = savedId + "/index.html" });
}
catch (Exception e)
{
Console.WriteLine("Error in MainPage.updateHTML: " + e.Message);
log.Log("MainPage.UpdateHTML: " + e.Message);
}
}
Может кто-нибудь дать мне подсказки, как это исправить.