I have a view controller for a webview, setup through KINWebBrowser.
I've tried several ways to get these methods to execute:
func webViewDidStartLoad(webView: UIWebView) {
print("Strat Loading")
}
func webViewDidFinishLoad(webView: UIWebView) {
print("Finish Loading")
}
func webView(webView: UIWebView, didFailLoadWithError error: NSError?) {
print(error?.localizedDescription)
}
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
return true
}
override func webViewDidFinishLoad(_ webView: UIWebView) {
super.webViewDidFinishLoad(webView)
print("Finish Loading 2")
}
But nothing seems to be working.
My class definition seems normal: class WebBrowserViewController: KINWebBrowserViewController, NavigationProtocol {
But no matter what I do - override func or not, no logger or print statement I put in these functions seems to be executed.
What am I doing wrong? I am trying to create an event that will listen to when all of the web content is loaded so I can stop having a loading spinner appear.
EDIT: Adding delegate information:
When I have tried to add a delegate to viewDidLoad():
webView.delegate = self
I have gotten the following error:
Ambiguous reference to member 'webView(_:decidePolicyFor:decisionHandler:)'
This is my viewDidLoad() without the delegate setting:
override func viewDidLoad() {
super.viewDidLoad()
pointsNavigationItem = addPointsButtonToNavigation()
self.showsURLInNavigationBar = false
self.showsPageTitleInNavigationBar = false;
self.tintColor = UIColor.navText
self.barTintColor = .navBackground
}
I'm not sure that KinWebBrowser does delegation the same way?
Added the KINWebBrowserDelegate to the class definition and added the following methods:
func webBrowser(_ webBrowser: KINWebBrowserViewController!, didFailToLoad URL: URL!, error: Error!) {
print("DEBUG 5")
}
func webBrowserViewControllerWillDismiss(_ viewController: KINWebBrowserViewController!) {
print("DEBUG 1")
}
func webBrowser(_ webBrowser: KINWebBrowserViewController!, didStartLoading URL: URL!) {
print("DEBUG 2")
}
func webBrowser(_ webBrowser: KINWebBrowserViewController!, didFinishLoading URL: URL!) {
print("DEBUG 3")
}
func didChangeValue<Value>(for keyPath: KeyPath<WebBrowserViewController, Value>) {
print("DEBUG 4")
}
None of the debugs are getting called in the log.
Those are the UIWebView delegate methods, however from the KINWebBrowser page you should probably be using the KINWebBrowserDelegate protocol
(void)webBrowser:(KINWebBrowserViewController *)webBrowser didStartLoadingURL:(NSURL *)URL;
(void)webBrowser:(KINWebBrowserViewController *)webBrowser didFinishLoadingURL:(NSURL *)URL;
(void)webBrowser:(KINWebBrowserViewController *)webBrowser didFailToLoadURL:(NSURL *)URL withError:(NSError *)error;
Related
I have a view controller for a webview, setup through KINWebBrowser.
I've tried several ways to get these methods to execute:
func webViewDidStartLoad(webView: UIWebView) {
print("Strat Loading")
}
func webViewDidFinishLoad(webView: UIWebView) {
print("Finish Loading")
}
func webView(webView: UIWebView, didFailLoadWithError error: NSError?) {
print(error?.localizedDescription)
}
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
return true
}
override func webViewDidFinishLoad(_ webView: UIWebView) {
super.webViewDidFinishLoad(webView)
print("Finish Loading 2")
}
But nothing seems to be working.
My class definition seems normal: class WebBrowserViewController: KINWebBrowserViewController, NavigationProtocol {
But no matter what I do - override func or not, no logger or print statement I put in these functions seems to be executed.
What am I doing wrong? I am trying to create an event that will listen to when all of the web content is loaded so I can stop having a loading spinner appear.
EDIT: Adding delegate information:
When I have tried to add a delegate to viewDidLoad():
webView.delegate = self
I have gotten the following error:
Ambiguous reference to member 'webView(_:decidePolicyFor:decisionHandler:)'
This is my viewDidLoad() without the delegate setting:
override func viewDidLoad() {
super.viewDidLoad()
pointsNavigationItem = addPointsButtonToNavigation()
self.showsURLInNavigationBar = false
self.showsPageTitleInNavigationBar = false;
self.tintColor = UIColor.navText
self.barTintColor = .navBackground
}
I'm not sure that KinWebBrowser does delegation the same way?
Added the KINWebBrowserDelegate to the class definition and added the following methods:
func webBrowser(_ webBrowser: KINWebBrowserViewController!, didFailToLoad URL: URL!, error: Error!) {
print("DEBUG 5")
}
func webBrowserViewControllerWillDismiss(_ viewController: KINWebBrowserViewController!) {
print("DEBUG 1")
}
func webBrowser(_ webBrowser: KINWebBrowserViewController!, didStartLoading URL: URL!) {
print("DEBUG 2")
}
func webBrowser(_ webBrowser: KINWebBrowserViewController!, didFinishLoading URL: URL!) {
print("DEBUG 3")
}
func didChangeValue<Value>(for keyPath: KeyPath<WebBrowserViewController, Value>) {
print("DEBUG 4")
}
None of the debugs are getting called in the log.
Those are the UIWebView delegate methods, however from the KINWebBrowser page you should probably be using the KINWebBrowserDelegate protocol
(void)webBrowser:(KINWebBrowserViewController *)webBrowser didStartLoadingURL:(NSURL *)URL;
(void)webBrowser:(KINWebBrowserViewController *)webBrowser didFinishLoadingURL:(NSURL *)URL;
(void)webBrowser:(KINWebBrowserViewController *)webBrowser didFailToLoadURL:(NSURL *)URL withError:(NSError *)error;
I have xcode 9.1 with swift 4 and i'm unable to authenticate using webview anymore. here is my code :
import UIKit
protocol AuthViewControllerDelegate{
func getAccessToken(_ code: String)
}
class AuthViewController: UIViewController, NSURLConnectionDelegate {
#IBOutlet weak var navItem: UINavigationItem!
#IBOutlet weak var webView: UIWebView!
var delegate: AuthViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
var link: "https://home.nest.com/login/oauth2?client_id=…"
if let encodeURL = link.URLEncodedString(), let url = URL(string: encodeURL) {
webView.loadRequest(URLRequest(url: url))
}
}
override var preferredStatusBarStyle : UIStatusBarStyle {
return .lightContent
}
#IBAction func CancelPressed(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
}
func webView(_ webView: UIWebView, shouldStartLoadWithRequest request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let url = request.url?.absoluteString, url.contains("code=") {
if let code = getQueryStringParameter(url,param: "code"){
self.dismiss(animated: true, completion: { () -> Void in
UIApplication.shared.isNetworkActivityIndicatorVisible = false
self.delegate?.getAccessToken(code)
})
}
}
return true
}
func webViewDidStartLoad(_ webView: UIWebView){
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func webViewDidFinishLoad(_ webView: UIWebView){
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
func getQueryStringParameter(_ url: String?, param: String) -> String? {
if let url = url, let urlComponents = URLComponents(string: url), let queryItems = (urlComponents.queryItems) {
return queryItems.filter({ (item) in item.name == param }).first?.value!
}
return nil
}
}
And here is the error i'm getting in the log :
2017-11-07 20:49:30.836087+0000 TestApp[1851:1259046] TIC TCP Conn
Failed [32:0x1c0365280]: 3:-9800 Err(-9800)
2017-11-07 20:49:30.836472+0000 TestApp[1851:1259046]
NSURLSession/NSURLConnection HTTP load failed
(kCFStreamErrorDomainSSL, -9800)
2017-11-07 20:49:30.836578+0000 TestApp[1851:1259046] Task
<0A675AA1-7110-4FCC-99B2-054380D22F01>.<0> HTTP load failed (error
code: -1200 [3:-9800])
2017-11-07 20:49:30.837548+0000 TestApp[1851:1258708] NSURLConnection
finished with error - code -1200
And i already have this into my .plist file :
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
NB: The same code was working perfectly with swift 3.2
Check if your htpps:// is right, had the same problem, fixed after changing url.
Adding NSAllowsArbitraryLoads into .plist file resolves the error
NSURLConnection error - code - 1200
where my development language is Objective C.
I'm trying to load Javascript synchronously in my WKWebView. From my research it looks like the evaluateJavaScript method is asynchronous and this is causing errors because the page would load before the JS that overrides a lot of behavior (it only works a portion of the time because of the async nature).
I've tried this but it doesn't seem to work :
extension WKWebView {
func evaluateJavaScriptSynchronously(script: String, completion: () -> Void) {
let semaphore = dispatch_semaphore_create(1)
evaluateJavaScript(script) { (result, error) in
if error == nil {
if result != nil {
print("SUCCESS")
completion()
}
} else {
print("ERROR!!!")
print(error)
print(result)
completion()
}
dispatch_semaphore_signal(semaphore)
}
let timeout = dispatch_time_t(10000)
dispatch_semaphore_wait(semaphore, timeout)
}
}
I know it's not working because the page still sometimes loads as if the JS wasn't there. Is there a similar thing I can do to synchronize the process?
You can define a WKNavigationDelegate to get notified when the page into the WKWebView has been loaded
class Controller: UIViewController, WKNavigationDelegate {
#IBOutlet var webView: WKWebView!
override func viewDidLoad() {
self.view = webView
webView.navigationDelegate = self
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
print("page loaded")
// <--- call your method from here
}
}
You can now call your method from within webView(wevView:didFinishNavigation:)
I'm very new to developing. I'm building a WebView and I want it to have pull to refresh capabilities. How do I go about doing that in swift?
My code in the View Controller is
#IBOutlet var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
func webViewDidFinishLoad(webView: UIWebView) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
func webViewDidStartLoad(webView: UIWebView) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
}
let url = NSURL (string: "https://www.foo.com");
let requestObj = NSURLRequest(URL: url!);
webView.loadRequest(requestObj);
NSUserDefaults.standardUserDefaults().registerDefaults(["UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
Your code is buggy you added two Web view Delegate in side the ViewDidLoad method that must be following and check the following viewDidLoad code for adding Pull to Refresh in web view:
#IBOutlet var webView: UIWebView!
var refController:UIRefreshControl = UIRefreshControl()
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL (string: "https://www.foo.com");
let requestObj = NSURLRequest(URL: url!);
webView.loadRequest(requestObj);
NSUserDefaults.standardUserDefaults().registerDefaults(["UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"])
refController.bounds = CGRectMake(0, 50, refController.bounds.size.width, refController.bounds.size.height)
refController.addTarget(self, action: Selector("mymethodforref:"), forControlEvents: UIControlEvents.ValueChanged)
refController.attributedTitle = NSAttributedString(string: "Pull to refresh")
webView.scrollView.addSubview(refController)
}
func mymethodforref(refresh:UIRefreshControl){
webView.reload()
refController.endRefreshing()
}
func webViewDidFinishLoad(webView: UIWebView) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
func webViewDidStartLoad(webView: UIWebView) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Details
Xcode 11.3.1, Swift 5.1
Solution
extension WKWebView {
var refreshControl: UIRefreshControl? { (scrollView.getAllSubviews() as [UIRefreshControl]).first }
enum PullToRefreshType {
case none
case embed
case custom(target: Any, selector: Selector)
}
func setPullToRefresh(type: PullToRefreshType) {
(scrollView.getAllSubviews() as [UIRefreshControl]).forEach { $0.removeFromSuperview() }
switch type {
case .none: break
case .embed: _setPullToRefresh(target: self, selector: #selector(webViewPullToRefreshHandler(source:)))
case .custom(let params): _setPullToRefresh(target: params.target, selector: params.selector)
}
}
private func _setPullToRefresh(target: Any, selector: Selector) {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(target, action: selector, for: .valueChanged)
scrollView.addSubview(refreshControl)
}
#objc func webViewPullToRefreshHandler(source: UIRefreshControl) {
guard let url = self.url else { source.endRefreshing(); return }
load(URLRequest(url: url))
}
}
// https://stackoverflow.com/a/47282118/4488252
extension UIView {
class func getAllSubviews<T: UIView>(from parenView: UIView) -> [T] {
return parenView.subviews.flatMap { subView -> [T] in
var result = getAllSubviews(from: subView) as [T]
if let view = subView as? T { result.append(view) }
return result
}
}
func getAllSubviews<T: UIView>() -> [T] { return UIView.getAllSubviews(from: self) as [T] }
}
Usage
Setup
import UIKit
import WebKit
class ViewController2: UIViewController {
private weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
// ... create webView here
webView.navigationDelegate = self
}
}
// MARK: WKNavigationDelegate
extension ViewController2: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.refreshControl?.endRefreshing()
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
webView.refreshControl?.endRefreshing()
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
webView.refreshControl?.endRefreshing()
}
}
Activate pull to refresh (option 1)
webView.setPullToRefresh(type: .embed)
Activate pull to refresh (option 2)
#objc func customPullToRefreshHandler(source: UIRefreshControl) {
guard let url = webView.url else { source.endRefreshing(); return }
webView.load(URLRequest(url: url))
}
// ....
webView.setPullToRefresh(type: .custom(target: self, selector: #selector(customPullToRefreshHandler(source:))))
Deactivate pull to refresh
webView.setPullToRefresh(type: .none)
Full sample
Do not forget to paste here the solution code
import UIKit
import WebKit
class ViewController: UIViewController {
private weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
createWebView()
webView.load("https://google.com")
// Way 1
webView.setPullToRefresh(type: .embed)
// Way 2
// webView.setPullToRefresh(type: .custom(target: self, selector: #selector(customPullToRefreshHandler(source:))))
}
private func createWebView() {
let webView = WKWebView(frame: .zero, configuration: .init())
view.addSubview(webView)
webView.navigationDelegate = self
self.webView = webView
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: webView.bottomAnchor).isActive = true
view.rightAnchor.constraint(equalTo: webView.rightAnchor).isActive = true
}
// #objc func customPullToRefreshHandler(source: UIRefreshControl) {
// guard let url = webView.url else { source.endRefreshing(); return }
// webView.load(URLRequest(url: url))
// print("WebView started loading")
// }
}
// MARK: WKNavigationDelegate
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.refreshControl?.endRefreshing()
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
webView.refreshControl?.endRefreshing()
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
webView.refreshControl?.endRefreshing()
}
}
extension WKWebView {
func load(_ urlString: String) {
guard let url = URL(string: urlString) else { return }
load(URLRequest(url: url))
}
}
Following code will put the refresh control in scrollview of webview.Initially it will load google.com. To see pull to refresh clearly I have set white color to the background of scroll view so it is clearly visible and on pull to refresh webview opens facebook page.
class ViewController: UIViewController,UIWebViewDelegate {
#IBOutlet weak var webView: UIWebView!
var refreshControl:UIRefreshControl?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.refreshControl = UIRefreshControl.init()
refreshControl!.addTarget(self, action:#selector(refreshControlClicked), for: UIControlEvents.valueChanged)
self.webView.scrollView.addSubview(self.refreshControl!)
self.webView.scrollView.backgroundColor = UIColor.white
self.webView.delegate = self
let url:URL = URL.init(string:"https://www.google.com")!
self.loadRequestWithUrl(URLRequest.init(url: url))
}
func webViewDidStartLoad(_ webView: UIWebView) {
NSLog("website loaded")
}
func loadRequestWithUrl(_ urlRequest : URLRequest?){
if(urlRequest != nil){
self.webView.loadRequest(urlRequest!)
}
}
func refreshControlClicked(){
let url:URL = URL.init(string:"https://www.facebook.com")!
self.loadRequestWithUrl(URLRequest.init(url: url))
}
}
You can use this one
Write this in DidLoad method
webViewHome.scrollView.delegate = self;
And then write this method
#pragma mark Scrollview delegate
(void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset{
if (scrollView.contentOffset.y < 0){
DLog(#" webview on top ");
[self AgainCheckInternet:nil];
}
}
Note: Please make sure you didn't set scrollview bounces property to No otherwise this method will not call
Anyone have an idea why my activity indicator is not stopping when the webview has finished loading? Web view is delegated, UIActivityIndicatorView start animating etc, is in my code... ?
This is the relevant code:
import UIKit
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var webview: UIWebView!
#IBOutlet weak var activity: UIActivityIndicatorView!
var url = "http://apple.com"
func loadURL () {
let requestURL = NSURL(string: url)
let request = NSURLRequest(URL: requestURL!)
webview.loadRequest(request)
}
override func viewDidLoad() {
super.viewDidLoad()
loadURL()
// Do any additional setup after loading the view, typically from a nib.
webview.delegate = self;
}
func webviewDidStartLoad(_ : UIWebView){
activity.startAnimating()
NSLog("The webview is starting to load")
}
func webviewDidFinishLoad(_ : UIWebView){
activity.stopAnimating()
activity.hidden=true;
NSLog("The webview is done loading")
}
Thanks!
Mind the functions names! It's webView..., camel case :)
Are you getting "The webview is done loading"?. If not, probably the request it's taking a very long time to complete it.
However, you should implement func webView(webView: UIWebView, didFailLoadWithError error: NSError?) to detect if there's any error and stop the activity.
Actually, the correct answer is a probably combination of DAN's and iGongora's, plus a possible third reason.
The immediate problem is that even in a happy path scenario (where the webview finishes successfully) is that it should be the following (pay attention to the case of the function names webViewDidStartLoad and webViewDidFinishLoad
func webViewDidStartLoad(webView: UIWebView) {
activity.startAnimating()
NSLog("The webview is starting to load")
}
func webViewDidFinishLoad(webView: UIWebView) {
activity.stopAnimating()
activity.hidden=true;
NSLog("The webview is done loading")
}
Additionally, if the UIWebView fails, you should also stop the spinner.
func webView(webView: UIWebView!, didFailLoadWithError error: NSError!) {
activity.stopAnimating()
activity.hidden=true;
print("Webview fail with error \(error)");
}
One other possible problem would be if you have not set the delegate properly. You can do it in the viewDidLoad, or in Interface Builder.
override func viewDidLoad() {
super.viewDidLoad()
...
self.webView.delegate = self;
...
}
Run this in main queue so this will stop other wise not the code for this swift 4 and swift 3 is
Dispatchqueue.main.async{
activity.stopAnimating()
}