iOS JavaScript міст


101

Я працюю над додатком, де я разом буду використовувати як HTML5 в UIWebView, так і рідний iOS. Я знаю, що можу реалізувати зв’язок між JavaScript та Objective-C. Чи є бібліотеки, які спрощують реалізацію цього спілкування? Я знаю, що існує декілька бібліотек для створення нативних програм для iOS у HTML5 та javascript (наприклад, AppMobi, PhoneGap), але я не впевнений, чи існує бібліотека, яка допоможе створити натомість додатки для iOS з великим використанням JavaScript. Мені потрібно:

  1. Виконати методи JS від Objective-C
  2. Виконати методи Objective-C з JS
  3. Слухайте рідні події JS від Objective-C (наприклад, DOM готовий захід)

1
Ви можете використовувати WKWebView: виклик з javascript window.webkit.messageHandlers. {NAME} .postMessage (повідомлення), а потім обробити його з [WKUserContentController addScriptMessageHandler: name:] для виклику Objective-C від JS
kostyl

Відповіді:


150

Існує кілька бібліотек, але я не використовував жодної з них у великих проектах, тому ви можете спробувати їх:

-

Однак я думаю, що це щось досить просте, що ви можете спробувати самі. Я особисто робив саме це, коли мені потрібно було це робити. Ви також можете створити просту бібліотеку, яка відповідає вашим потребам.

1. Виконайте методи JS від Objective-C

Це дійсно лише один рядок коду.

NSString *returnvalue = [webView stringByEvaluatingJavaScriptFromString:@"your javascript code string here"];

Детальніше про офіційну документацію UIWebView .

2. Виконати методи Objective-C з JS

На жаль, це трохи складніше, оскільки не існує того самого властивості windowScriptObject (і класу), який існує на Mac OSX, що дозволяє повноцінно спілкуватися між ними.

Однак ви можете легко зателефонувати за допомогою спеціальних URL-адрес JavaScript, наприклад:

window.location = yourscheme://callfunction/parameter1/parameter2?parameter3=value

І перехопіть це від Objective-C з цим:

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
   NSURL *URL = [request URL]; 
   if ([[URL scheme] isEqualToString:@"yourscheme"]) {
       // parse the rest of the URL object and execute functions
   } 
}

Це не так чисто, як це повинно бути (або за допомогою windowScriptObject), але це працює.

3. Прослуховуйте рідні події JS від Objective-C (наприклад, DOM готовий захід)

З наведеного вище пояснення ви бачите, що якщо ви хочете це зробити, вам потрібно створити якийсь код JavaScript, приєднати його до події, яку ви хочете відстежувати, і викликати правильний window.locationвиклик, який слід перехопити.

Знову ж таки, не чисто, як це має бути, але це працює.


12
Підказка: не вводьте підкреслені або подібні у вашій схемі.
fabb

4
і поверніть значення, коли вам належить :)
Pizzaiola Gorgonzola

2
Ще одна підказка: Завантажте URL-адресу в iFrame, щоб запобігти мерехтінню
iCaramba

@Folleto Привіт, ви сказали, "але я нічого не використовував у великих проектах", чому б і ні ?? Ви знайшли кілька питань про ці бібліотеки ??? Заздалегідь спасибі.
JERC

1
Немає питань. Я просто не використовував їх, тому не можу коментувати використання їх у великих проектах. Я хотів уточнити це. :)
Фоллетто

57

Запропонований метод виклику об'єкта c з JS у прийнятій відповіді не рекомендується. Один із прикладів проблем: якщо ви здійснюєте два негайних послідовних дзвінки, один ігнорується (ви не можете змінити місце розташування занадто швидко).

Я рекомендую наступний альтернативний підхід:

function execute(url) 
{
  var iframe = document.createElement("IFRAME");
  iframe.setAttribute("src", url);
  document.documentElement.appendChild(iframe);
  iframe.parentNode.removeChild(iframe);
  iframe = null;
}

Ви викликаєте executeфункцію неодноразово, і оскільки кожен виклик виконується у власному iframe, їх не слід ігнорувати при швидкому виклику.

Подяки цьому хлопцеві .


1
Ви також можете використовувати чергу в JavaScript, яку iOS може отримати, щоб уникнути втрати повідомлень із .src занадто швидко. Зв'язане вище виконання, github.com/marcuswestin/WebViewJavascriptBridge , використовує підхід черги.
кевінткафлін

12

Оновлення: це змінилося в iOS 8. Моя відповідь стосується попередніх версій.

Альтернативою, яка може вас відхилити в магазині додатків, є використання WebScriptObject.

Ці API є загальнодоступними на OSX, але не на iOS.

Потрібно визначити інтерфейси до внутрішніх класів.

@interface WebScriptObject: NSObject
@end

@interface WebView
- (WebScriptObject *)windowScriptObject;
@end

@interface UIWebDocumentView: UIView
- (WebView *)webView;
@end

Вам потрібно визначити свій об'єкт, який буде служити вашим WebScriptObject

@interface WebScriptBridge: NSObject
- (void)someEvent: (uint64_t)foo :(NSString *)bar;
- (void)testfoo;
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name;
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
+ (WebScriptBridge*)getWebScriptBridge;
@end

static WebScriptBridge *gWebScriptBridge = nil;

@implementation WebScriptBridge
- (void)someEvent: (uint64_t)foo :(NSString *)bar
{
    NSLog(bar);
}

-(void)testfoo {
    NSLog(@"testfoo!");
}

+ (BOOL)isKeyExcludedFromWebScript:(const char *)name;
{
    return NO;
}

+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
{
    return NO;
}

+ (NSString *)webScriptNameForSelector:(SEL)sel
{
    // Naming rules can be found at: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/WebKit/Protocols/WebScripting_Protocol/Reference/Reference.html
    if (sel == @selector(testfoo)) return @"testfoo";
    if (sel == @selector(someEvent::)) return @"someEvent";

    return nil;
}
+ (WebScriptBridge*)getWebScriptBridge {
    if (gWebScriptBridge == nil)
        gWebScriptBridge = [WebScriptBridge new];

    return gWebScriptBridge;
}
@end

Тепер встановіть цей екземпляр у свій UIWebView

if ([uiWebView.subviews count] > 0) {
    UIView *scrollView = uiWebView.subviews[0];

    for (UIView *childView in scrollView.subviews) {
        if ([childView isKindOfClass:[UIWebDocumentView class]]) {
            UIWebDocumentView *documentView = (UIWebDocumentView *)childView;
            WebScriptObject *wso = documentView.webView.windowScriptObject;

            [wso setValue:[WebScriptBridge getWebScriptBridge] forKey:@"yourBridge"];
        }
    }
}

Тепер всередині вашого javascript ви можете зателефонувати:

yourBridge.someEvent(100, "hello");
yourBridge.testfoo();

Моя програма відхилена в магазині додатків. Я отримав проблему: "Додаток містить або успадковує непублічні класи в AppName: UIWebDocumentView"
chandru

5

У iOS8 ви можете подивитися на WKWebView замість UIWebView . Він має такий клас: WKScriptMessageHandler: надає спосіб прийому повідомлень з JavaScript, що працюють на веб-сторінці.


Звичайно, але метод не виконує
континуулси

WKWebViewмає багато проблем, зокрема неправильне поводження з файлами cookie. Просто попередження для всіх, хто приходить сюди, думаючи, що це вирішить усі ваші проблеми - гугл трохи перед тим, як прийняти його.
CupawnTae

3

Це можливо за допомогою iOS7, замовлення http://blog.bignerdranch.com/3784-javascriptcore-and-ios-7/


3
Насправді: JavaScriptCore не взаємодіє з UIWebViews. Це чудовий інструмент, але для іншого класу проблем. ;)
Фоллетто

1
Так, це так: JSContext * context = [_webView значенняForKeyPath: @ "documentView.webView.mainFrame.javaScriptContext"]; Але JavaScriptCore занадто глючно на даний момент, щоб мати реальну користь.
мальхаль

0

Ваша найкраща ставка - додатки титанових додатків для додатків. Вони вже побудували Javascript міст Obj-C за допомогою двигуна V8 двигуна JavascriptCore, який використовує webkit. Він також є відкритим кодом, тому ви зможете завантажити його і попрацювати з Obj-C як завгодно.


@hova, міст не Obj-C V8. V8 використовується лише для Android.
Росс

0

Погляньте на проект KirinJS: Kirin JS, який дозволяє використовувати Javascript для логіки програми та власного інтерфейсу, адекватного платформі, на якій він працює.


хтось ще сприяє кирину? я не бачу жодної активності останні кілька місяців ...
Сем

0

Я створив бібліотеку на зразок WebViewJavascriptBridge, але вона більше схожа на JQuery, простіша в налаштуванні та простіша у використанні. Не покладається на jQuery (правда, на його честь, якби я знав, що WebViewJavascriptBridge існував до того, як писати це, я, можливо, трохи стримувався перед зануренням). Дайте мені знати, що ви думаєте! жокеї


0

Якщо ви використовуєте WKWebView в iOS 8, погляньте на XWebView, який може автоматично виставити власний інтерфейс на javascript.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.