SwiftUI WebView ( WKWebView ) 自动高度
SwiftUI WebView ( WKWebView ) autoHeight
我正在尝试使用自动高度创建 WebView。
我必须以与多行相同的方式显示一些内容 Text()
: 如果它的内容变大 - 它必须自动放大我的 WebView 的高度
我的代码是一组蹩脚的 hack,而且效果非常糟糕。
import SwiftUI
import WebKit
@available(OSX 11.0, *)
public struct WebView: View {
@Binding var html: String
public init (html: Binding<String> ){
_html = html
}
public var body: some View {
WebViewWrapper(html: html)
}
}
@available(OSX 11.0, *)
public struct WebViewWrapper: NSViewRepresentable {
let html: String
public func makeNSView(context: Context) -> WKWebView {
return WKWebView()
}
public func updateNSView(_ webView: WKWebView, context: Context) {
webView.loadHTMLString(html, baseURL: nil)
webView.actualateHeight()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
webView.actualateHeight()
}
}
}
fileprivate extension WKWebView {
func actualateHeight(){
self.setFrameHeight(1)
self.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
if complete != nil {
self.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
if let height = height {
self.setFrameHeight(height as! Float)
}
})
}
})
}
func setFrameHeight(_ height: Float) {
let size = NSSize(width: self.frame.width, height: CGFloat(height) )
self.setFrameSize(size)
self.setBoundsSize(size)
print("YYY \(height)")
}
}
用法:
struct PreferencesView : View {
var model : Config
@ObservedObject var splittedStatusWindow : ConfigProperty<Bool>
@State var text: String = "<html> <style>body{background-color:linen;} H1 {font-size: 50;} .testClass { color: red; } #hell{ color: green;}</style> <body><h1 class='testClass'>Hello World</h1><p id='hell'><strong>hell</strong> yeah!</p></body></html>"
init(model: Config) {
self.model = model
self.splittedStatusWindow = model.splitedStatusWindow
}
var body: some View {
Spacer()
TextField("", text: $text)
Divider()
WebView(html: $text)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
Divider()
Spacer()
}
}
代码问题:
- hacky 解决方案错误的大小自动更新
- 代码包装器的大小比预期的要大。总是。
例如:
但预期某事像:
import SwiftUI
import WebKit
@available(OSX 11.0, *)
public struct WebView: View {
@Binding var html: String
@State var dynamicHeight: CGFloat = 10
public init ( html: Binding<String> ) {
_html = html
}
public var body: some View {
WebViewWrapper(html: html, dynamicHeight: $dynamicHeight)
.frame(height: dynamicHeight)
}
}
@available(OSX 11.0, *)
public struct WebViewWrapper: NSViewRepresentable {
let html: String
@Binding var dynamicHeight: CGFloat
public func makeNSView(context: Context) -> NoScrollWKWebView {
let a = NoScrollWKWebView()
a.navigationDelegate = context.coordinator
return a
}
public func updateNSView(_ webView: NoScrollWKWebView, context: Context) {
webView.loadHTMLString(html, baseURL: nil)
}
public func makeCoordinator() -> Coordinator { Coordinator(self) }
}
@available(OSX 11.0, *)
public extension WebViewWrapper {
class Coordinator: NSObject, WKNavigationDelegate {
var parent: WebViewWrapper
public init(_ parent: WebViewWrapper) {
self.parent = parent
}
public 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
}
})
}
}
}
public class NoScrollWKWebView: WKWebView {
public override func scrollWheel(with theEvent: NSEvent) {
nextResponder?.scrollWheel(with: theEvent)
}
}
我正在尝试使用自动高度创建 WebView。
我必须以与多行相同的方式显示一些内容 Text()
: 如果它的内容变大 - 它必须自动放大我的 WebView 的高度
我的代码是一组蹩脚的 hack,而且效果非常糟糕。
import SwiftUI
import WebKit
@available(OSX 11.0, *)
public struct WebView: View {
@Binding var html: String
public init (html: Binding<String> ){
_html = html
}
public var body: some View {
WebViewWrapper(html: html)
}
}
@available(OSX 11.0, *)
public struct WebViewWrapper: NSViewRepresentable {
let html: String
public func makeNSView(context: Context) -> WKWebView {
return WKWebView()
}
public func updateNSView(_ webView: WKWebView, context: Context) {
webView.loadHTMLString(html, baseURL: nil)
webView.actualateHeight()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
webView.actualateHeight()
}
}
}
fileprivate extension WKWebView {
func actualateHeight(){
self.setFrameHeight(1)
self.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
if complete != nil {
self.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
if let height = height {
self.setFrameHeight(height as! Float)
}
})
}
})
}
func setFrameHeight(_ height: Float) {
let size = NSSize(width: self.frame.width, height: CGFloat(height) )
self.setFrameSize(size)
self.setBoundsSize(size)
print("YYY \(height)")
}
}
用法:
struct PreferencesView : View {
var model : Config
@ObservedObject var splittedStatusWindow : ConfigProperty<Bool>
@State var text: String = "<html> <style>body{background-color:linen;} H1 {font-size: 50;} .testClass { color: red; } #hell{ color: green;}</style> <body><h1 class='testClass'>Hello World</h1><p id='hell'><strong>hell</strong> yeah!</p></body></html>"
init(model: Config) {
self.model = model
self.splittedStatusWindow = model.splitedStatusWindow
}
var body: some View {
Spacer()
TextField("", text: $text)
Divider()
WebView(html: $text)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
Divider()
Spacer()
}
}
代码问题:
- hacky 解决方案错误的大小自动更新
- 代码包装器的大小比预期的要大。总是。
例如:
但预期某事像:
import SwiftUI
import WebKit
@available(OSX 11.0, *)
public struct WebView: View {
@Binding var html: String
@State var dynamicHeight: CGFloat = 10
public init ( html: Binding<String> ) {
_html = html
}
public var body: some View {
WebViewWrapper(html: html, dynamicHeight: $dynamicHeight)
.frame(height: dynamicHeight)
}
}
@available(OSX 11.0, *)
public struct WebViewWrapper: NSViewRepresentable {
let html: String
@Binding var dynamicHeight: CGFloat
public func makeNSView(context: Context) -> NoScrollWKWebView {
let a = NoScrollWKWebView()
a.navigationDelegate = context.coordinator
return a
}
public func updateNSView(_ webView: NoScrollWKWebView, context: Context) {
webView.loadHTMLString(html, baseURL: nil)
}
public func makeCoordinator() -> Coordinator { Coordinator(self) }
}
@available(OSX 11.0, *)
public extension WebViewWrapper {
class Coordinator: NSObject, WKNavigationDelegate {
var parent: WebViewWrapper
public init(_ parent: WebViewWrapper) {
self.parent = parent
}
public 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
}
})
}
}
}
public class NoScrollWKWebView: WKWebView {
public override func scrollWheel(with theEvent: NSEvent) {
nextResponder?.scrollWheel(with: theEvent)
}
}