ios - WKWebView requires delay to set its scrollView's contentOffset and zoomScale after load() -


i wrote code save off wkwebview's scroll view's contentoffset , zoomscale, restored after webview loads. i've found setting scrollview properties works using delay (e.g. dispatchqueue.main.asyncafter(). why necessary? there better way achieve this?

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

update aj b suggestion (sorry repetition)

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 restore if these nil         guard let offset = contentoffset, let zoom = zoomscale else { return }          guard let lastsize = lastcontentsize, lastsize == newsize else {             print("waiting size settle")             lastcontentsize = newsize             return         }          repeatedsizecount += 1          guard repeatedsizecount >= 4 else {             print("size repeated \(repeatedsizecount) time(s)")             return         }          print("settled - set saved zoom , 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")!))     } } 

prints following:

reload: {781, 1453} observed: {781, 1453} waiting size settle observed: {320, 595} waiting size 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 , offset observed: {781, 1453} observed: {781, 1453} observed: {781, 1453} 

i doing similar trying content height after load , sizing webview's container height. struggled awhile, best approach found observe wkwebview's scrollview content height, , when content height repeats @ same size know loaded. bit hacky worked consistently me. i'd know if knows better solution well.

//this example html string demonstrate issue  var html = "<html><body><p>we're no strangers love  know rules ,  full commitment's i'm thinking of  wouldn't other guy    want tell how i'm feeling  gotta make understand    never gonna give up, never gonna let down  never gonna run around , desert  never gonna make cry, never gonna goodbye  never gonna tell lie , hurt    we've known each other long  heart's been aching you're shy  inside both know what's been going on  know game , we're gonna play    , if ask me how i'm feeling  don't tell me you're blind see    never gonna give up, never gonna let down  never gonna run around , desert  never gonna make cry, never gonna goodbye  never gonna tell lie , hurt    never gonna give up, never gonna let down  never gonna run around , desert  never gonna make cry, never gonna goodbye  never gonna tell lie , hurt    we've known each other long  heart's been aching you're shy  inside both know what's been going on  know game , we're gonna play    want tell how i'm feeling  gotta make understand    never gonna give up, never gonna let down  never gonna run around , desert  never gonna make cry, never gonna goodbye  never gonna tell lie , 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>" 

i injected javascript send events ready state , dom loading, , printed size @ times didfinish navigation function. printed:

// adjusting 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 container height after content size repeats 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) 

Comments

Popular posts from this blog

python Tkinter Capturing keyboard events save as one single string -

android - InAppBilling registering BroadcastReceiver in AndroidManifest -

javascript - Z-index in d3.js -