2017-08-14 32 views
2

我寫了一些代碼來保存WKWebView的滾動視圖的contentOffsetzoomScale,所以它們可以在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} 

回答

1

我在做類似的事情,試圖在加載後獲取內容高度,然後調整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>" 

我注入了一些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) 
+0

你是說延遲可能是必要的,因爲在調用'didFinish ...'時webView沒有完全加載?並且您使用scrollView的高度達到了加載後(但放大之前)知道它已完全加載的高度? –

+1

是的網絡視圖加載,但滾動視圖尚未完成鋪設。我發現Web內容會加載,'didFinish'函數會觸發,我會調整Web視圖容器的大小。然而,在這一點上設置高度會影響Web視圖內容的佈局,由於某種原因它會增加大量的空白。所以我設置了一個延遲,但取決於設備和連接速度,它只會有時會起作用。但是在滾動視圖的內容大小上使用KVO並等待它達到一致的大小將起作用 –

+0

在您認爲scrollView完成更改之前,您觀察到了多少次KVO更改(沒有雙關語意圖)?在我通過KVO看到兩次相同的尺寸後,我嘗試重置偏移量和縮放,但之後我改變了它,我在原始的非縮放尺寸中看到兩個KVO回調。如果我忽略重置偏移和縮放,我會看到原始大小總共有五個KVO回調。 –

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)]; 
      }); 
     } 

    } 
} 

希望這會幫助你。

+0

儘管這可能會提供答案,但最好寫出它如何解決問題,以及它與其他答案有何不同。 –