Essayez d'afficher une vue Web légèrement riche dans SwiftUI en utilisant UIViewRepresentable

introduction

Depuis iOS14, SwiftUI ne prend actuellement pas en charge View équivalent à WKWebView. Par conséquent, il est nécessaire d'encapsuler et d'utiliser le WKWebView d'UIKit pour SwiftUI. Cette fois, le but est d'afficher une WebView équipée de fonctions de base (barre d'outils, barre de progression), et je voudrais résumer comment utiliser ʻUIViewRepresentable` qui y apparaît.

À propos de UIViewRepresentable

Pour utiliser la vue d'UIKit avec SwiftUI, vous devez utiliser ʻUIViewRepresentable. ʻUIViewRepresentable est un wrapper pour utiliser UIKit View dans SwiftUI. Décrit chaque fonction définie dans le protocole ʻUIViewRepresentable`.

func makeUIView(context: Self.Context) -> Self.UIViewType

Doit être implémenté. Créez une instance de la vue à afficher. La vue UIKit utilisée dans SwiftUI est renvoyée comme valeur de retour.

func updateUIView(Self.UIViewType, context: Self.Context)

Doit être implémenté. Appelé lorsque l'état de l'application est mis à jour. Si la vue est mise à jour, décrivez-la dans cette fonction.

static func dismantleUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator)

Appelé lorsque la vue UIKit spécifiée est supprimée. Si nécessaire, décrivez le processus de nettoyage tel que la suppression de la notification enregistrée dans cette fonction.

func makeCoordinator() -> Self.Coordinator

Implémenter lorsqu'un événement doit être notifié du côté de la vue En définissant Coordinator, il devient possible d'effectuer la gestion des événements par des opérations utilisateur telles que Delegate.

Créer une WebView à l'aide de WKWebView

Passons maintenant au sujet principal de l'affichage WKWebView.

Afficher WKWebView

La première consiste simplement à afficher la WebView dans l'interface utilisateur de Swift. ʻUIViewType devient ʻassociatedtype, alors changez-le en type de vue de l'UIKit que vous voulez encapsuler. Cette fois, j'utiliserai WKWebView.

WebView.swift


struct WebView: UIViewRepresentable {
    ///Afficher à afficher
    private let webView = WKWebView()
    ///URL à afficher
    let url: URL

    func makeUIView(context: Context) -> WKWebView {
        //La valeur de retour est WKWebView et elle est renvoyée.
        webView.load(URLRequest(url: url))
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) { }
}

Créer une barre d'outils

Ensuite, nous allons créer une barre d'outils avec trois éléments: back`` forward`` reload.

Contrôle des actions en appuyant sur un bouton

Tout d'abord, créez une barre d'outils et placez chaque bouton. Ensuite, utilisez Property Wrappers pour permettre à WebView de détecter quand chaque bouton est appuyé sur la barre d'outils. Si vous appuyez sur le bouton de la barre d'outils dans cet état, l'état de l'application sera mis à jour et ʻupdateUIView (_ uiView:, context:) de ʻUIViewRepresentable se déclenchera. De ce qui précède, du côté WebView, décrivez l'action en fonction du bouton dans ʻupdateUIView (_ uiView:, context:) `.

WebView.swift


struct WebView: UIViewRepresentable {
    //réduction...

    ///Actions WebView
    enum Action {
        case none
        case goBack
        case goForward
        case reload
    }

    ///action
    @Binding var action: Action

    func updateUIView(_ uiView: WKWebView, context: Context) {
        ///Appelé chaque fois que la valeur liée est mise à jour
        ///Lorsque l'action est mise à jour, traiter selon la valeur mise à jour
        switch action {
        case .goBack:
            uiView.goBack()
        case .goForward:
            uiView.goForward()
        case .reload:
            uiView.reload()
        case .none:
            break
        }
        action = .none
    }
}

WebToolBarView.swift


struct WebToolBarView: View {
    ///action
    @Binding var action: WebView.Action

    var body: some View {
        VStack() {
            HStack() {
                //Mettre à jour les actions en fonction du bouton sur lequel vous appuyez
                Button("Back") { action = .goBack }
                Button("Forward") { action = .goForward }
                Button("Reload") { action = .reload }
            }
        }
    }
}

RichWebView.swift


struct RichWebView: View {
    /// URL
    let url: URL
    ///action
    @State private var action: WebView.Action = .none

    var body: some View {
        VStack() {
            WebView(url: url,
                    action: $action)
            WebToolBarView(action: $action)
        }
    }
}

Désactivation des boutons

Il est maintenant possible de traiter lorsque le bouton est appuyé dans WebView. Cependant, les boutons peuvent être tapés dans n'importe quel état, donc si vous ne pouvez pas passer à la page précédente ou suivante, désactivez chaque bouton.

** Définir le coordinateur ** Si vous pouvez ou non passer à la page précédente ou suivante est déterminé lorsque le chargement de la page WebView est terminé. Pour ce faire, vous devez implémenter WKNavigationDelegate, mais vous ne pouvez pas l'implémenter directement dans WebView. Donc, avec ʻUIViewRepresentable, vous devez créer un ** Coordinator, une instance personnalisée ** pour informer l'interface utilisateur Swift des changements reçus de la ** vue UIKit. WKNavigationDelegate est implémenté dans Coordinator, à travers lequel la gestion des événements du côté de l'interface utilisateur Swift est effectuée.

WebView.swift


struct WebView: UIViewRepresentable {
    //réduction... 

    ///Puis-je revenir en arrière
    @Binding var canGoBack: Bool
    ///Voulez-vous poursuivre
    @Binding var canGoForward: Bool

    func makeCoordinator() -> WebView.Coordinator {
        return Coordinator(parent: self)
    }
}

extension WebView {
    final class Coordinator: NSObject, WKNavigationDelegate {
        ///Vue parent
        let parent: WebView

        init(parent: WebView) {
            self.parent = parent
        }

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            parent.canGoBack = webView.canGoBack
            parent.canGoForward = webView.canGoForward
        }
    }
}

Après cela, recevez chaque valeur avec WebToolBarView via RichWebView, et décrivez le traitement d'inactivité et d'activation de chaque bouton, et vous avez terminé.

WebToolBarView.swift


    Button("Back") { action = .goBack }.disabled(!canGoBack)
    Button("Forward") { action = .goForward }.disabled(!canGoForward)

Créer une barre de progression

Enfin, créons une barre de progression.

Obtenez la valeur avec KVO

Vous devez obtenir la «progression estimée» et «est en cours de chargement» de «WKWebView» pour créer une barre de progression. Cette fois, nous utiliserons KVO pour chacun. Vous recevrez une notification de modification de View, utilisez donc le Coordinator que vous avez utilisé précédemment.

WebView.swift


struct WebView: UIViewRepresentable {
    //réduction... 

     ///Progression du chargement
    @Binding var estimatedProgress: Double
    ///Que ce soit le chargement
    @Binding var isLoading: Bool

    static func dismantleUIView(_ uiView: WKWebView, coordinator: Coordinator) {
        //Appelé lorsque WKWebView est supprimé
        //Désactiver et supprimer les notifications lorsque l'instance est supprimée
        coordinator.observations.forEach({ $0.invalidate() })
        coordinator.observations.removeAll()
    }
}

extension WebView {
    final class Coordinator: NSObject, WKNavigationDelegate {
        ///Vue parent
        let parent: WebView
        /// NSKeyValueObservations
        var observations: [NSKeyValueObservation] = []

        init(parent: WebView) {
            self.parent = parent
            //Enregistrer les notifications
            let progressObservation = parent.webView.observe(\.estimatedProgress, options: .new, changeHandler: { _, value in
                parent.estimatedProgress = value.newValue ?? 0
            })
            let isLoadingObservation = parent.webView.observe(\.isLoading, options: .new, changeHandler: { _, value in
                parent.isLoading = value.newValue ?? false
            })
            observations = [
                progressObservation,
                isLoadingObservation
            ]
        }
        //réduction...
    }
}
```

#### Créer une barre de progression
 Vous avez maintenant toutes les pièces dont vous avez besoin pour votre barre de progression.
 Tout ce que vous avez à faire est de créer `ProgressBarView.swift`, de recevoir la valeur via` RichWebView` et de l'afficher.


#### **`ProgressBarView.swift`**
```swift

struct ProgressBarView: View {
    ///Progression du chargement
    var estimatedProgress: Double

    var body: some View {
        VStack {
            GeometryReader { geometry in
                Rectangle()
                    .foregroundColor(Color.gray)
                    .opacity(0.3)
                    .frame(width: geometry.size.width)
                Rectangle()
                    .foregroundColor(Color.blue)
                    .frame(width: geometry.size.width * CGFloat(estimatedProgress))
            }
        }.frame(height: 3.0)
    }
}

RichWebView.swift


struct RichWebView: View {
    //réduction...

    ///Progression du chargement
    @State private var estimatedProgress: Double = 0.0
    ///Que ce soit le chargement
    @State private var isLoading: Bool = false

    var body: some View {
        VStack() {
            if isLoading {
                ProgressBarView(estimatedProgress: estimatedProgress)
            }
            //réduction...
        }
    }
}

Vous avez maintenant implémenté un ensemble de fonctions. Il y a d'autres points à considérer comme la gestion des erreurs, mais je pense que nous avons pu créer une WebView avec des fonctions approximatives.

en conclusion

Cette fois, je l'ai implémenté dans le but d'afficher une WebView riche, mais pour implémenter WebView avec SwiftUI, il est nécessaire de le créer en utilisant les fonctions de base de ʻUIViewRepresentable`, donc c'est juste pour étudier. pense. Si vous êtes intéressé, essayez-le. La source est résumée dans le github suivant, donc si vous êtes intéressé, je vous serais reconnaissant si vous pouviez le voir. (Le côté github contient du code omis ici, comme l'affichage du titre de la barre de navigation et les restrictions de mise en page.) RichWebViewSample

De plus, si vous avez des suggestions d'amélioration ou des opinions concernant cette implémentation, nous vous serions reconnaissants de bien vouloir commenter. Merci beaucoup.

Références

Recommended Posts

Essayez d'afficher une vue Web légèrement riche dans SwiftUI en utilisant UIViewRepresentable
Créez un environnement de fonctionnement automatique du navigateur en 5 minutes à l'aide de Selenide
Essayez de créer un babillard en Java
Comment réparer un crash lors de la suppression de données de royaume dans la liste de l'interface utilisateur Swift
Essayez d'afficher le nombre premier en utilisant le type booléen
[Swift] Comment afficher les caractères saisis sur le Widget via UserDefaults lors de l'utilisation du WidgetKit
[Swift] Enregistrer la couleur aux valeurs par défaut de l'utilisateur avec l'interface utilisateur Swift (Remarque)
Comment afficher un aperçu du navigateur avec VS Code
Comment convertir A en A et A en A en utilisant le produit logique et la somme en Java
Essayez de résoudre un problème FizzBuzz restreint en Java
Essayez de créer un environnement de développement Java à l'aide de Docker
Je veux trouver un chemin relatif dans une situation où Path est utilisé
Comment afficher des graphiques dans Ruby on Rails (LazyHighChart)
Afficher la définition d'API dans l'interface utilisateur Swagger à l'aide de Docker + Rails6 + apipie
Comment changer la couleur d'arrière-plan de la barre de navigation, etc. dans Swift UI
Essayez d'utiliser RocksDB avec Java
Photothèque avec Swift UI