Как вызвать Objective-C из JavaScript в UIWebView в iOS 7?
Обратите внимание, что это не дубликат из-за части "в iOS 7" моего вопроса и того факта, что предыдущее поведение (и, следовательно, ответы на предыдущие вопросы) изменилось.
Я хочу знать, как код JavaScript, вызываемый через UIWebView, может вызывать код Objective C в iOS 7. Это то, что я ранее широко использовал в проектах в прошлом, однако поведение в iOS 7 изменилось, в результате чего делегат webView didFailLoadWithError вызывается, когда раньше этого не делалось.
Я использовал для вызова функциональности Objective C с помощью пользовательского трюка URL:
function invokeObjectiveC(action, target, data) {
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "MY-URLScheme:" + action + ":" + target + ":" + data);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
Конкретная строка, которая заставляет didFailLoadWithError: быть вызванным, выглядит так:
document.documentElement.appendChild(iframe);
Другой распространенный альтернативный подход, распространяемый по Интернету, чтобы сделать то же самое, заключается в следующем:
window.location = "MY-URLScheme:" + action + ":" + target + ":" + data;
И это работает на iOS 7 в том смысле, что didFailLoadWithError не вызывается, однако у него есть (известная) проблема, заключающаяся в том, что при его быстром повторном вызове дважды первый вызов теряется.
Мне было интересно, как Cordova делает это, поэтому я скачал исходный код и посмотрел, и я думаю, что они делают это похоже на меня (3-я строка отличается):
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "MY-URLScheme:" + action + ":" + target + ":" + data);
document.body.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
Однако это все еще приводит к тому, что didFailLoadWithError вызывается. Что заставило меня озадачиться тем, почему Cordova не сделала ничего, чтобы исправить это (при условии, что я правильно смотрю на их код).
Тот факт, что didFailLoadWithError вызывается, не кажется вредным, поскольку страницы все еще загружаются, а Objective-C по-прежнему вызывается. Однако очень и очень неудовлетворительно иметь код, который приводит к вызову метода ошибки, поэтому я хочу найти альтернативный способ.
Поэтому возникает вопрос: есть ли способ вызывать Objective-C из JS, загруженного в UIWebView, надежно и без вызова didFailLoadWithError на iOS 7?
Кто-нибудь может подтвердить, что Cordova использует вышеуказанный способ или что-то еще?
Решения, использующие JavaScriptCore в UIWebView (в отличие от внешнего по отношению к UIWebView), выглядят недокументированными / не поддерживаются в iOS/ хрупки? Например:
Доступ к движку JavaScriptCore UIWebView
Триггер целевой метод C из JavaScript с использованием JavaScriptCore в iOS 7 в ViewControllers
1 ответ
Обходной путь iframe немного глючит и не очень надежен. JavaScriptCore - это хороший подход, но фреймворк закрыт для iOS 6 и iOS 5. Существует более быстрый и быстрый способ вызова Objetive-C из UIWebView. Вы можете реализовать метод категории для функции оповещения.
@implementation UIWebView (JavaScriptAlert)
- (void)webView:(UIWebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame
{
if ([message hasPrefix:CUSTOM_SCHEME_STRING]) {
//process your message
}
else {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:message delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil];
[alert show];
[alert release];
}
}
@end
Когда вы вызываете метод window.alert() из javascript, вызывается вышеупомянутая функция Objetive-C. Используйте свой префикс для ваших сообщений и откройте UIAlertView, когда вы хотите стандартное оповещение.
Этот метод работает на iOS 5, 6 и 7 без проблем. Это очень надежно. Я тестировал вызов оповещения 10000 раз подряд, и никакие сообщения не терялись. В случае решения iframe это не так, многие сообщения теряются, если вы создаете iframe слишком быстро, поэтому вы внедрили пакетную очередь в свой код JS и синхронизировали их с интервалом ~1 мс.