WKWebView需要延迟才能在load()之后设置其scrollView的contentOffset和zoomScale

时间:2017-08-14 14:51:13

标签: ios wkwebview

我编写了一些代码来保存WKWebView的滚动视图zoomScaleDispatchQueue.main.asyncAfter(),因此可以在加载webView后恢复它们。我发现设置这些scrollView属性只能使用延迟(例如使用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")!)) } } 。为什么这是必要的?有没有更好的方法来实现这个?

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")!))
    }
}

使用AJ B建议更新 (抱歉重复)

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}

打印以下内容:

{{1}}

4 个答案:

答案 0 :(得分:1)

我正在尝试类似的尝试在加载后获取内容高度,然后将webview的容器调整为该高度。我挣扎了一段时间,我发现最好的方法是观察WKWebView的ScrollView内容高度,当内容高度以相同的大小重复时,你知道它已经完全加载了。这有点hacky但它​​一直对我有用。我想知道是否有人知道更好的解决方案。

//This was an example html string that would demonstrate the issue

var html = "<html><body><p>We're no strangers to love  You know the rules and so do I  A full commitment's what I'm thinking of  You wouldn't get this from any other guy    I just want to tell you how I'm feeling  Gotta make you understand    Never gonna give you up, never gonna let you down  Never gonna run around and desert you  Never gonna make you cry, never gonna say goodbye  Never gonna tell a lie and hurt you    We've known each other for so long  Your heart's been aching but you're too shy to say it  Inside we both know what's been going on  We know the game and we're gonna play it    And if you ask me how I'm feeling  Don't tell me you're too blind to see    Never gonna give you up, never gonna let you down  Never gonna run around and desert you  Never gonna make you cry, never gonna say goodbye  Never gonna tell a lie and hurt you    Never gonna give you up, never gonna let you down  Never gonna run around and desert you  Never gonna make you cry, never gonna say goodbye  Never gonna tell a lie and hurt you    We've known each other for so long  Your heart's been aching but you're too shy to say it  Inside we both know what's been going on  We know the game and we're gonna play it    I just want to tell you how I'm feeling  Gotta make you understand    Never gonna give you up, never gonna let you down  Never gonna run around and desert you  Never gonna make you cry, never gonna say goodbye  Never gonna tell a lie and hurt you</p><p><img src=\"https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg\" width=\"\" height=\"\" style=\"display:block;height:auto;max-width:100%;width:100%;\"></p></body></html>"

我注入了一些javascript来发送就绪状态和dom加载的事件,并在那些时间打印大小以及didFinish导航功能。这就是印刷品:

// Adjusting the container height in didFinish navigation function 

started navigation
committed navigation
content height = 0.0
Javascript: Ready state change interactive | content height = 0.0
Javascript: DOM content loaded | content height = 0.0
Javascript: Ready state change complete | content height = 0.0
ended navigation content height = 0.0 (didFinish navigation)
content size observed = 1638.0
height constraint = Optional(0.0)
content size observed = 691.666666666667
height constraint = Optional(0.0)
content size observed = 1171.0
height constraint = Optional(0.0)
content size observed = 2772.0
height constraint = Optional(0.0)
content size observed = 2772.0
height constraint = Optional(0.0)
content size observed = 2772.0
height constraint = Optional(0.0)
content size observed = 2772.0
height constraint = Optional(0.0)
content size observed = 2772.0
height constraint = Optional(0.0)
content size observed = 2772.0
height constraint = Optional(0.0)
content size observed = 2772.0
height constraint = Optional(0.0)

// Adjusting the container height after content size repeats itself KVO

started navigation
committed navigation
content height = 0.0
Javascript: Ready state change interactive | content height = 0.0
Javascript: DOM content loaded | content height = 0.0
Javascript: Ready state change complete | content height = 0.0
ended navigation content height = 0.0 (didFinish navigation)
content size observed = 1638.0
height constraint = Optional(1.0)
content size observed = 691.666666666667
height constraint = Optional(1.0)
content size observed = 691.666666666667
height constraint = Optional(1.0)
content size observed = 691.666666666667
height constraint = Optional(691.666666666667)
content size observed = 691.666666666667
height constraint = Optional(691.666666666667)
content size observed = 691.666666666667
height constraint = Optional(691.666666666667)
content size observed = 691.666666666667
height constraint = Optional(691.666666666667)
content size observed = 691.666666666667
height constraint = Optional(691.666666666667)
content size observed = 691.666666666667
height constraint = Optional(691.666666666667)
content size observed = 691.666666666667
height constraint = Optional(691.666666666667)

答案 1 :(得分:0)

//default flag is 2
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    NSLog(@"scrollView:%.f", scrollView.contentOffset.y);
    self.flag--;
    if (self.flag == 0) {
        [self jump];
    }
}
- (void)jump{
    if ([self.urlString isEqualToString:self.webView.URL.absoluteString]) {
        CGFloat offsetY = 0;
        offsetY = [[NSUserDefaults standardUserDefaults] floatForKey:@"history"];
        if (offsetY) {
            self.markLabel.hidden = NO;
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.webView.scrollView setContentOffset:CGPointMake(0, offsetY)];
            });
        }

    }
}

希望这会对你有所帮助。

答案 2 :(得分:0)

我在minimumZoomScale上设置了两个参数setZoomScaledidFinish

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.scrollView.minimumZoomScale = self.store.zoom
        self.scrollView.setZoomScale(self.store.zoom, animated: true)
    }

答案 3 :(得分:0)

我发现,至少对于内容偏移量,我可以通过JavaScript进行设置,而无需延迟即可在webView(_, didFinish:)中进行设置。

换句话说,替换

self.webView.scrollView.setContentOffset(self.contentOffset, animated: false)

使用

self.webView.evaluateJavaScript("window.scrollTo(\(self.contentOffset.minX), \(self.contentOffset.minY));)