Usar o WKWebView do iOS

Os exemplos a seguir representam um subconjunto das opções disponíveis. Esses exemplos devem ser considerados como soluções mínimas viáveis para determinadas circunstâncias. O uso de WKWebView nesses exemplos é limitado ao suporte do fluxo do Google Pay e não inclui outros recursos com tecnologia WebView. Use seu próprio julgamento e personalizações específicas do projeto ao implementar qualquer uma das soluções fornecidas.

Mostrar um pop-up no mesmo controlador de visualização

Este exemplo em Swift demonstra como uma nova WebView (para um pop-up) pode ser mostrada no mesmo UIViewController, cobrindo uma WebView principal. Para criar e mostrar um pop-up, implemente o seguinte método de 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
}
  

Além disso, mais um método desse delegado que remove um pop-up da tela:

/// 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()
}
  

A função addWebView(_ webView: WKWebView) também está definindo o uiDelegate para um webView transmitido. Confira um exemplo de implementação:

/// 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)
  ])
}
  

Para implementações de controlador de visualização única, um botão "Fechar" precisa ser adicionado para permitir que os usuários dispensem pop-ups (por exemplo, uma página de "Termos de Serviço"). Esse botão pode ser colocado em vários locais, dependendo da interface do app. No exemplo a seguir, o botão é adicionado diretamente à 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)
  ])
}
  

Mostrar um pop-up em um novo controlador de visualização

Este exemplo em Objective-C demonstra como uma nova WebView (para um pop-up) pode ser mostrada em um novo controlador de visualização (que pode ser usado se o app for compatível com guias). Como uma das opções, esse controlador de visualização pode ter um inicializador que aceita uma instância WKWebView externa:

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

Para criar e mostrar um pop-up, implemente o seguinte método de 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;
}
  

Além disso, mais um método desse delegado que remove um pop-up da tela:

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

Mostrar um pop-up usando o SwiftUI

Este exemplo do SwiftUI demonstra como uma nova WebView (para um pop-up) pode ser mostrada na mesma visualização do Swift, cobrindo uma WebView principal. Como WKWebView é um componente do UIKit, envolva-o em 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)
  }
}
  

O snippet de código inclui o wrapper UIViewRepresentable para uma WebView e uma classe Coordinator personalizada capaz de processar eventos delegados da WebView. Quando um pop-up é solicitado, ele é criado e colocado na parte superior de uma WebView principal. Em um evento de fechamento de pop-up, ele é removido da hierarquia de visualizações.