Webview Back & Forward Button Not working (Swift, Xcode 14) - ios

My webView Back & Forward buttons used to work but they are not anymore. I've looked everywhere I could for a solution but only found similar ones including here but nothing works for me or for Swift (at least to me). Below are my codes.I have Webkit Framework added, still not working when the buttons did work before even without adding the Framework. App Transport Security setting is set to YES for Arbitrary Load as before when the buttons were working. Did Apple change something? Please help. Thanks!!!
import UIKit
import WebKit
class SocialPlatformController: UIViewController, WKNavigationDelegate {
#IBOutlet weak var BackBtn: UIButton!
#IBOutlet weak var ForwardBtn: UIButton!
#IBOutlet weak var webView: WKWebView!
#IBAction func backToMainScreen(_ sender: Any) {
}
#IBAction func goBackBtnTap(_ sender: Any) {
if webView.canGoBack {
webView.goBack()
}
}
#IBAction func goForwardBtnTap(_ sender: Any) {
if webView.canGoForward {
webView.goForward()
}
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Swift.Void) {
BackBtn.isEnabled = webView.canGoBack
ForwardBtn.isEnabled = webView.canGoForward
guard
let response = navigationResponse.response as? HTTPURLResponse,
let url = navigationResponse.response.url
else {
decisionHandler(.cancel)
return
}
if let headerFields = response.allHeaderFields as? [String: String] {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
cookies.forEach { (cookie) in
HTTPCookieStorage.shared.setCookie(cookie)
}
}
decisionHandler(.allow)
}
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
let url:URL = URL(string:"https://www.facebook.com/groups/246328283932460")!
let urlRequest:URLRequest = URLRequest(url: url)
webView.load(urlRequest)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}

Perhaps it's better to handle the Back and Forward button state in a navigation delegate handler that's fired after the navigation has been finished, for example:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)

//successful load
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
updateButtons()
}
//failure load
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
updateButtons()
}
//back-forward button update
func updateButtons() {
backButton.isEnabled = webView.canGoBack
forwardbutton.isEnabled = webView.canGoForward
}
You can follow these delegates and methods. I think buttons are not getting updated/loaded as they should.

Related

Remove context menu from the swift 4.0 webview

I want to disable the context menu ie any action to maintain an element of my WKWebView swift 4
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
// 1
let url = URL(string: "https://www.google.com")!
webView.load(URLRequest(url: url))
// 2
let refresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: webView, action: #selector(webView.reload))
toolbarItems = [refresh]
navigationController?.isToolbarHidden = false
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
title = webView.title
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
//show progress indicator
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
//show error
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
//show error
}
private weak var lastPresentedController : UIViewController?
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
// WKWebView actions sheets workaround
if presentedViewController != nil && lastPresentedController != presentedViewController {
lastPresentedController = presentedViewController;
presentedViewController?.dismiss(animated: flag, completion: {
completion?();
self.lastPresentedController = nil;
});
} else if lastPresentedController == nil {
super.dismiss(animated: flag, completion: completion);
}
}
}

how to hide html elements in wkwebview in IOS?

i want to hide certain html elements in wkwebview using swift 4, xcode 9. i know how to do it in android and have added the code below. i just need an equivalent code for ios in swift 4 for wkwebView.
myWebView.setWebViewClient(new WebViewClient()
{
#Override
public void onPageFinished(WebView view, String url)
{
myWebView.loadUrl("javascript:(function() { " +
"document.getElementById('divHeader')
[0].style.display='none'; " +
"})()");
}
});
myWebView.loadUrl(URL);
Use WKWebView
import WebKit
class FirstViewController: UIViewController, WKNavigationDelegate {
#IBOutlet weak var webVw: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webVw.navigationDelegate = self
loadmyPage(htmlstr: "https://www.mywebsite.com")
}
func loadmyPage(htmlstr:String){
let url = URL (string: htmlstr)
let request = URLRequest(url: url!)
webVw.load(request)
}
//MARK:- WKNavigationDelegate
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print(error.localizedDescription)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print("Start to load")
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("finish to load")
let elementID = "right_sidebar"
let removeElementIdScript = "var element = document.getElementById('\(elementID)'); element.parentElement.removeChild(element);"
webView.evaluateJavaScript(removeElementIdScript) { (response, error) in
debugPrint("Am here")
}
}

create activity indicator when navigate in webView using webkit in swift 4

I want to create a wkwebview in my iOS app, and I want to add an activity indicator to it. I want when we click all the button or part in the webview, it will appear the activity indicator. Can you give me some code to do this? Here's my code right now. It only show the indicator when start load the website but when we click at the process, the indicator not appear.
import UIKit
import WebKit
class ViewController: UIViewController,WKNavigationDelegate,UIWebViewDelegate {
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var webView: WKWebView!
#IBOutlet var containerView: UIView? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.activityIndicator)
self.view.addSubview(self.webView)
let url:URL = URL(string : "https://www.facebook.com/")!
let urlRequest : URLRequest = URLRequest(url: url)
webView.load(urlRequest)
//activity indicator
self.webView.addSubview(self.activityIndicator)
self.activityIndicator.startAnimating()
self.webView.navigationDelegate = self
self.activityIndicator.hidesWhenStopped = true
//refresh
webView.scrollView.bounces = true
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(ViewController.refreshWebView), for: UIControlEvents.valueChanged)
webView.scrollView.addSubview(refreshControl)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear( animated )
let url:URL = URL(string : "https://www.facebook.com/")!
let urlRequest : URLRequest = URLRequest(url: url)
webView.load(urlRequest)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
activityIndicator.stopAnimating()
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
activityIndicator.stopAnimating()
}
#objc func refreshWebView(sender: UIRefreshControl) {
print("refersh")
sender.endRefreshing()
}
}
Below code will help to add activity indicator in Navigation Bar. So no need to create any subviews. This works with Swift 4.
import Foundation
import UIKit
import WebKit
class ContactVC: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
var activityIndicator: UIActivityIndicatorView!
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
activityIndicator.hidesWhenStopped = true
let barButton = UIBarButtonItem(customView: activityIndicator)
self.navigationItem.setRightBarButton(barButton, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://www.facebook.com/")!
webView.load(URLRequest(url: url))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func showActivityIndicator(show: Bool) {
if show {
// Start the loading animation
activityIndicator.startAnimating()
} else {
// Stop the loading animation
activityIndicator.stopAnimating()
}
}
//MARK:- WKNavigationDelegate
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print("Start to load")
showActivityIndicator(show: true)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("Finish to load")
title = webView.title
showActivityIndicator(show: false)
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print(error.localizedDescription)
showActivityIndicator(show: false)
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print(error.localizedDescription)
showActivityIndicator(show: false)
}
}
Implement didStartProvisionalNavigation delegate function.
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
activityIndicator.startAnimating()
}

WKWebView goBack requiring two invocations

I'm working on a iOS app that uses WKWebView, with custom navigation buttons. We're finding that following some links will require using our Back button twice, as the first invocation seems to just reload the current page. This does not happen in mobile Safari.
What could cause goBack() to need to be called twice to actually navigate back one page, while it works correctly in Safari? Are there changes I can make in the app to correct the issue?
Note: I cannot make changes to the web site's content or structure, so any fixes would have to be in the app.
Update:
It appears the site is using JavaScript for some of its content loads. The observeValue method is not invoked at all when this happens and is probably the culprit.
import UIKit
import WebKit
import SafariServices
class LandingPageViewController: UIViewController, SFSafariViewControllerDelegate, WKUIDelegate, WKNavigationDelegate {
#IBOutlet var urlNavigationItem: UINavigationItem!
#IBOutlet var backButton: UIBarButtonItem!
#IBOutlet var forwardButton: UIBarButtonItem!
#IBOutlet var reloadButton: UIBarButtonItem!
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView()
view.addSubview(webView)
webView.addObserver(self, forKeyPath: "loading", options: .new, context: nil)
let request = URLRequest(url: url) //url is defined elsewhere
webView.navigationDelegate = self
webView.load(request)
webView.allowsBackForwardNavigationGestures = true
webView.uiDelegate = self
backButton.isEnabled = false
forwardButton.isEnabled = false
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if(keyPath == "loading"){
backButton.isEnabled = webView.canGoBack
forwardButton.isEnabled = webView.canGoForward
}
}
#IBAction func back(_ sender: UIBarButtonItem) {
webView.goBack()
}
#IBAction func forward(_ sender: UIBarButtonItem){
webView.goForward()
}
#IBAction func reload(_ sender: UIBarButtonItem) {
let request = URLRequest(url: webView.url!)
webView.load(request)
}
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
// Custom logic here
}
}
try with following delegate methods,
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
decisionHandler(.allow)
}
You have implemented one but not call decisionHandler(.allow)
I was facing the same issue. What worked for me was stopping the navigation and then go back.
func goBack() {
self.webView.stopLoading()
if (self.webView.canGoBack) {
self.webView.goBack()
return;
}
}

Displaying activity indicator on WKWebView using swift

I am working on the following code and trying to show an activity indicator in the view whilst the page is loading..
I tried to implement the WKNavigationDelegate methods but I am failing as nothing shows.
Any suggestions on how to fix this?
I am not setting the SupportWebView view delegate anywhere but I wouldn't know how to do it in swift..
import UIKit
import WebKit
class SupportWebView: UIViewController, WKNavigationDelegate {
#IBOutlet var containerView : UIView? = nil
var webView: WKWebView?
override func loadView() {
super.loadView()
self.webView = WKWebView()
self.view = self.webView
}
override func viewDidLoad() {
super.viewDidLoad()
var dataManager = DataManager.sharedDataManager()
var url = dataManager.myValidURL
var req = NSURLRequest(URL:url!)
self.webView!.loadRequest(req)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
}
As commented, you forgot to set the webView delegate:
override func loadView() {
super.loadView()
self.webView = WKWebView()
self.webView.navigationDelegate = self
self.view = self.webView
}
You should use the delegate methods for all other purposes, but key path monitoring works fine for this one purpose.
Here is a Swift 4 implementation that works fine.
// Somewhere in your view controller
private var loadingObservation: NSKeyValueObservation?
private lazy var loadingIndicator: UIActivityIndicatorView = {
let spinner = UIActivityIndicatorView()
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.color = .black
return spinner
}()
override func viewDidLoad() {
super.viewDidLoad()
// Setup...
loadingObservation = webView.observe(\.isLoading, options: [.new, .old]) { [weak self] (_, change) in
guard let strongSelf = self else { return }
// this is fine
let new = change.newValue!
let old = change.oldValue!
if new && !old {
strongSelf.view.addSubview(strongSelf.loadingIndicator)
strongSelf.loadingIndicator.startAnimating()
NSLayoutConstraint.activate([strongSelf.loadingIndicator.centerXAnchor.constraint(equalTo: strongSelf.view.centerXAnchor),
strongSelf.loadingIndicator.centerYAnchor.constraint(equalTo: strongSelf.view.centerYAnchor)])
strongSelf.view.bringSubview(toFront: strongSelf.loadingIndicator)
}
else if !new && old {
strongSelf.loadingIndicator.stopAnimating()
strongSelf.loadingIndicator.removeFromSuperview()
}
}
}
Please, below code which is working fine[Swift 4.2].
#IBOutlet weak var wv: WKWebView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
loadYoutube(videoID: "KqNS7uAvOxk")
}
Now load Youtube Video
func loadYoutube(videoID:String) {
guard let youtubeURL = URL(string: "https://www.youtube.com/embed/\(videoID)")
else { return }
wv.load( URLRequest(url: youtubeURL) )
wv.navigationDelegate = self
}
Implement below this function:
func showActivityIndicator(show: Bool) {
if show {
activityIndicator.startAnimating()
} else {
activityIndicator.stopAnimating()
}
}
Implement below these three delegate method:
func webView(_ webView: WKWebView, didFinish navigation:
WKNavigation!) {
showActivityIndicator(show: false)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation
navigation: WKNavigation!) {
showActivityIndicator(show: true)
}
func webView(_ webView: WKWebView, didFail navigation:
WKNavigation!, withError error: Error) {
showActivityIndicator(show: false)
}
Let me know if it is not working.

Resources