Korzystanie z WKWebView w systemie iOS

Poniższe przykłady przedstawiają podzbiór dostępnych opcji. Te przykłady należy traktować jako minimalne, ale wystarczające rozwiązania w danych okolicznościach. WKWebView użycie w tych przykładach jest ograniczone do obsługi procesu Google Pay i nie obejmuje żadnych dodatkowych funkcji opartych na WebView. Wdrażając w projekcie dowolne z tych rozwiązań, kieruj się własnym osądem i dostosuj je do konkretnych potrzeb projektu.

Wyświetlanie wyskakującego okienka w kontrolerze tego samego widoku

Ten przykład w języku Swift pokazuje, jak nowy komponent WebView (wyskakujący) może być wyświetlany w tym samym UIViewController, który obejmuje nadrzędny komponent WebView. Aby utworzyć i wyświetlić wyskakujące okienko, zaimplementuj tę metodę z protokołu WKUIDelegate:

/// Creates a new WebView for displaying a popup
func webView(
    _ webView: WKWebView,
    createWebViewWith configuration: WKWebViewConfiguration,
    for navigationAction: WKNavigationAction,
    windowFeatures: WKWindowFeatures) -> WKWebView? {

  // If the target of the navigation is a new window (popup), this property is nil
  guard navigationAction.targetFrame == nil else { return nil }

  // Creates a new WebView for a popup, and displays it in the same view controller (on the top of `mainWebView`)
  let popupWebView = WKWebView(frame: .zero, configuration: configuration)
  addWebView(popupWebView)
  return popupWebView
}
  

Kolejna metoda z tego delegata, która usuwa wyskakujące okienko z ekranu:

/// Removes the WebView from the view hierarchy and updates the UI as needed (after popup was closed)
func webViewDidClose(_ webView: WKWebView) {
  /// Keeping the `mainWebView` on screen, and removing only popups
  guard webView != mainWebView else { return }

  webView.removeFromSuperview()
}
  

Funkcja addWebView(_ webView: WKWebView) ustawia też wartość uiDelegate dla przekazanego parametru webView. Oto przykładowa implementacja:

/// Adds a WebView into the view hierarchy
/// Same method is being used for displaying both `mainWebView` and any popups
func addWebView(_ webView: WKWebView) {
  webView.uiDelegate = self
  webView.translatesAutoresizingMaskIntoConstraints = false

  view.addSubview(webView)
  NSLayoutConstraint.activate([
    webView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
    webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
    webView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
    webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
  ])
}
  

W przypadku implementacji z jednym kontrolerem widoku należy dodać przycisk „Zamknij”, aby umożliwić użytkownikom zamykanie wyskakujących okienek (np. strony „Warunki usługi”). Ten przycisk może być umieszczony w różnych miejscach w zależności od interfejsu aplikacji. W tym przykładzie przycisk jest dodawany bezpośrednio do widoku WebView:

/// Adds a "Close" button overlay to the top-trailing corner of a given WebView
func addCloseButton(to webView: WKWebView) {
  let buttonSize = 24.0
  let buttonMargin = 6.0

  let closeAction = UIAction { [weak self] _ in
    self?.webViewDidClose(webView)
  }

  let closeButton = UIButton(type: .system, primaryAction: closeAction)
  closeButton.translatesAutoresizingMaskIntoConstraints = false

  var config = UIButton.Configuration.filled()
  config.baseBackgroundColor = .systemFill.withAlphaComponent(0.5)
  config.baseForegroundColor = .systemBackground
  config.buttonSize = .medium
  config.cornerStyle = .capsule
  config.image = UIImage(systemName: "xmark", withConfiguration: UIImage.SymbolConfiguration(pointSize: buttonSize / 2, weight: .bold))
  closeButton.configuration = config

  webView.addSubview(closeButton)
  NSLayoutConstraint.activate([
    closeButton.topAnchor.constraint(equalTo: webView.safeAreaLayoutGuide.topAnchor, constant: buttonMargin),
    closeButton.trailingAnchor.constraint(equalTo: webView.safeAreaLayoutGuide.trailingAnchor, constant: -buttonMargin),
    closeButton.widthAnchor.constraint(equalToConstant: buttonSize),
    closeButton.heightAnchor.constraint(equalToConstant: buttonSize)
  ])
}
  

Wyświetlanie wyskakującego okienka w nowym kontrolerze widoku

Ten przykład w języku Objective-C pokazuje, jak można wyświetlić nowy komponent WebView (wyskakujący) w nowym kontrolerze widoku (którego można użyć, jeśli aplikacja obsługuje karty). Jedną z opcji jest to, że kontroler widoku może mieć inicjator, który akceptuje zewnętrzną instancję WKWebView:

/// Creates a view controller with a given WebView
- (instancetype)initWithWebView:(WKWebView *)webView {
  if (self = [super init]) {
    self.webView = webView;
  }
  return self;
}
  

Aby utworzyć i wyświetlić wyskakujące okienko, zaimplementuj tę metodę z protokołu WKUIDelegate:

/// Creates a new WebView for displaying a popup
- (nullable WKWebView *)webView:(WKWebView *)webView
    createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
               forNavigationAction:(WKNavigationAction *)navigationAction
                    windowFeatures:(WKWindowFeatures *)windowFeatures {

  // If the target of the navigation is a new window (popup), this property is nil
  if (navigationAction.targetFrame != nil) { return nil; }

  // Create a new WebView for a popup
  WKWebView *popupWebView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];

  // Create a new instance of `NewViewController` with a newly created WebView, and present it modally
  NewViewController *popupViewController = [[NewViewController alloc] initWithWebView:popupWebView];
  [self presentViewController:popupViewController animated:YES completion:nil];

  return popupWebView;
}
  

Kolejna metoda z tego delegata, która usuwa wyskakujące okienko z ekranu:

/// Dismisses the current view controller (after popup was closed)
- (void)webViewDidClose:(WKWebView *)webView {
  [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
  

Wyświetlanie wyskakującego okienka za pomocą SwiftUI

Ten przykład w SwiftUI pokazuje, jak nowy komponent WebView (wyskakujący) może być wyświetlany w tym samym widoku Swift, który obejmuje nadrzędny komponent WebView. Ponieważ WKWebView jest komponentem UIKit, opakuj go w UIViewRepresentable:

/// A SwiftUI wrapper for `WKWebView`
struct WebView: UIViewRepresentable {

  /// Underlying WebView
  let wkWebView = WKWebView()

  /// Loads a given `url` into the underlying WebView
  func load(url: URL) -> Self {
    wkWebView.load(URLRequest(url: url))
    return self
  }

  /// Creates the view object representing `WKWebView` and configures its initial state
  func makeUIView(context: Context) -> some UIView {
    wkWebView.uiDelegate = context.coordinator
    return wkWebView
  }

  /// When the state of the app changes, SwiftUI calls this method for any changes affecting
  /// the corresponding UIKit viewAs an alternative, this method could be used to load the `url`
  /// into the WebView instead of calling `load(url:)` explicitly
  func updateUIView(_ uiView: UIViewType, context: Context) { }

  /// Creates a bridge between SwiftUI and `WKUIDelegate`
  func makeCoordinator() -> WKUIDelegate {

    /// A coordinator capable of handling `WKUIDelegate` events
    final class Coordinator: NSObject, WKUIDelegate {

      /// Main WebView which displays the `url` passed into `SwiftUISurface`
      let mainWebView: WebView

      /// Creates a coordinator with a given main WebView
      init(_ mainWebView: WebView) {
        self.mainWebView = mainWebView
      }

      /// Creates a new WebView for displaying a popup
      func webView(
        _ webView: WKWebView,
        createWebViewWith configuration: WKWebViewConfiguration,
        for navigationAction: WKNavigationAction,
        windowFeatures: WKWindowFeatures) -> WKWebView? {

        // If the target of the navigation is a new window (popup), this property is nil
        guard navigationAction.targetFrame == nil else { return nil }

        // Creates a new WebView for a popup which would use the same coordinator
        let popupWebView = WKWebView(frame: webView.frame, configuration: configuration)
        popupWebView.translatesAutoresizingMaskIntoConstraints = false
        popupWebView.uiDelegate = webView.uiDelegate

        /// Displays the newly created popup WebView on the top of a parent WebView (`mainWebView`)
        webView.addSubview(popupWebView)
        NSLayoutConstraint.activate([
          popupWebView.leadingAnchor.constraint(equalTo: webView.leadingAnchor),
          popupWebView.topAnchor.constraint(equalTo: webView.topAnchor),
          popupWebView.trailingAnchor.constraint(equalTo: webView.trailingAnchor),
          popupWebView.bottomAnchor.constraint(equalTo: webView.bottomAnchor),
        ])

        return popupWebView
      }

      /// Removes the WebView from the view hierarchy and updates the UI as needed (after popup was closed)
      func webViewDidClose(_ webView: WKWebView) {
        /// Keeping the `mainWebView` on screen, and removing only popups
        guard webView != mainWebView.wkWebView else { return }

        webView.removeFromSuperview()
      }
    }

    return Coordinator(self)
  }
}
  

Fragment kodu zawiera zarówno UIViewRepresentable otokę dla elementu WebView, jak i niestandardową klasę Coordinator, która może obsługiwać zdarzenia delegowane elementu WebView. Gdy pojawi się żądanie wyskakującego okienka, tworzy je i umieszcza na wierzchu nadrzędnego widoku WebView. W przypadku zdarzenia zamknięcia wyskakującego okienka – usuwa je z hierarchii widoków.