[Swift] WKWebView를 통해 로컬의 image를 load할 때 주의 사항

민경준·2022년 4월 7일
2

기본적으로 로컬의 image를 로드할 때 아래와 같이 하면 손 쉽게 가능하다.

guard let url = Bundle.main.url(forResource: "image", withExtension: "png") else { return }

let webView = WKWebView(frame: self.view.frame)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webView.loadFileURL(url, allowingReadAccessTo: url)
webView.navigationDelegate = self
self.view.addSubview(webView)

그런데 여기서 문제는 이미지가 가운데로 정렬되지 않는다는 점..
이유를 찾아봤더니 초기에 이미지 태그에 padding 값이 들어가는데 이것이 iphone safe Area에 맞춰져 있었다.
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)

/ / ---> [ 아이폰X 안전영역(Safe Area) 대응 사례 공유 ]


그래서 해당 padding 값을 고쳐서 중앙에 배치하기 위해 `evaluateJavaScript(_:completionHandler:)` 함수를 사용하기로 했다.
우선 load가 완료되고 난 이후에 padding 값을 조절해야 하기 때문에 `WKNavigationDelegate` 함수인 `webView(_:didFinish:)` 함수에서 코드를 작성하기로 했다.
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
	let imgHeight: CGFloat = (image.size.height / 2.0)
    let sizeHeight: CGFloat = (self.view.frame.size.height / 2.0)
    let topPadding: CGFloat = sizeHeight - imgHeight
    
    /// image의 길이가 view의 길이보다 길다면 가운데 정렬을 할 필요가 없다.
    guard topPadding > 0 else { return }
    /// padding 값은 순서대로 top right bottom left 을 뜻한다.
    let paddingStr: String = "\(topPadding) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)"
    /// image가 로드 될 경우 tag는 img로 들어가기 때문에 img에 해당하는 태그를 찾아 스타일을 변경 해준다.
    let script: String = "document.getElementsByTagName('img')[0].style.padding='\(paddingStr)';"
    webView.evaluateJavaScript(script)
}
}

해당 코드를 실행시키고 나면 아래와 같은 모습이 나온다.

하지만 여기서 주의해야 할 부분이 하나 있다. 위와 같이 이미지의 사이즈가 view의 width 보다 작다면 잘 작동하지만 view의 width를 넘어서게 된다면 해당 코드가 제대로 작동하지 않게 된다.

4k 화질의 사진을 이용해서 테스트를 해보면 같은 코드라고 해도 아래와 같은 결과가 나온다.

(가운데 정렬이 제대로 적용되지 않은 모습...)

대체 왜 이런 현상이 나타나는걸까? 이유는 간단했다. 이미지 사이즈에 맞추기 위해서 wkwebview의 픽셀이 자체적으로 이미지의 픽셀에 맞게 늘어난것... 화면을 통해서 눈에 보이는건 똑같지만 실제 화면 안에 픽셀은 위의 사진을 로드했을 때 픽셀과 다르다. 그래서 padding 값을 설정할 때 그 pixel 수치에 맞게 설정 해줘야 제대로 작동한다.

예를 들자면 처음 사진의 경우 화면의 픽셀이 w: 414.0, h: 896.0 였다면 두번째 사진의 경우 사진의 크기인 w: 2560 , h: 3840 에 맞춰서 화면의 픽셀이 w: 2560, h: 5540이 된다는 이야기이다.

그래서 padding 값을 설정할때 변화된 화면 픽셀의 높이인 5540에 맞춰서 설정을 해줘야 한다. 이와 관련된 코드를 위의 코드 중간에 작성해보도록 하자.

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
	let imgHeight: CGFloat = (image.size.height / 2.0)
    var sizeHeight: CGFloat = (self.view.frame.size.height / 2.0)
    
    <<---------------------------
    if self.view.frame.width < image.size.width {
    	/// image의 width가 view의 크기보다 클 경우
        /// webView의 픽셀 값이 image의 width에 맞춰 변경 되기 때문에
        /// view size 값도 해당 픽셀값에 대응하여 변경해 줘야 한다.
    	let pixelHeight: CGFloat = (image.size.width * self.view.frame.height) / self.view.frame.width
        sizeHeight = (pixelHeight / 2.0)
    }
    ---------------------------->>
    
    let topPadding: CGFloat = sizeHeight - imgHeight
    /// image의 길이가 view의 길이보다 길다면 가운데 정렬을 할 필요가 없다.
    guard topPadding > 0 else { return }
    
    /// padding 값은 순서대로 top right bottom left 을 뜻한다.
    let paddingStr: String = "\(topPadding) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)"
    
    /// image가 로드 될 경우 tag는 img로 들어가기 때문에 img에 해당하는 태그를 찾아 스타일을 변경 해준다.
    let script: String = "document.getElementsByTagName('img')[0].style.padding='\(paddingStr)';"
    webView.evaluateJavaScript(script)
}

이렇게 하면 다음과 같이 정상적으로 가운데 정렬이 되는 모습을 확인 할 수 있다.

profile
iOS Developer 💻

0개의 댓글