Skip to content

Instantly share code, notes, and snippets.

@hasanalisiseci
Last active March 14, 2025 23:27
Show Gist options
  • Save hasanalisiseci/e4822135b4402d0eb16c26a21246dba5 to your computer and use it in GitHub Desktop.
Save hasanalisiseci/e4822135b4402d0eb16c26a21246dba5 to your computer and use it in GitHub Desktop.
A view derived from WKWebView that returns a dynamically calculated height value based on HTML content using the UIViewRepresentable protocol in SwiftUI.
//
// WebViewHTMLContent.swift
// WebViewHTMLContent
//
// Created by Hasan Ali Şişeci on 20.06.2023.
//
import WebKit
import SwiftUI
struct WebViewHTMLContent: UIViewRepresentable {
@Binding var dynamicHeight: CGFloat
let htmlString: String
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.navigationDelegate = context.coordinator
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
let htmlStart = """
<html>
<head>
<meta name=\"viewport\" content=\"width=device-width,minimum-scale=1.0, maximum-scale=1.0\" />
<style>
@font-face {
font-family: 'NotoSans';
font-weight: normal;
src: url("NotoSans-Regular.ttf") format('truetype');
}
@font-face {
font-family: "NotoSans";
font-weight: bold;
src: url("NotoSans-Bold.ttf")
}
* {
font-family: 'NotoSans';
font-size: 14;
margin: 0;
padding: 0;
}
img {
display: inline;
height:auto;
max-width: 100%;
}
</style>
</head>
<body>
"""
let htmlEnd = "</body></html>"
uiView.loadHTMLString(htmlStart + htmlString + htmlEnd, baseURL: Bundle.main.bundleURL)
}
class Coordinator: NSObject, WKNavigationDelegate {
var parent: WebViewHTMLContent
init(_ parent: WebViewHTMLContent) {
self.parent = parent
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if webView.isLoading == false {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
if complete != nil {
webView.evaluateJavaScript("document.documentElement.scrollHeight", completionHandler: { (height, error) in
if error == nil {
guard let height = height as? CGFloat else { return }
webView.frame.size.height = height
self.parent.dynamicHeight = height
}
})
}
webView.sizeToFit()
})
}
}
}
}
}
//Example Usage
struct ExampleWebView: View {
@State var height: CGFloat = .zero
let htmlContent = "<h1>My First Heading</h1><p>My first paragraph.</p>"
var body: some View {
VStack {
WebViewHTML(dynamicHeight: $height, htmlString: htmlContent)
.frame(height: height)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment