使用 iOS WKWebView

以下範例僅列出部分可用選項。這些範例應視為特定情況下的最低可行解決方案。 WKWebView這些範例的使用範圍僅限於支援 Google Pay 流程,不包含任何其他由 WebView 支援的功能。 在專案中導入任何提供的解決方案時,請自行判斷並進行專案專屬的自訂作業。

在同一個檢視畫面控制器中顯示彈出式視窗

這個 Swift 範例示範如何在涵蓋父項 WebView 的相同 UIViewController 中,顯示新的 WebView (適用於彈出式視窗)。 如要建立及顯示彈出式視窗,請實作 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
}
  

此外,這個委派項目還有一個方法,可從畫面上移除彈出式視窗:

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

函式 addWebView(_ webView: WKWebView) 也會為傳遞的 webView 設定 uiDelegate。以下是導入範例:

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

如果是單一檢視畫面控制器實作,則應加入「關閉」按鈕,讓使用者可以關閉彈出式視窗 (例如「服務條款」頁面)。這個按鈕的位置會因應用程式的 UI 而異。在下列範例中,按鈕會直接新增至 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)
  ])
}
  

在新檢視畫面控制器中顯示彈出式視窗

這個 Objective-C 範例說明如何在新的檢視區塊控制器中顯示新的 WebView (適用於彈出式視窗),如果您的應用程式支援分頁,則可使用這個控制器。 其中一個選項是讓這個檢視畫面控制器擁有可接受外部 WKWebView 例項的初始化器:

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

如要建立及顯示彈出式視窗,請實作 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;
}
  

此外,這個委派項目還有一個方法,可從畫面上移除彈出式視窗:

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

使用 SwiftUI 顯示彈出式視窗

這個 SwiftUI 範例示範如何在涵蓋父項 WebView 的相同 Swift 檢視區塊中,顯示新的 WebView (適用於彈出式視窗)。 由於 WKWebViewUIKit 元件,請將其包裝在 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)
  }
}
  

程式碼片段包含 WebView 的 UIViewRepresentable 包裝函式,以及可處理 WebView 委派事件的自訂 Coordinator 類別。當系統要求彈出式視窗時,會建立該視窗,並將其置於父項 WebView 的頂端。在彈出式視窗關閉事件中,系統會從檢視區塊階層中移除該事件。