web-dev-qa-db-ja.com

ビュー階層にアタッチされていない場合のWKWebKitjavascriptの実行

WKWebViewからUIWebViewに切り替えたい。私のコードでは、Webビューインスタンスが(ビュー階層にアタッチせずに)オフスクリーンで作成され、URLがロードされます。ロードが完了すると、Webビューは別のUIWindow(インタースティシャル広告など)でユーザーに表示されます。 UIWebViewですべてが正常に機能します。しかし、WKWebViewに切り替えた後、angular javascriptコードは予期しない方法で動作します。

いくつかの調査を行った後、WKWebViewインスタンスがビュー階層にアタッチされていない場合、javascriptタイマー(およびHTTPローダー)が一時停止されることがわかりました:デタッチされたWKWebViewで実行されるjavascriptでタイマーを開始すると、一時停止されませんWebビューがアタッチされるまで発生します。

誰かが可能な回避策を提案できますか(非表示のWebビューをキーウィンドウにアタッチしてから別のウィンドウに移動する以外に)?

問題(または、おそらくWebKit機能)を示すためのサンプルプロジェクトを作成しました

https://dl.dropboxusercontent.com/u/148568148/WebKitViewTest.Zip

テストアプリケーションは、タイマーをスケジュールしてHTTPリクエストをロードするjavascriptを含むWebページをロードします。スイッチを使用して、Webビューを表示/非表示にし(「hidden」プロパティを設定)、ビュー階層にアタッチ/デタッチできます。

Sample app running on iOS 8.0

Webビューがアタッチされている(非表示または表示されている)場合、Loadを押すと、JavaScriptは正常に機能します。successまたはfailureアラートダイアログが表示されます。切り離されている場合、タイマーはビュー階層に接続されている場合にのみ起動します(「切り離し」を「オン」に切り替え、「ロード」を押し、数秒待ってから「オフ」に戻します)。 Safari WebInspectorからconsole.logを確認することもできます。

テストWebページのソースは次のとおりです。

<!doctype html>
<html ng-app="project">
  <head>
    <script
      src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.16/angular.js"></script>
    <script src="project.js"></script>
  </head>
  <body>
    <div id="simple" ng-controller="MyController" data-ng-init="callNotify(message);">
    </div>
  </body>
</html>

そしてjavascript:

angular.
  module('project', []).
  controller('MyController', ['$scope','notify', function ($scope, notify) {
  $scope.callNotify = function(msg) {
    notify(msg);
  };
}]).
  factory('notify', ['$window', '$http', '$q', '$timeout', function(win, $http, $q, $timeout) {
  return function(msg) {
    deferred = $q.defer();

    $timeout(function() {
      $http.get('http://jsonplaceholder.typicode.com/posts').
      success(function(data, status, headers, config) {
        alert("success");
        console.log("success")
      }).
        error(function(data, status, headers, config) {
        alert("error");
        console.log("error")
      });
    });
    return deferred.promise;
  };
}]);
39
Alex Lementuev

同じ問題が発生したので、WKWebViewをビュー階層に追加し、AutoLayoutを使用して画面外に移動する以外に、解決策は見つかりませんでした。幸い、この回避策は確実に機能します。

4
Steve Landey

私はまったく同じ問題を抱えていて、UIStackViewで解決しました。 StackNavigationControllerの最小限のドロップイン置換であるUINavigationControllerを作成しました。これは基本的に、スタックに新しいビューを追加し、最後のビューを除くすべてを非表示にします。したがって、すべてのビューはまだ階層内にあり、実行およびロードされていますが、最後のビューのみが表示されます。アニメーションはなく、APIが不足しているため、自由に構築してください。

_public class StackNavigationController: UIViewController {

    private(set) var viewControllers: [UIViewController] = []
    private var stackView: UIStackView { return view as! UIStackView }

    override public func loadView() {
        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.distribution = .fillEqually
        view = stackView
    }

    func pushViewController(_ viewController: UIViewController, animated: Bool) {
        let previousView = stackView.arrangedSubviews.last
        viewControllers.append(viewController)
        stackView.addArrangedSubview(viewController.view)
        previousView?.isHidden = true
    }

    func popToRootViewController(animated: Bool) {
        while stackView.arrangedSubviews.count > 1 {
            stackView.arrangedSubviews.last?.removeFromSuperview()
            viewControllers.removeLast()
        }
        stackView.arrangedSubviews.first?.isHidden = false
    }

}
_

私の物語:

  1. ルートビューとしてWKWebView(1)を含むUINavigationControllerがあります
  2. WKWebView(1)には、_target: _blank_パラメーターが設定されたハイパーリンクがあります
  3. リンクをタップした後、WKUIDelegatewebView(_:didRequestNewWebView:)をキャッチし、別のWKWebView(2)UINavigationControllerにプッシュします。
  4. WKWebView(2)はサーバーへのリクエストを実行します
  5. サーバーはJavaScriptメッセージでWKWebView(1)に応答します
  6. WKWebView(1)が停止しているため、メッセージが表示されません
0
Mateusz