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
Post a Comment