ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [iOS] WebView in TableView구성
    Tech 2022. 2. 24. 00:09

    저는 날림 개발이니 해당 게시글의 틀린 부분은 님말이 맞습니다.

    이 블로그의 용도는 기록에 포커스를 맞추고 있습니다.


     

    앱을 만들 때 현실적으로 100% 네이티브를 만들기는 힘들다.

     

    예를 들어 특정 부분에는 링크가 들어간다던지 특정 부분은 배경이라던지

    이런 모든 상황을 100% 가정하고 common ui을 만들어서 쓰면 솔직히 좋다.

     

    하지만 현실은 유저가 생성한 콘텐츠라던가, 특정 이벤트에 따라 [이곳을 누르세요] 이런 걸 넣으려면... 

    너무 많은 케이스가 있기 때문에 여기서 어긋나면 해당 이벤트를 위해 단독 이벤트 페이지를 만들고 배포하는 꼴이 된다.

     

    그래서 대부분 웹을 섞어 쓴다, 아니 섞어 쓸 수밖에 없다. 현실은 그렇고요 뭐 아님 말고.

     

    그래서 오늘은 그동안 작업한 것들에 대한 케이스 설명~

     

    첫 번째는 페이지 전체를 덮는 경우

     

    WKWebView는 UIScrollview을 가지고 있기 때문에, WKWebView 그 자체로도 스크롤을 할 수가 있음.

    다만 UX상 사용자에게 명시적으로 이전으로 나갈 수 있는 액션이 없으면

    좋지 않기 때문에 갈색의 < 버튼 영역을 추가해서 이전 페이지로 나갈 수 있도록 배치했다.

     

    이경우는 단일 페이지로 끝내는 경우 (주로 프로모션 페이지) 진행했고, 단순 페이지만 띄워주면 그만이기 때문에 별다른 문제가 없었다.

    다만 해당 페이지로 시작하는 웹앱(혹은 웹페이지) 경우 사이트 내 내비게이션과 < 가 있도록 구성해서 앱에서 별다른 버튼은 배치하지 않음.

     

    물론 특정 조건을 가정해서 (주로 내비게이션 관련이나 화면 탈출 관련) window.webkit.messageHandlers을 통해서

    네이티브의 레이아웃을 조정하는 방법도 있던 걸로 기억. 물론 제한적이겠지만..

     

    전에 다니던 회사에서, 슈퍼앱을 만들고 싶다는 이야기가 있어서
    이런 기반으로 웹앱으로 빨리 만들고 앱에서 앱 타고 들어가서 진행할 수 있도록 아이디어는 냈는데, 물론 일정상 리소스 투입상 보류..

     

    두 번째는 UITableView에 넣는 경우 이 글을 쓴 이유.

     

    끔찍하지만...

     

    정확히는 WKWebView가 UITableViewCell에 있는 거겠지만 구체적인 건 대충 이해할 테니 넘어가도록 합시다.

     

    다들 알겠지만 UITableViewCell의 Autolayout을 잘 잡아놓으면 UITableViewCell의 높이는 고려할 필요는 없다.

    굳이 넣으면 이런 코드겠죠.

     

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
      return UITableView.automaticDimension
    }

     

    하지만 WKWebView는 UIScrollView이라는 것, 저렇게 하면 0으로 처리돼서 안 떠요...

     

    가 아래가 👇  삽질 기록입니다.

     


    1. 웹뷰 세팅하기

    UITableViewCell에 WKWebView 넣는 건 기본적이니 생략하고 제가 설정한 세팅값을 올립니다.

    webView.isOpaque = false
    webView.backgroundColor = .clear
    webView.scrollView.contentInset = .zero
    webView.scrollView.isScrollEnabled = false
    webView.scrollView.backgroundColor = .clear
    webView.scrollView.layer.masksToBounds = false
    webView.scrollView.bounces = false
    
    webView.backgroundColor = .clear

     

    배경색과, 스크롤, bounces 같은걸 모두 지웠습니다. 어차피 그런 데이터가 있으면 UITableView 내 이중 스크롤이 생기는 거고

    저희 목적은 '웹뷰가 딱 맞게 UITableView에 들어가기' 니깐요.

     

    2. CSS 세팅하기

    'HTML의 일부만 가져와서' wkwebview에 띄우는 케이스라면 
    HTML, Body 모두 기초적인 stylesheet을 셋업 해주세요 html, body { margin: 0; padding: 0px; } 라던지 
    우리가 웹사이트에서 보던 기초적인 css가 로드가 안돼서 높이 계산이 제대로 안 되는 부분이 허다합니다 'ㅅ'...

     

    그리고 가능하면 image, video 같은 미디어 객체에는 width height을 제대로 명시되어있다면 더 빠르고 편안한 계산이 가능합니다.

    없다면 아마 함정에 빠지실 거예요.

     

    HTML Element와 CSS 정상적으로 배치되어야 웹뷰도 이 높이가 이 거구 나를 알 수 있습니다.

    무슨 소리인지 모르겠다면 일단 옆자리의 웹 개발자에게 커피를 빨리 사줍시다.

     

    3. 시작하기

    1. wkwebView에 didFinish가 호출되면 wkwebview의 scrollview contentSize을 계산한다.
    2. wkwebView.scrollview.contentSize.height을 그대로 넘겨받아서 uitableview:heightForRowAt에서 넣고 테이블을 reloadData() 해본다 
    3. 잘 보인다!

    축하드립니다!

     

    4. 함정😩

     

    요즘 사이트는 lazy loading과 script 로딩 시 defer 라던가 있어서 실제 didFinish와 페이지가 처리되는 didFinish가 다릅니다.

    그리고 클릭했는데 높이가 더 추가되는 무언가 있다면? 그럼 didFinish에서 처리가 불가능합니다.

     

    네, 계속 좀 더 무언가를 계속 보고 있어야 합니다.

    self.webViewCell?.webView.scrollView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
    페이지를 obbserver를 제대로 안 죽여주면 화면 나갈 때 터지던가 아님 뒤에서 계속 잡고 있으니
    페이지가 deinit 되거나 없어질 때 잘 처리해주세요.

     

    contentSize로 해결된다면 여기서 끝낼 수 있습니다!

    observeValue에서 반환되는 값을 tableview에 넣어서 처리해주세요 잘 뜨면 ok입니다.

     

    💢💢💢💢💢 근데 왜.. 나는... 안 맞지? 💢💢💢💢💢

     

    근데 이것도 정적 페이지에서는 유효했는데 아이패드같이 큰 화면이나

    뭔가 사용자 액션으로 페이지가 늘어지면 contentSize가 무한으로 (..) 늘어지는 이슈가 생겼습니다. 왜? 모름..

     

    1번째 이슈는 위에 body/div을 감싼 stylesheet가 width, height, display, position이 제대로 정의가 안되어서

    그냥 말 그대로 백지에 뭔가 띄워진 상태이라 누가 객체고 높이고 안 잡힌 상태라 오류 났던 이유였고.

     

    2번째 이슈는 사용자가 액션을 취하면  contentSize가 무한으로 늘어났습니다. 추측으로는..

     

    [contentSize가 늘어나서 UITableViewCell의 높이를 변형 -> 테이블을 늘리니 웹뷰가 늘어나니 ->..]

     

    이거의 무한루프로 추측됩니다.

    암만 테스트해봐도 같은 상황이라 contentSize을 신뢰할 수 없었으니.. (여담으로 제가 넣은 HTML이 좀 가변 액션이 있긴 합니다.)

     

    그래서 직접적으로 자바스크립 실행으로 (..) 변경했습니다.

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if let object = object as? NSObject, object == self.webCell?.webView.scrollView, keyPath == "contentSize" {
            if let v = object as? UIScrollView {
                webView.evaluateJavaScript("document.body.offsetHeight", completionHandler: { (height, error) in
                    guard let height = height as? CGFloat else { return }
                    if self.webHeight == height { return }
                    logD("height", height)
                    self.webHeight = height
                })
             }
        }
    }

     

    (offsetHeight든 clientHeight든 조건은 맞는데 본인 상황에 맞는 걸 테스트해보세요.)

     


    여하튼 저는 contentSize obbserver + javascript 짬뽕으로 해결하긴 했습니다.

     

    딱히 검색해봐도 이거다 하는 답은 없어서 이리저리 삽질하다 작동 은하니 일단 블로그에 써봤습니다.

    가능하면 샘플 1처럼 풀 페이지 덮으시면 스크롤에 대해서 고민하실 필요는 없을.. 을것같고

     

    쇼핑몰, 커뮤니티는 혹은 외부 콘텐츠가 있다면 회사 서비스의 UI 위아래가 필요하니 case by case죠 뭐 상황에 맞춰서 할 수밖에..

     

    좋은 아이디어 있으면 댓글로 부탁드립니다.

     

    그럼 끗.

     

     

     

     

    댓글

Designed by Tistory.