WKWebViewのコンテンツ量を取得するときにハマった話
初めに
WKWebViewをUISrollView内部に埋め込む必要があり、
表示するページの仕様上、WebViewの高さ取得に苦労しました。
表示するWebページが静的が高さが固定なのであれば、簡単です。
今回表示することになったWebページは、ロード後に高さが変わっていたため、無理やり実装しました。
方法
(前提: WKWebViewのscrollView.isScrollEnabledはfalseです。)
高さ固定の場合
override func webView(
_ webView: WKWebView,
didFinish navigation: WKNavigation!
) {
// これで取れます
let height = webView.scrollView.contentSize.height
}
ここで取得した高さをWebViewのAutolayoutの制約やframeに使ってあげれば、OKです。
ロード後に高さが変わる場合
方法1: フロント側からJS連携でタイミングを教えてもらう
このパターンが一番お行儀がいいと思います笑
しかし、フロントチームに依頼する必要があるため、納期の関係上難しいこともあります。
方法2: タイマーで監視する
私の場合は、アプリ側で完結するこのパターンで実装しました。
Webページの仕様としては、webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
が実行されて1,2秒後に高さが変わっていたので、「タイマーで1秒ごとに高さを確認し、高さが変わっていれば更新し、タイマーは5秒で破棄する」というロジックにしました。
(KVOでobserveすることも考えましたが、無限ループする可能性があり危険)
class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
private var webView: WKWebView!
private var timerToObserveWebViewHeight: Timer?
private var timerToObserveWebViewHeightCount = 0
private var timerToObserveWebViewHeightLimit = 5
private var tmpWebViewHeight = 0
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView(frame: .zero, configuration: config)
webView.scrollView.isScrollEnabled = false
webView.navigationDelegate = self
webView.uiDelegate = self
let url = URL(string: "https://hogehoge-")!
let request = URLRequest(url: url)
webView.load(request)
}
@objc private func timerToObserveWebViewHeightAction() {
guard let timer = timerToObserveWebViewHeight else { return }
if timerToObserveWebViewHeightLimit == timerToObserveWebViewHeightCount {
timer.invalidate()
timerToObserveMyPageHeight = nil
timerToObserveWebViewHeightCount = 0
tmpWebViewHeight = 0
}
let height = webView.scrollView.contentSize.height
// 高さが更新されていれば、高さを更新する
if height != CGFloat(tmpMyPageHeight) {
webViewHeightConstraint?.constant = height
view.layoutIfNeeded()
}
tmpWebViewHeight = Int(height)
timerToObserveWebViewHeightCount += 1
}
override func webView(
_ webView: WKWebView,
didFinish navigation: WKNavigation!
) {
// ロード後に、高さを監視するタイマーを生成し、スタート
timerToObserveMyPageHeight = Timer.scheduledTimer(
timeInterval: 1,
target: self,
selector: #selector(timerToObserveWebViewHeightAction),
userInfo: nil,
repeats: true
)
timerToObserveMyPageHeight?.fire()
}
}
終わりに
タイマーで無理やり実装したので、他にいい方法があれば教えていただけると嬉しいです。
今の案件では、ロード後の数秒監視で対応できましたが、タイミングが分からない場合は、タイマーを動かし続けることになるので...
株式会社 カラビナテクノロジーは「命綱や支点を素早く確実に繋ぐカラビナ。そんなカラビナのような役割をテクノロジーで実現したい」という想いのもと、福岡で設立。 主にシステム開発・アプリ開発・ Webサイト制作を行っています。採用情報→karabiner.tech/recruit/requirements/
Discussion