Search code examples
iosswiftswiftuiwebviewwkwebview

SwiftUI open a new View on link click inside of WKWebView


I would like to open a new view in SwfitUI (webview) after clicking on a link inside of WKWebView, I do not know how to do. I understand that you have to go through Coordinator, but I don't know more. I don't have a very high level in swift.

Here is my Swiftui view (I removed superfluous elements) and al calsse to display my content in my view :

struct PostDetailView: View {

    var p: Post
    @ObservedObject var dataManager: DataManager



    let preferences:Preferences = parsePreferences()
    let contentWebView = ArticleCustomView()
    @State var showComments = false
    @Binding var favorite: Bool



    @Environment(\.colorScheme) var colorScheme


    var body: some View {
    
        let content: String =  contentWebView.contentArticle(title: p.title.rendered.stringByDecodingHTMLEntities, content: p.content.rendered, date: formatDateDetailPost(dateToChange: p.date), author: p.author_meta.nickname)
        
        
        ZStack{
        
            BackgroundView()
            
            articleWebView(text: content, color: colorScheme)
                
         
        }
        .navigationBarTitle(...)
        .navigationBarItems(...)

    }
}

x

struct articleWebView: UIViewRepresentable  {
    @State var text: String
    @State var color: ColorScheme
    
    func makeUIView(context: Context) -> WKWebView {
    
         return _wkwebview
        
    }
    

    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
        
        var html = ""
        var normalizeCSS: String = ""
        var articlesCSS: String = ""
        //let articlesDarkCSS: String
        let altCSS: String
        

        altCSS = "-dark" 
        
        guard let fileURL = Bundle.main.url(forResource: "articles", withExtension: "html") else { return print("articles.html introuvable")}
        guard let fileURLNormalizeCss = Bundle.main.url(forResource: "normalize", withExtension: "css") else { return print("normalize.css introuvable")}
        guard let fileURLArticlesCss = Bundle.main.url(forResource: "articles\(altCSS)", withExtension: "css") else { return print("articles\(altCSS).css introuvable")}
        
        do {
            html = try String(contentsOf: fileURL)
        } catch  {
            print("Unable to get the articles.html")
        }
        
        do {
            normalizeCSS = try String(contentsOf: fileURLNormalizeCss)
        } catch  {
            print("Unable to get the normalize.css")
        }
        
        do {
            articlesCSS = try String(contentsOf: fileURLArticlesCss)
        } catch  {
            print("Unable to get the articles.css")
        }
        
        html = html.replacingOccurrences(of: "[article:content]", with: text)
        html = html.replacingOccurrences(of: "[normalize:CSS]", with: normalizeCSS)
        html = html.replacingOccurrences(of: "[style:articles]", with: articlesCSS)
        
        
        uiView.loadHTMLString(html, baseURL: nil)
        
        
        
        
        
        //----------------------------------------------//
        uiView.allowsBackForwardNavigationGestures = true
        uiView.isOpaque = false
        uiView.backgroundColor = .clear
        uiView.scrollView.backgroundColor = UIColor.clear
        uiView.scrollView.showsVerticalScrollIndicator = false
        uiView.scrollView.pinchGestureRecognizer?.isEnabled = false 

        
        
              
        
        
        
    }
 }   

Thank you in advance.


Solution

  • I finally found a solution to my problem. You have to use the Coordinator to know when there is a click on a link and to retrieve the link.

    Using Binding I transfer the url to the view and I open a sheetView and inside I have my my webview which opens the url

    struct PostDetailView: View {
    
    var p: Post
    @ObservedObject var dataManager: DataManager
    
    
    
    let preferences:Preferences = parsePreferences()
    let contentWebView = ArticleCustomView()
    @State var showComments = false
    @Binding var favorite: Bool
    
    @State var activeSheet: Bool = false
    @State var urlToOpenInSheet: String = ""
    
    @Environment(\.colorScheme) var colorScheme
    
    
    var body: some View {
    
        let content: String =  contentWebView.contentArticle(title: p.title.rendered.stringByDecodingHTMLEntities, content: p.content.rendered, date: formatDateDetailPost(dateToChange: p.date), author: p.author_meta.nickname)
        
        
        ZStack{
        
            BackgroundView()
            
            articleWebView(text: content, color: colorScheme, activeSheet: $activeSheet, urlToOpenInSheet: $urlToOpenInSheet)
                
         
        }
        .navigationBarTitle(...)
        .navigationBarItems(...)
        .sheet(isPresented: $activeSheet) {
                if !urlToOpenInSheet.isEmpty {
                    ExternalWebView(urlToVisite: urlToOpenInSheet, activeButtons: true, fromSheet: true, closeSheet: $activeSheet)
                        .ignoresSafeArea()
                        .accentColor(.white)
                }
            }
        .onChange(of: urlToOpenInSheet) { newValue in
                print("url changed to \(urlToOpenInSheet)!")
            }
    
        }
    }
    
    
    
    
    struct articleWebView: UIViewRepresentable  {
    @State var text: String
    @State var color: ColorScheme
    @Binding var activeSheet: Bool
    @Binding var urlToOpenInSheet: String
    
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> WKWebView {
        
        let WKWebView = WKWebView()
        WKWebView.navigationDelegate = context.coordinator
        return WKWebView
    }
    
    
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
        var html = ""
        var normalizeCSS: String = ""
        var articlesCSS: String = ""
        //let articlesDarkCSS: String
        let altCSS: String
        
    
        
        guard let fileURL = Bundle.main.url(forResource: "articles", withExtension: "html") else { return print("articles.html introuvable")}
        guard let fileURLNormalizeCss = Bundle.main.url(forResource: "normalize", withExtension: "css") else { return print("normalize.css introuvable")}
        guard let fileURLArticlesCss = Bundle.main.url(forResource: "articles\(altCSS)", withExtension: "css") else { return print("articles\(altCSS).css introuvable")}
        
        do {
            html = try String(contentsOf: fileURL)
        } catch  {
            print("Unable to get the articles.html")
        }
        
        do {
            normalizeCSS = try String(contentsOf: fileURLNormalizeCss)
        } catch  {
            print("Unable to get the normalize.css")
        }
        
        do {
            articlesCSS = try String(contentsOf: fileURLArticlesCss)
        } catch  {
            print("Unable to get the articles.css")
        }
        
        html = html.replacingOccurrences(of: "[article:content]", with: text)
        html = html.replacingOccurrences(of: "[normalize:CSS]", with: normalizeCSS)
        html = html.replacingOccurrences(of: "[style:articles]", with: articlesCSS)
        
        //print(html)
        uiView.loadHTMLString(html, baseURL: nil)
        
        
        
        
        
        //---------------------- Activer /Desactiver certaines options sur la Webview -------------------------//
        uiView.allowsBackForwardNavigationGestures = true
        uiView.isOpaque = false
        uiView.backgroundColor = .clear
        uiView.scrollView.backgroundColor = UIColor.clear
        uiView.scrollView.showsVerticalScrollIndicator = false // indicateur de scroll
        uiView.scrollView.pinchGestureRecognizer?.isEnabled = false // zommer-dezommer avec le doigt
        
        
    }
    
    class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate {
        var parent: articleWebView
        
    
        init(_ parent: articleWebView) {
            self.parent = parent
        }
        
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            
            
            if navigationAction.navigationType == .linkActivated  {
                if let url = navigationAction.request.url {
                    
                    if UIApplication.shared.canOpenURL(url) {
                        
                        //UIApplication.shared.open(url)
                        print(url)
                        parent.urlToOpenInSheet = url.absoluteString
                        print(self.parent.urlToOpenInSheet)
                        parent.activeSheet = true
                        
                       
                        decisionHandler(.cancel)
                    } else {
                        decisionHandler(.allow)
                    }
                }
            } else {
                //print("not a user click")
                decisionHandler(.allow)
            }
        }
        
        
    }
    
    
    
    }