웹 뷰를 만들고 웹 뷰에서 넘어오는 값을 받거나 웹 뷰에게 값을 넘기는 방법을 알아보자.
웹 뷰 초기 세팅
import Foundation
import UIKit
import SnapKit
import WebKit
class MainViewController: UIViewController, ReactorKit.View {
...
// safe area view
let safetyAreaView = UIView()
let progressView: UIProgressView = {
let view = UIProgressView()
view.trackTintColor = .lightGray
view.progressTintColor = .systemRed
return view
}()
// 웹 뷰 담을 뷰
let containerView = UIView()
// WebView
var webView = WKWebView()
// 로딩 뷰
let indicator: UIActivityIndicatorView = {
let activityIndicator = UIActivityIndicatorView()
activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
activityIndicator.color = .black
activityIndicator.hidesWhenStopped = true
activityIndicator.style = UIActivityIndicatorView.Style.medium
activityIndicator.stopAnimating()
return activityIndicator
}()
...
override func viewDidLoad() {
super.viewDidLoad()
initView()
initConstraint()
}
private func initView() {
self.view.backgroundColor = .white
safetyAreaView.backgroundColor = .clear
self.view.addSubview(safetyAreaView)
containerView.backgroundColor = .white
safetyAreaView.addSubview(containerView)
containerView.addSubview(progressView)
setAttributes()
view.addSubview(indicator)
}
func initConstraint() {
safetyAreaView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide)
make.leading.trailing.equalToSuperview()
make.bottom.equalToSuperview()
}
containerView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
progressView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.leading.trailing.equalToSuperview()
}
webView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
indicator.snp.makeConstraints { make in
make.center.equalToSuperview()
}
}
private func setAttributes() {
let contentController = WKUserContentController()
// 중요!!! 일단 초기 세팅으로 두자
contentController.add(self, name: "customHandlerCallBack1")
contentController.add(self, name: "customHandlerCallBack2")
let configuration = WKWebViewConfiguration()
configuration.suppressesIncrementalRendering = false
configuration.userContentController = contentController
configuration.defaultWebpagePreferences.allowsContentJavaScript = true
let pref = WKWebpagePreferences.init()
pref.preferredContentMode = .mobile
configuration.defaultWebpagePreferences = pref
webView = WKWebView(frame: .zero, configuration: configuration)
webView.backgroundColor = .white
webView.scrollView.alwaysBounceVertical = true
webView.scrollView.bounces = false
webView.scrollView.isScrollEnabled = true
webView.scrollView.showsVerticalScrollIndicator = true
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webView.scrollView.contentInsetAdjustmentBehavior = .always
webView.navigationDelegate = self
webView.uiDelegate = self
webView.scrollView.backgroundColor = .white
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
// webView.navigationDelegate = self
containerView.addSubview(webView)
loadWebView()
}
private func loadWebView() {
let urlRequest: URLRequest = URLRequest(url: URL(string: "http://웹뷰에 띄울 웹 URL")!)
webView.load(urlRequest)
// 앞뒤 스와이핑 허용 여부 (default: false)
// webView.allowsBackForwardNavigationGestures = true
}
// 웹뷰의 모든 캐시나 쿠키를 지우고 싶을 때 사용 (현재 코드엔 사용안함)
func deleteWebCash() {
let websiteDataTypes = NSSet(array: [
WKWebsiteDataTypeDiskCache,
WKWebsiteDataTypeMemoryCache,
WKWebsiteDataTypeCookies,
WKWebsiteDataTypeOfflineWebApplicationCache,
WKWebsiteDataTypeWebSQLDatabases])
let date = Date(timeIntervalSince1970: 0)
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes as! Set<String>, modifiedSince: date, completionHandler: { })
}
// 웹뷰의 로딩 진행률 출력
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("webView processing: \(webView.estimatedProgress)")
containerView.bringSubviewToFront(progressView)
if webView.estimatedProgress == 1.0 {
progressView.setProgress(0, animated: false)
progressView.isHidden = true
} else {
progressView.setProgress(Float(webView.estimatedProgress), animated: true)
progressView.isHidden = false
}
}
.....
}
extension MainViewController: WKNavigationDelegate {
// 로드 시작
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
Log.network("webView didStartProvisionalNavigation")
Log.network("WebView didstart url : \(webView.url?.absoluteString)")
}
// 로드 종료
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
Log.network("webView didFinish")
Log.network("WebView didFinish url : \(webView.url?.absoluteString)")
webView.configuration.suppressesIncrementalRendering = true
}
// 로드 오류
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
Log.network("webView didFail fail : \(error)")
Log.network("WebView didFail fail url : \(webView.url?.absoluteString)")
let alert = UIAlertController(title: "webView didFail fail", message: error.localizedDescription, preferredStyle: UIAlertController.Style.alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler : nil )
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
// 서버 끊겨있을 시
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
Log.network("webView didFailProvisionalNavigation fail : \(error)")
Log.network("WebView didFailProvisionalNavigation fail url : \(webView.url?.absoluteString)")
let alert = UIAlertController(title: "webView didFailProvisionalNavigation fail", message: error.localizedDescription, preferredStyle: UIAlertController.Style.alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler : nil )
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
웹 뷰 -> 네이티브 받을 때 코드
우선 네이티브 쪽
/*
반드시 위에 코드 중
contentController.add(self, name: "customHandlerCallBack1")
contentController.add(self, name: "customHandlerCallBack2")
을 작성하여 핸들러를 추가해주자
*/
extension MainViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
Log.debug("message: \(message.debugDescription)")
Log.debug("userContentController, didReceive : \(message.name), \(message.body)")
switch message.name {
case "customHandlerCallBack1":
if let data = message.body as? [String: Any] {
Log.debug("웹 뷰에서 넘어온 data: \(data)")
Log.debug(data["key값"]) // value값
....
}
case "customHandlerCallBack2":
if let data = message.body as? [String: Any] {
Log.debug("웹 뷰에서 넘어온 data: \(data)")
Log.debug(data["key값"]) // value값
....
}
default:
break
}
}
}
자바스크립트 쪽
function callNative() {
// 네이티브에 customHandlerCallBack1 이란 name으로 전송
webkit.messageHandlers.customHandlerCallBack1.postMessage(data);
// 네이티브에 customHandlerCallBack2 란 name으로 전송
webkit.messageHandlers.customHandlerCallBack2.postMessage(data);
}
네이티브 -> 웹 뷰 넘길 때 코드
class MainViewController: UIViewController, ReactorKit.View {
...
func bindAction(_ reactor: MainViewReactor) {
//action
testBtn.rx.tap
.subscribe {[weak self] _ in
guard let self = self else { return }
// show라는 javascript 함수 호출 (파라미터에 'a' 인자 넣어서 전송)
self.webView.evaluateJavaScript("show('a')") { (result, error) in
print(result)
}
}
.disposed(by: disposeBag)
}
...
}
자바스크립트 코드
function show(data) {
console.log(data)
}
'Swift' 카테고리의 다른 글
[Swift] FCM 주제 구독 및 백그라운드 처리 (0) | 2024.07.01 |
---|---|
[Swift] 의존성 주입, DIContainer(IOC Container)만들기 (0) | 2024.02.15 |
[Swift] 좌우 무한 collectionView 를 만들어 보자 (2) | 2024.01.05 |
[Swift] 상단 탭바 페이지 뷰컨트롤러 만들기 (Upper Tab Page View) (0) | 2024.01.02 |
[Swift] UICollectionView 내부 내용에 따른 Cell 동적 높이 설정법 (0) | 2023.11.10 |