我寫了一些代碼來保存WKWebView的滾動視圖的contentOffset
和zoomScale
,所以它們可以在webView加載後恢復。我發現,這些設置滾動視圖屬性僅使用延遲工作(例如用DispatchQueue.main.asyncAfter()
爲什麼這個必要嗎?有沒有更好的方式來實現這一目標?WKWebView需要延遲設置其scrollView的contentOffset和zoomScale加載後()
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
var contentOffset = CGPoint.zero
var zoomScale: CGFloat = 1.0
lazy var webView: WKWebView = {
let wv = WKWebView(frame: CGRect.zero)
wv.translatesAutoresizingMaskIntoConstraints = false
wv.allowsBackForwardNavigationGestures = true
wv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
wv.navigationDelegate = self
return wv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(webView)
webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
reload()
}
@IBAction func refreshTapped(_ sender: UIBarButtonItem) {
reload()
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// Without some delay, the restoration doesn't happen!
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: {
self.webView.scrollView.setZoomScale(self.zoomScale, animated: false)
self.webView.scrollView.setContentOffset(self.contentOffset, animated: false)
})
}
private func reload() {
contentOffset = webView.scrollView.contentOffset
zoomScale = webView.scrollView.zoomScale;
webView.load(URLRequest(url: URL(string: "https://www.google.com")!))
}
}
更新與AJ乙建議 (對不起對於重複)
import UIKit
import WebKit
class ViewController: UIViewController {
private static let keyPath = "webView.scrollView.contentSize"
private static var kvoContext = 0
private var contentOffset: CGPoint?
private var zoomScale: CGFloat?
private var lastContentSize: CGSize?
private var repeatedSizeCount = 0
lazy var webView: WKWebView = {
let wv = WKWebView(frame: CGRect.zero)
wv.translatesAutoresizingMaskIntoConstraints = false
wv.allowsBackForwardNavigationGestures = true
wv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
return wv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(webView)
webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addObserver(self, forKeyPath: ViewController.keyPath, options: .new, context: &ViewController.kvoContext)
reload()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeObserver(self, forKeyPath: ViewController.keyPath)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let newSize = change?[.newKey] as? CGSize, context == &ViewController.kvoContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
print("Observed: \(NSStringFromCGSize(newSize))")
// Nothing to restore if these are nil
guard let offset = contentOffset, let zoom = zoomScale else { return }
guard let lastSize = lastContentSize, lastSize == newSize else {
print("Waiting for size to settle")
lastContentSize = newSize
return
}
repeatedSizeCount += 1
guard repeatedSizeCount >= 4 else {
print("Size repeated \(repeatedSizeCount) time(s)")
return
}
print("Settled - set saved zoom and offset")
contentOffset = nil
zoomScale = nil
lastContentSize = nil
repeatedSizeCount = 0
webView.scrollView.setZoomScale(zoom, animated: false)
webView.scrollView.setContentOffset(offset, animated: false)
}
@IBAction func refreshTapped(_ sender: UIBarButtonItem) {
contentOffset = webView.scrollView.contentOffset
zoomScale = webView.scrollView.zoomScale;
reload()
}
private func reload() {
print("Reload: \(NSStringFromCGSize(webView.scrollView.contentSize))")
webView.load(URLRequest(url: URL(string: "https://www.google.com")!))
}
}
打印以下:
Reload: {781, 1453}
Observed: {781, 1453}
Waiting for size to settle
Observed: {320, 595}
Waiting for size to settle
Observed: {320, 595}
Size repeated 1 time(s)
Observed: {320, 595}
Size repeated 2 time(s)
Observed: {320, 595}
Size repeated 3 time(s)
Observed: {320, 595}
Settled - set saved zoom and offset
Observed: {781, 1453}
Observed: {781, 1453}
Observed: {781, 1453}
你是說延遲可能是必要的,因爲在調用'didFinish ...'時webView沒有完全加載?並且您使用scrollView的高度達到了加載後(但放大之前)知道它已完全加載的高度? –
是的網絡視圖加載,但滾動視圖尚未完成鋪設。我發現Web內容會加載,'didFinish'函數會觸發,我會調整Web視圖容器的大小。然而,在這一點上設置高度會影響Web視圖內容的佈局,由於某種原因它會增加大量的空白。所以我設置了一個延遲,但取決於設備和連接速度,它只會有時會起作用。但是在滾動視圖的內容大小上使用KVO並等待它達到一致的大小將起作用 –
在您認爲scrollView完成更改之前,您觀察到了多少次KVO更改(沒有雙關語意圖)?在我通過KVO看到兩次相同的尺寸後,我嘗試重置偏移量和縮放,但之後我改變了它,我在原始的非縮放尺寸中看到兩個KVO回調。如果我忽略重置偏移和縮放,我會看到原始大小總共有五個KVO回調。 –