Приведённые ниже примеры представляют собой лишь часть доступных вариантов. Эти примеры следует рассматривать как минимально приемлемые решения для данных обстоятельств. Использование 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) также устанавливает uiDelegate для переданного webView . Вот пример реализации:
/// 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) ]) }
Для реализаций контроллера с одним представлением следует добавить кнопку «Закрыть», позволяющую пользователям закрывать всплывающие окна (например, страницу «Условия обслуживания»). Эту кнопку можно разместить в разных местах в зависимости от пользовательского интерфейса приложения. В следующем примере кнопка добавляется непосредственно в 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 View, охватывающем родительский WebView. Поскольку WKWebView — это компонент UIKit , оберните его в 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) } }
Фрагмент кода включает в себя как обёртку UIViewRepresentable для WebView, так и пользовательский класс Coordinator , способный обрабатывать события делегата WebView. При запросе всплывающего окна он создаёт его и размещает поверх родительского WebView. При закрытии всплывающего окна оно удаляется из иерархии представлений.