sunagimoブログ

主にUnityに関する技術を取り上げます

【Unity】Webviewの実装(Android、iOS対応)

本記事で扱うUnityのバージョンは「Unity2018.3.7f1」です。


UnityでのWebviewのライブラリというと、
greeさんの「unity-webview」
github.com


もしくは、AssetStoreで配信されている「UniWebView」を使用されていると思います。
https://www.assetstore.unity3d.com/jp/?stay#!/content/92605



今回はgreeさんの「unity-webview」を使用して、
サンプルを作成したので使い方や解説を行っていきます。



実装例
github.com


基本はサンプル通りに処理を書いています。


ただ少し違うのが、WebView内で選択したリンクをそのままUnity側に送って、
Unity側で処理するようにしています。

Android

Android PluginのCWebViewPlugin.javaのshouldOverrideUrlLoading内の処理を一部修正しています。

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    canGoBack = webView.canGoBack();
    canGoForward = webView.canGoForward();
    // if (url.startsWith("http://") || url.startsWith("https://")
    //     || url.startsWith("file://") || url.startsWith("javascript:")) {
    //     // Let webview handle the URL
    //     return false;
    // } else if (url.startsWith("unity:")) {
    //     String message = url.substring(6);
    //     mWebViewPlugin.call("CallFromJS", message);
    //     return true;
    // }
    // すべてUnity側へ飛ばすように修正。
    if (url.startsWith("http://") || url.startsWith("https://")
    || url.startsWith("file://") || url.startsWith("javascript:") || url.startsWith("unity:")) {
        mWebViewPlugin.call("CallFromJS", url);
        return true;
    }
    
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
    PackageManager pm = a.getPackageManager();
    List<ResolveInfo> apps = pm.queryIntentActivities(intent, 0);
    if (apps.size() > 0) {
        view.getContext().startActivity(intent);
    }
    return true;
}





iOS

WebView.mmのdecidePolicyForNavigationActionの処理の一部を修正しています。

- (void)webView:(WKWebView *)wkWebView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    if (webView == nil) {
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    NSURL *nsurl = [navigationAction.request URL];
    NSString *url = [nsurl absoluteString];
    // URLの文字列が文字化けしてしまうためエンコード。
    NSString *sendUrl = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]];
    if ([url rangeOfString:@"//itunes.apple.com/"].location != NSNotFound) {
        [[UIApplication sharedApplication] openURL:nsurl];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    } else if ([url hasPrefix:@"unity:"]) {
        // UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [[url substringFromIndex:6] UTF8String]);
        UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [sendUrl UTF8String]);
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    // } else if (![url hasPrefix:@"about:blank"]  // for loadHTML(), cf. #365
    //            && ![url hasPrefix:@"file:"]
    //            && ![url hasPrefix:@"http:"]
    //            && ![url hasPrefix:@"https:"]
    //            && ![url hasPrefix:@"mailto:"]
    //            && ![url hasPrefix:@"tel:"]
    //            && ![url hasPrefix:@"facetime:"]
    //            && ![url hasPrefix:@"sms:"]) {
    //     if([[UIApplication sharedApplication] canOpenURL:nsurl]) {
    //         [[UIApplication sharedApplication] openURL:nsurl];
    //     }
    //     decisionHandler(WKNavigationActionPolicyCancel);
    //     return;
    } else if (navigationAction.navigationType == WKNavigationTypeLinkActivated
               && (!navigationAction.targetFrame || !navigationAction.targetFrame.isMainFrame)) {
        // cf. for target="_blank", cf. http://qiita.com/ShingoFukuyama/items/b3a1441025a36ab7659c
        // [webView load:navigationAction.request];
        // 全てのメッセージを送ってあげるようにする。
        UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [sendUrl UTF8String]);
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    } else {
        if (navigationAction.targetFrame != nil && navigationAction.targetFrame.isMainFrame) {
            // If the custom header is not attached, give it and make a request again.
            if (![self isSetupedCustomHeader:[navigationAction request]]) {
                NSLog(@"navi ... %@", navigationAction);
                [wkWebView loadRequest:[self constructionCustomHeader:navigationAction.request]];
                decisionHandler(WKNavigationActionPolicyCancel);
                return;
            }
        }
    }
    UnitySendMessage([gameObjectName UTF8String], "CallOnStarted", [url UTF8String]);
    decisionHandler(WKNavigationActionPolicyAllow);
}






他にもiOS側でもいくつか処理の追加を行っています。

bouncesの機能をOFFに変更

スクロールを最後まで引っ張ったときに、空白の部分まで見えてしまうやつ

画像をドラッグアンドドロップによる画像反転禁止
    // bouncesをOFFに変更。
    id subview = [[webView subviews] objectAtIndex:0];
    if([[subview class] isSubclassOfClass:[UIScrollView class]])
    {
        ((UIScrollView *)subview).bounces = NO;

        // iOS11からのドラッグアンドドロップによる画像反転禁止。
        if(@available(iOS 11.0, *))
        {
            ((UIScrollView *)subview).interactions = [[NSArray alloc] init];
        }
    }




3DTouchの機能を無効化
// iOS10以降で3DTouchを無効にする。
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
- (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo
{
    return false;
}
#endif





WebView内のテキスト選択を禁止

長押しによるメニューの表示禁止

// 読み込み完了時に呼び出される。
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
    // webView内のテキスト選択禁止。
    [webView evaluateJavaScript:(@"document.documentElement.style.webkitUserSelect='none';") completionHandler:^(NSString *result, NSError *error) {}];
    // webView内の長押しによるメニュー表示禁止。
    [webView evaluateJavaScript:(@"document.documentElement.style.webkitTouchCallout='none';") completionHandler:^(NSString *result, NSError *error) {}];
}





ここらへんの処理も追加せずに、パラメータなどで切り替えられたらいいな
もういっそUnityでAPI化してほしい!