WKWebView에서 새 창을 열고 닫고, 새 창을 열 때 target이 blank로 처리된 경우 처리하는 법을 알아보자.


웹 뷰를 이용해서 개발을 하다가 새 윈도우 창을 열 때 blank는 지원하는 것이냐는 질문을 받고 blank가 뭔지 몰라 당황했던 기억이 있다. 구글링을 하다보니 정말 기본중에 기본… 이었는데, WKWebView에서 새 윈도우 창을 열고, 닫게 하는 방법과 또 이 blank는 무엇인지 공부한 내용을 정리해보려고 한다.

window.open()

새로운 윈도우 창을 띄울 때 window.open()이라는 코드를 사용한다. 이 때 옵션도 같이 설정할 수 있다.

보통 JS에서 새로운 창을 띄우려면 아래와 같이 코드를 작성할 것이다.

<script type="text/javascript">
function openPop(){
    var popup = window.open('http://www.naver.com', '네이버팝업', 'width=700px,height=800px,scrollbars=yes');
}
</script>

<body>
   <a href="#none" target="_blank" onclick="openPop()">팝업</a>
</body>

위 코드는 링크를 클릭했을 때 네이버 창 팝업을 띄우는 것이다.

팝업 여는 위치, target

이때 팝업을 띄울 위치를 설정할 수 있다.

  • target="_blank" : 팝업을 새 창에서 연다. default 값이다.
  • target="_parent" : 부모 창에서 팝업이 열린다.
  • target="_self" : 현재 페이지에서 팝업이 열린다.
  • target="_top" : 현재 페이지에서의 최상위 페이지에서 팝업이 열린다.
<!--팝업을 여는곳 -->
 <a href="#none" target="_blank" onclick="openPop()">팝업</a>

그래서 target을 blank로 설정하면 새 빈 페이지의 윈도우 창을 띄우고 그 창에서 해당 링크로 이동하겠다는 뜻이다.

iOS WebView에서 새 윈도우 창/팝업 열고 닫기

iOS에서 WKWebView를 사용하고 있을 때, 특정 메서드를 오버라이딩하지 않으면 window.open()으로 새로운 창을 열려고 할 때 열리지 않는다. 따라서 WKUIDelegate의 메서드를 오버라이딩 해줘야 한다.

이때 새 윈도우 창이나 팝업을 열고 닫는 방법은 크게 두 가지가 있다.

1. SubView 추가하기

내가 사용한 방법이다. 그나마 이 방법을 이용하는 것이 간단했고, 상위 뷰에서 새로운 윈도우 창을 열 때마다 하위 뷰를 더한다는 점에서 구조가 직관적이라고 생각했다. 아래의 두 메서드 모두 WKUIDelegate의 메서드이다.

// 새 윈도우 창/팝업 열기
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    let newWebView = WKWebView(frame: webView.bounds, configuration: configuration)
    newWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    newWebView.uiDelegate = self
    newWebView.navigationDelegate = self 
    webView.addSubview(newWebView) // 새 하위 뷰를 만들어서 추가한다.

    return newWebView
}

// 윈도우 창/팝업을 닫을 때
func webViewDidClose(_ webView: WKWebView) {
    webView.removeFromSuperview() // 상위 뷰에서 뷰를 제거한다.
}

2. JavaScript injection 사용하기

이 방법을 쓰려면 WKNavigationDelegate를 구현해야 한다. 위의 방법은 WKUIDelegate만 구현해도 됐었다. 여기에서는 window.open()을 했을 때, 새로운 윈도우를 여는 것 대신에 같은 위도우에서 새로운 url 주소를 로드하는 방식으로 구현한다. 또 window.close()를 했을 때 웹 뷰 컨트롤러가 dismiss 될 것이다.

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    webView.evaluateJavaScript("window.open = function(open) { return function (url, name, features) { window.location.href = url; return window; }; } (window.open);", completionHandler: nil)
    webView.evaluateJavaScript("window.close = function() { window.location.href = 'myapp://closewebview'; }", completionHandler: nil)

    if let url = navigationAction.request.url?.absoluteString, url.contains("myapp://closewebview") {
        self.dismiss(animated: true, completion: nil)
        decisionHandler(.cancel)
        return
    }

    decisionHandler(.allow)
}

그리고 <a href="http://a_hyper_link" target="_blank">Display name</a>를 지원하기 위해서 , WKUIDelegate의 메서드를 아래와 같이 구현한다.

func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
  if !(navigationAction.targetFrame?.isMainFrame ?? false) {
    webView.load(navigationAction.request)
  }
  return nil
}

참고로 이를 테스트하기 위한 사이트가 있으니 참고하면 아주 편할 것 같다.(https://facebook-login-demo-71f0c.firebaseapp.com/)

출처

  • https://lnsideout.tistory.com/entry/jQuery-windowopen-%ED%8C%9D%EC%97%85-%EC%99%84%EB%B2%BD%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC
  • https://stackoverflow.com/questions/25713069/why-is-wkwebview-not-opening-links-with-target-blank
  • https://blog.nextzy.me/ios-handling-popup-new-window-inside-web-view-c9c91b23ac2b