How do I dismiss a progressView when it's fully loaded? - ios

I have a progress view that tracks the status of a Youtube video loading via WKWebView. When it's loaded, I want to dismiss the progressView because when I click back onto the main page of my app, the status bar is still there (but it doesn't display anything and takes up space, blocking other elements of my app). How do I, when the progressView is completely loaded, dismiss the progressView?
This is my class that deals with the WKWebView.
import UIKit
import WebKit
class VideoPlayViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
var progressView: UIProgressView!
var videoURL: String = ""
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: videoURL)!
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
//Progress View/ Refresh Button
progressView = UIProgressView(progressViewStyle: .default)
progressView.sizeToFit()
let progressButton = UIBarButtonItem(customView: progressView)
progressView.progressTintColor = UIColor(red: 254, green: 53, blue: 98)
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let refresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: webView, action: #selector(webView.reload))
refresh.tintColor = UIColor(red: 254, green: 53, blue: 98)
toolbarItems = [progressButton, spacer, refresh]
navigationController?.isToolbarHidden = false
// Lets the progress bar change according to what the Observer sends back (# from 0-1)
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "estimatedProgress" {
progressView.progress = Float(webView.estimatedProgress)
}
}
}
If you need more info / more code, let me know.

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath else { return }
switch keyPath {
case "estimatedProgress":
// If you are using a `UIProgressView`, this is how you update the progress
progressView.isHidden = webView.estimatedProgress == 1
progressView.progress = Float(webView.estimatedProgress)
default:
break
}
}
and don't forgot to deinitialize the observers
deinit {
//remove all observers
webView.removeObserver(self, forKeyPath: "estimatedProgress")
}

Check webView(_:didFinish:) method of WKNavigationDelegate

You have to set typeable:
public typealias isCompletion = (_ isCompleted: Bool?) -> Void
And add variables like this in class:
var completion: isCompletion?
Now you have to create a method :
func webViewLoad(_ iscompleted:#escaping isCompletion){
completion = iscompleted
let url = URL(string: videoURL)!
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
}
You have to set completion in the delegate method
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
print("finish to load")
completion(true)
}
You can call this method from viewdidload like this:
// show loading from here
webViewLoad { (flag) in
//hide loading here
}

Related

Identify when AVPlayerViewController is closed by tapping X button

Is there any method to identify when we close the AVPlayerViewController when the video is still playing?
Attached the image as to which 'Close' button I am referring to.
try this :-
import UIKit
import AVKit
import AVFoundation
class VC: UIViewController {
var video_Url:String = String()
let playerController = AVPlayerViewController()
//MARK:- ViewDidload
override func viewDidLoad() {
super.viewDidLoad()
let player = AVPlayer(url: URL(string: video_Url)!)
playerController.player = player
self.present(playerController, animated: false) {
player.play()
self.playerController.addObserver(self, forKeyPath: #keyPath(UIViewController.view.frame), options: [.old, .new], context: nil)
}
}
override func viewDidDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(NSNotification.Name.AVPlayerItemDidPlayToEndTime)
}
//MARK:- All Method
func playerDidFinishPlaying(note: NSNotification) {
self.navigationController?.popViewController(animated: false)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
self.playerController.removeObserver(self, forKeyPath: #keyPath(UIViewController.view.frame))
self.navigationController?.popViewController(animated: false)
}
}

How to open web view on button click in SwiftUI?

Here is my WebView
struct Webview: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> WebviewController {
let webviewController = WebviewController()
let request = URLRequest(url: self.url, cachePolicy: .returnCacheDataElseLoad)
webviewController.webview.load(request)
return webviewController
}
func updateUIViewController(_ webviewController: WebviewController, context: Context) {
//
}
}
class WebviewController: UIViewController, WKNavigationDelegate {
lazy var webview: WKWebView = WKWebView()
lazy var progressbar: UIProgressView = UIProgressView()
deinit {
self.webview.removeObserver(self, forKeyPath: "estimatedProgress")
self.webview.scrollView.removeObserver(self, forKeyPath: "contentOffset")
}
override func viewDidLoad() {
super.viewDidLoad()
self.webview.navigationDelegate = self
self.view.addSubview(self.webview)
self.webview.frame = self.view.frame
self.webview.translatesAutoresizingMaskIntoConstraints = false
self.view.addConstraints([
self.webview.topAnchor.constraint(equalTo: self.view.topAnchor),
self.webview.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
self.webview.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
self.webview.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
])
self.webview.addSubview(self.progressbar)
self.setProgressBarPosition()
webview.scrollView.addObserver(self, forKeyPath: "contentOffset", options: .new, context: nil)
self.progressbar.progress = 0.1
webview.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
}
func setProgressBarPosition() {
self.progressbar.translatesAutoresizingMaskIntoConstraints = false
self.webview.removeConstraints(self.webview.constraints)
self.webview.addConstraints([
self.progressbar.topAnchor.constraint(equalTo: self.webview.topAnchor, constant: self.webview.scrollView.contentOffset.y * -1),
self.progressbar.leadingAnchor.constraint(equalTo: self.webview.leadingAnchor),
self.progressbar.trailingAnchor.constraint(equalTo: self.webview.trailingAnchor),
])
}
// MARK: - Web view progress
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
switch keyPath {
case "estimatedProgress":
if self.webview.estimatedProgress >= 1.0 {
UIView.animate(withDuration: 0.3, animations: { () in
self.progressbar.alpha = 0.0
}, completion: { finished in
self.progressbar.setProgress(0.0, animated: false)
})
} else {
self.progressbar.isHidden = false
self.progressbar.alpha = 1.0
progressbar.setProgress(Float(self.webview.estimatedProgress), animated: true)
}
case "contentOffset":
self.setProgressBarPosition()
default:
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
}
And here is my button in the Content view.
Button(action: {
Webview(url: URL(string:"https://www.google.com")!)
})
{
Image("go")
}
}
Actually, when I am clicking it, nothing happens but when I move it outside of the button, it opens automatically.
My question is, how to open WebView on the button click, I am very new to SwiftUI.
Thank you.
You are calling in the wrong way.
Use this
struct ContentView: View {
#State private var isShowingWebView: Bool = false
var body: some View {
Button(action: {
isShowingWebView = true
// Webview(url: URL(string:"https://www.google.com")!) <<-- Remove
})
{
Text("go")
}
.sheet(isPresented: $isShowingWebView) {
Webview(url: URL(string:"https://www.google.com")!)
}
}
}

Load WKWebView in background/off screen

Is there a way to load a WKWebView in the background/off-screen so I can hide the loading behind another view?
You can add the WKWebView to the view hierarchy but its x is your current width, so it lays out of the screen but within the rendering hierarchy.
Add WKNavigationDelegate to your class and add it to the webView like
webView.navigationDelegate = self
Then implement the following function:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation)
This function and some others are called when the webview finished loading the content, BUT this does not include the 100% finished rendering. After that there is still some time required for rendering, the time consumption for this depends on the content that was loaded.
There currently is no callback for the 100% finished loading and rendering, so if you know the files, you can calculate or use a fix delay before moving the webView into the visible rect.
OR
If you feel fine with that, you observe private values in the webview and move your webview after those value changes to your preferred state. This looks for example like that:
class MyCustomWKWebView: WKWebView {
func setupWebView(link: String) {
let url = NSURL(string: link)
let request = NSURLRequest(url: url! as URL)
load(request as URLRequest)
addObserver(self, forKeyPath: "loading", options: .new, context: nil)
}
override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let _ = object as? WKWebView else { return }
guard let keyPath = keyPath else { return }
guard let change = change else { return }
switch keyPath {
case "loading":
if let val = change[NSKeyValueChangeKey.newKey] as? Bool {
//do something!
}
default:
break
}
}
deinit {
removeObserver(self, forKeyPath: "loading")
}
}

How to know when my WKWebview derived subview is loaded in Swift

I have a view, which loads a subview after certain actions have happened, within the viewDidLoad() method:
override func viewDidLoad() {
super.viewDidLoad()
//OTHER STUFF...
let config = WKWebViewConfiguration()
config.userContentController = contentController
self.myWebView = WKWebView(
frame: self.containerView.bounds,
configuration: config
)
self.myWebView.navigationDelegate = self
self.view.addSubview(self.myWebView)
}
I need to do some checks once the web view has loaded. How can I do something like:
webSubviewDidLoad() {
//do stuff
}
there is a specific delegate didFinishNavigation
didFinishNavigation documentation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let configuration = WKWebViewConfiguration()
let webView = WKWebView(frame: .zero, configuration: configuration)
webView.translatesAutoresizingMaskIntoConstraints = false
webView.navigationDelegate = self
view.addSubview(webView)
/* add layout constraints that make the webview fitting in your view */
if let url = URL(string: "https://google.com") {
webView.load(URLRequest(url: url))
}
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("Finished navigating to url \(webView.url)")
}
}
or you can check the estimatedProgress property for the progress
estimatedProgress documentation
Step 1. Add an observer to be notified when your web view finishes loading like below. This code should be placed within viewDidLoad method of your view controller class.
myWebView.addObserver(self, forKeyPath: #keyPath(WKWebView.isLoading), options: .new, context: nil)
Step 2. Implement the observeValue(forKeyPath:) method in your view controller, doing whatever actions you need to do like below.
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "isLoading" && !myWebView.isLoading {
// Do your needed action here
}
}
Implement your own WebView:
import WebKit
protocol WebViewDelegate {
func didLayoutSubviews()
}
class WebView : WKWebView {
weak var delegate: WebViewDelegate?
override func layoutSubviews() {
super.layoutSubviews()
self.delegate?.didLayoutSubviews()
}
}

WKWebView on link click listener?

Does there exist something like a onLinkClickListener in the WKWebView class? I tried googling it but found nothing, I also found some unanswered questions on stackoverflow of simillar type.
The reason I need a linkClickListener is that, when I click on a link and the page did not load yet, it does not load the website. I also could create a fancy loading screen, when the page is loading with the listener.
You can do it like this
add WKNavigationDelegate to your class
class ViewController: UIViewController, WKNavigationDelegate
set a navigation delegate
yourWKWebview.navigationDelegate = self
after that you will be able to use decidePolicyFor navigationAction function
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.navigationType == WKNavigationType.linkActivated {
print("link")
decisionHandler(WKNavigationActionPolicy.cancel)
return
}
print("no link")
decisionHandler(WKNavigationActionPolicy.allow)
}
Here is the solution you were looking for
Original answer from Bala: https://stackoverflow.com/a/44408807/8661382
Create WkNavigationDelegate to your class:
class ViewController: UIViewController, WKNavigationDelegate {
}
Override the method loadView and add an observer like this:
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
webView.addObserver(self, forKeyPath: "URL", options: [.new, .old], context: nil)
view = webView
}
In viewDidLoad add an url to your webView.:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(false, animated: true)
let url = URL(string: "https://www.hackingwithswift.com")!
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
}
Finally, most importantly override observerValue method like this:
override func observeValue(forKeyPath keyPath: String?, of object: Any?,
change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? Int, let oldValue = change?
[.oldKey] as? Int, newValue != oldValue {
//Value Changed
print(change?[.newKey] as Any)
}else{
//Value not Changed
print(change?[.oldKey] as Any)
}
}
This will print the link you click on webView before loading the link.

Resources