Develop/Swift

SwiftUI 상에서 HTML 출력과 Link, CSS 적용하는 방법

hsleedevelop 2022. 11. 13. 12:52
반응형

Feature

  1. SwfitUI에서 HTML을 출력한다.
  2. HTML - 링크 클릭 시, 활성화된 링크를 가져오거나, 핸들러를 제공한다.
  3. HTML에 CSS를 적용한다.
import SwiftUI
import WebKit

///참고 - https://stackoverflow.com/a/59018581/3374327
struct SUIWebView: UIViewRepresentable {
    @ObservedObject var viewModel: SUIWebViewModel

    let url: URL?
    let html: String?
    let css: String?

    func makeUIView(context: UIViewRepresentableContext<SUIWebView>) -> WKWebView {
        
        var webView: WKWebView!
        if let url = url {
            let request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad)
            webView = .init()
            webView.load(request)
        } else if let html = html {
            
            if let css = css {
                let source = """
                var style = document.createElement('style');
                style.innerHTML = '\(css)';
                document.head.appendChild(style);
                """
                
                let userScript = WKUserScript(source: source,
                                              injectionTime: .atDocumentEnd,
                                              forMainFrameOnly: true)
                
                let userContentController = WKUserContentController()
                userContentController.addUserScript(userScript)
                
                let configuration = WKWebViewConfiguration()
                configuration.userContentController = userContentController
                webView = .init(frame: .zero, configuration: configuration)
            } else {
                webView = .init()
            }
            webView.loadHTMLString(html, baseURL: nil)
        }
        
        webView.navigationDelegate = context.coordinator
        return webView
    }

    func updateUIView(_ webview: WKWebView, context: UIViewRepresentableContext<SUIWebView>) {
        
    }
    
    final class Coordinator: NSObject, WKNavigationDelegate {
        private var viewModel: SUIWebViewModel

        init(_ viewModel: SUIWebViewModel) {
            self.viewModel = viewModel
        }

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            //print("WebView: navigation finished")
            self.viewModel.didFinishLoading = true
        }
        
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            if navigationAction.navigationType == WKNavigationType.linkActivated {
                if let url = navigationAction.request.mainDocumentURL {
                    self.viewModel.activedLinkURL = url
                    self.viewModel.navigateFunc?(url)
                }
                decisionHandler(.cancel)
                return
            }
            decisionHandler(.allow)
        }
    }

    func makeCoordinator() -> SUIWebView.Coordinator {
        Coordinator(viewModel)
    }
}

final class SUIWebViewModel: ObservableObject {
    @Published var activedLinkURL: URL?
    @Published var didFinishLoading: Bool = false
    var navigateFunc: ((URL) -> Void)?
}

 

private let css = "body { font-size: 2.4rem; font-weight: normal; font-stretch: normal; line-height: 1.21; letter-spacing: 0;text-align: left; color: #666666; }"

VStack {
	SUIWebView(viewModel: webViewModel, url: nil, html: term.termsDetailContents, css: css)
	if webViewModel.didFinishLoading {
    	let _ = logD("didFinishLoading")
    }
}
 
 

 

 

반응형