Search code examples
swiftuiswiftui-tabviewswiftui-scrollview

Embedding webview within a tabview in SwiftUI


I've been trying to create a TabView with PageIndexViewStyle where each page is a ScrollView that contains an image and a WebView. The example code here from @Asperi works great when I'm creating a page outside of the TabView, but when I move things inside the TabView, the WebView on the second page isn't displayed.

enter image description here

import SwiftUI
import WebKit

struct Webview : UIViewRepresentable {
    @Binding var dynamicHeight: CGFloat
    var webview: WKWebView = WKWebView()
    
    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: Webview
        
        init(_ parent: Webview) {
            self.parent = parent
        }
        
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            webView.evaluateJavaScript("document.documentElement.scrollHeight", completionHandler: { (height, error) in
                DispatchQueue.main.async {
                    self.parent.dynamicHeight = height as! CGFloat
                }
            })
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> WKWebView  {
        webview.scrollView.bounces = false
        webview.navigationDelegate = context.coordinator
        let htmlStart = "<HTML><HEAD><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, shrink-to-fit=no\"></HEAD><BODY>"
        let htmlEnd = "</BODY></HTML>"
        let dummy_html = """
                        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ut venenatis risus. Fusce eget orci quis odio lobortis hendrerit. Vivamus in sollicitudin arcu. Integer nisi eros, hendrerit eget mollis et, fringilla et libero. Duis tempor interdum velit. Curabitur</p>
                        <p>ullamcorper, nulla nec elementum sagittis, diam odio tempus erat, at egestas nibh dui nec purus. Suspendisse at risus nibh. Mauris lacinia rutrum sapien non faucibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec interdum enim et augue suscipit, vitae mollis enim maximus.</p>
                        <p>Fusce et convallis ligula. Ut rutrum ipsum laoreet turpis sodales, nec gravida nisi molestie. Ut convallis aliquet metus, sit amet vestibulum risus dictum mattis. Sed nec leo vel mauris pharetra ornare quis non lorem. Aliquam sed justo</p>
                        """
        let htmlString = "\(htmlStart)\(dummy_html)\(htmlEnd)"
        webview.loadHTMLString(htmlString, baseURL:  nil)
        return webview
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
    }
}


struct ContentView: View {
    @State private var webViewHeight: CGFloat = .zero
    var body: some View {
        TabView {
            ScrollView {
                VStack {
                    Image(systemName: "doc")
                        .resizable()
                        .scaledToFit()
                        .frame(height: 100)
                    Divider()
                    Webview(dynamicHeight: $webViewHeight)
                        .padding(.horizontal)
                        .frame(height: webViewHeight)
                }
            }
            
            ScrollView {
                VStack {
                    Image(systemName: "star")
                        .resizable()
                        .scaledToFit()
                        .frame(height: 100)
                    Divider()
                    Webview(dynamicHeight: $webViewHeight)
                        .padding(.horizontal)
                        .frame(height: webViewHeight)
                }
            }
        }
        .tabViewStyle(PageTabViewStyle())
        .indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
    }
}

Solution

  • I can't exactly tell you what is causing that odd behaviour but setting the selection parameter of the TabView solved the problem. From my understanding, SwiftUI wanted us to provide a selection binding on our TabView.

    So, Changing

     TabView {
    

    to

    TabView(selection: $selectedtab) {
    // Or
    TabView(selection: .constant(1)) {
    

    Solved it.