代码之家  ›  专栏  ›  技术社区  ›  Robert Hartman

加载后,WKWebView需要延迟设置其scrollView的contentOffset和zoomScale()

  •  4
  • Robert Hartman  · 技术社区  · 8 年前

    contentOffset zoomScale ,因此可以在Web视图加载后恢复。我发现设置那些scrollView属性只能使用延迟(例如 DispatchQueue.main.asyncAfter() . 为什么这是必要的?有没有更好的方法来实现这一点?

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

    打印以下内容:

    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}
    
    4 回复  |  直到 8 年前
        1
  •  1
  •   AJ B    8 年前

    我做了一些类似的事情,试图在加载后获得内容高度,然后将webview的容器大小调整为该高度。我与它斗争了一段时间,我发现最好的方法是观察WKWebView的ScrollView内容高度,当内容高度以相同的大小重复时,你就知道它已完全加载。这有点老套,但对我来说一直有效。我想知道是否还有人知道更好的解决方案。

    //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>"
    

    // 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)
    
        2
  •  1
  •   Chris Vasselli    5 年前

    我发现,至少对于内容偏移量,我能够将其设置为 webView(_, didFinish:) 通过JavaScript毫不延迟地进行设置。

    换言之,替换

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

    具有

    self.webView.evaluateJavaScript("window.scrollTo(\(self.contentOffset.minX), \(self.contentOffset.minY));)
    
        3
  •  0
  •   clemens    8 年前
    //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)];
                });
            }
    
        }
    }
    

        4
  •  0
  •   Victor Kushnerov    6 年前

    minimumZoomScale setZoomScale didFinish

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