What am I supposed to pass to scrollToEndOfDocument(_:)? - swift

I’m trying to get a WKWebView to scroll down automatically when its content changes. I added this function to my WKNavigationDelegate, which is a subclass of NSViewController:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("Finished loading web view.")
webView.scrollToEndOfDocument(self)
}
It crashes:
-[WKWebView scrollToEndOfDocument:]: unrecognized selector sent to instance 0x6080001a07e0
I guess self is not what I should be passing to scrollToEndOfDocument. I also tried to pass webView or nil instead, with the same result. What value should I pass?
N.B. I tried self because Apple’s docs say:
Declaration
func scrollToEndOfDocument(_ sender: Any?)
Parameters
sender
Typically the object that invoked this method.

You may have to do it via JavaScript:
let script = "window.scrollTo(0, document.body.scrollHeight)"
webView.evaluateJavaScript(script)
This is for old-school WebView; leaving for posterity:
Try calling scrollToEndOfDocument: on the WebView's main frame's document view:
webView.mainFrame.frameView.documentView.scrollToEndOfDocument(nil)
(What you pass in as sender shouldn't matter and has no bearing on the error you're seeing.)

Related

WKWebView Navigation Delegate Not Called

I am having difficulty getting a WKWebview to call its delegate methods, namely the did finish one.
I am attempting something similar to this post generating a pdf report using html loaded into a WKWebView;
WKWebView not calling navigation delegate methods
However trying the suggestion there, that doesn't work for me. I have a WKWebView made in a separate class to the View controller that will be calling its methods.
If I strip things right down and just try to simply load a website in the WebView to see if the delegate functions get called I get nothing.
Here is my test class;
import UIKit
import WebKit
class TestWebview: NSObject {
let webView = WKWebView ()
func loadWebsite () {
print ("LOAD TEST CALLED")
webView.navigationDelegate = self
let link = URL(string: "https://www.apple.com")!
let request = URLRequest(url: link)
webView.load(request)
}
}
extension TestWebview: WKNavigationDelegate {
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
print ("DID COMMIT")
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print ("FINISHED LOADING")
}
}
And here is how I am calling it in a separate view controller;
override func viewDidLoad() {
super.viewDidLoad()
let test = TestWebview ()
test.loadWebsite()
}
Although if I put a WKWebView as an outlet into a view controller using Storyboards then the delegate methods do get called. I am trying to avoid this however as I don't want to see a web view in the view controller and want to keep all the code for that separate in another class.
Am I missing something? Checking various other posts on Stack Overflow about this hasn't given me any solutions other than making sure the navigation delegate is being set, which I am. Any help would be greatly appreciated.
You need to move let test = TestWebview () to class level, otherwise this object is going to be evicted when viewDidLoad completes: the navigationDelegate is defined as weak, so it's not going to prevent it either.

Swift NSObject class does not reliably respond to delegate method calls

I have had a couple of instances of this recently, so I thought I'd reach out to better understand if I am doing something wrong or iOS/Xcode is just being weird.
I have a utility class, which just inherits from NSObject. My utility class holds reference to a WKWebView, and is the webview's navigationDelegate.
For the life of me, I cannot get the utility class to respond to the navigationDelegate methods, UNLESS I breakpoint at the point of setting the delegate, print out something (anything, from my testing) to the console (yep, this is important. Just breakpointing isn't enough - I have to PRINT something). Then re-run, and it responds as normal.
I am very familiar with iOS development and delegation patterns, but this is behaviour I've seen before and never understood, so I wanted to better my understanding if it's something I am getting wrong.
Here is some example code of what I am doing. It's worth noting, that if I take the utility out of the equation and just use UIViewControllers as the navigationDelegate, everything works fine.
class ExampleUtility: NSObject, WKNavigationDelegate {
private weak var containerViewController: UIViewController?
private let webView: WKWebView
init(containerViewController: UIViewController) {
self.containerViewController = containerViewController
webView = WKWebView(frame: containerViewController.view.frame)
super.init()
setupWebView()
}
private func setupWebView() {
webView.navigationDelegate = self
containerViewController?.view.addSubview(webView)
}
func start() {
guard let url = URL(string: urlString) else {
return
}
let request = URLRequest(url: url)
webview.load(request)
}
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// This is never called, unless I print ANYTHING to the console
}
}
The fact that it responds after breakpointing and printing makes me thing its a bug of some sort? Also worth nothing that this isn't only on debug runs. It happens when not running from Xcode also.
Any help is greatly appreciated.
Please just keep ExampleUtility object as property in containerViewController.
Then it will work.
The reason you have this issue is, the webview instance is created in the ExampleUtility but the instance of Example Utility was already released since it is not property of containerViewController.
Please try and it will work surely.

WKNavigation delegate functions calling too late

I am loading two websites in two different web views and after loading website i am hiding some content of it which is working perfect using WKNavigationDelegate did finish method.
but issue is did finish function is calling about 90 sec after loading website in web view.
Function is working fine but i just want to know why this function is loading too late it should execute function right after loading website.
my code is
web_view.navigationDelegate = self as? WKNavigationDelegate
web_view.isUserInteractionEnabled = true
let request = URLRequest(url: url!)
self.web_view.load(request)
self.view.addSubview(self.web_view)
delegate method
extension urdu_HomeViewController : WKNavigationDelegate{
//enable javascript to remove vavigation from website
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
let removeelementid = "javascript:(function() { " + "document.getElementsByClassName('td-header-menu-wrap-full td-container-wrap')[0].style.display=\"none\"; " + "})()"
webView.evaluateJavaScript(removeelementid) { (res, error) in
if error != nil
{
print("Error")
}
else
{
//print(res!)
}
}
}
WKNavigationDelegate didFinish method is called only after all subresources are loaded. So in your case it might be that some resource takes too much time to load and that delays calling didFinish.
You should rather use WKUserScript to execute your JS right after DOM is ready.

How can I check If the valid page was loaded in the WKWebView?

That's what I'm doing:
Loading the page in WKWebView
Pressing the button on the page
As a result I can get either valid URL, either invalid URL. How do I check if I've got the valid one? I need something like URLThatWasLoadedInWKWebView == "myValidURL"?
var webView: WKWebView!
var webUrl="google.com"
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: webUrl)!
let urlRequest = URLRequest(url: url)
webView.load(urlRequest)
view.insertSubview(webView, at: 0)
}
After I clicked on the button, I need to check that the page has loaded the valid "mail.google.com"
If you have your view controller conform to the WKNavigationDelegate then you can use those delegate functions to get the information you're looking for.
Inside this delegate function:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
You could check the webView.url which shows you the url that the web view just loaded. You could use that to check against whatever url you are trying to check against.

WKNavigationDelegate only works if set to self

I am writing a Swift Hybrid application and I need to be able to know when my WKWebView has finished loading a request. I am attempting to use WKNavigationDelegate to achieve this. However, the only way I can get the events to fire is if I set it this way:
webView.navigationDelegate = self
The problem is I have some data that I want associated with my request, so I created a custom class that implements WKNavigationDelegate that looks like this:
class MyNavigationDelegate : NSObject, WKNavigationDelegate {
init(...) {
//set local variables with passed args
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
println("finished loading")
}
}
then I assign it like this:
webView.navigationDelegate = MyNavigationDelegate(my, arguments, here)
webView.loadRequest(...)
The page is loading so there is no problem with how I'm loading it. What am I missing?
The delegate is a weak property, so make sure that you're holding on to the delegate class instance elsewhere -- it's likely getting deallocated before it even gets used. Meanwhile, self is not getting deallocated, so it is working.