4

I'm writing a webapp in swift with xcode. I've a question "How can I add a progressbar that shows me the loading of each page?"

@IBOutlet var webView: UIWebView!
override func viewDidLoad() {
   super.viewDidLoad()
   let url = NSURL(string: "http://stackoverflow.com")
   let request = NSURLRequest(URL: url)
   webView.loadRequest(request)
}

(Sorry for my english)

Mirko Brombin
  • 1,002
  • 3
  • 12
  • 34

5 Answers5

16

You can find a very good answer in this post. You can just add a progress bar as a subview to your webview. The main problem is the accuracy of the progress bar. The proposed answer is to begin by animating it constantly, block it at 95% when still loading and when your request is complete, zip it all the way to 100%.

Here's a solution in Swift:

Add these properties:

//Add this progress view via Interface Builder (IBOutlet) or programatically
let myProgressView: UIProgressView

var theBool: Bool
var myTimer: NSTimer

These functions will fill the progress view. You can play with the parameters:

func funcToCallWhenStartLoadingYourWebview() {
    self.myProgressView.progress = 0.0
    self.theBool = false
    self.myTimer = NSTimer.scheduledTimerWithTimeInterval(0.01667, target: self, selector: "timerCallback", userInfo: nil, repeats: true)
}

func funcToCallCalledWhenUIWebViewFinishesLoading() {
    self.theBool = true
}

func timerCallback() {
    if self.theBool {
        if self.myProgressView.progress >= 1 {
            self.myProgressView.hidden = true
            self.myTimer.invalidate()
        } else {
            self.myProgressView.progress += 0.1
        }
    } else {
        self.myProgressView.progress += 0.05
        if self.myProgressView.progress >= 0.95 {
            self.myProgressView.progress = 0.95
        }
    }
}
Community
  • 1
  • 1
Starscream
  • 1,128
  • 1
  • 9
  • 22
  • Thanks for the answer but i need it for swift, how i can use that code with my swift-app? – Mirko Brombin Jan 26 '15 at 09:15
  • Hey I added some code. You just need to add the progressview to your UI. – Starscream Jan 26 '15 at 09:43
  • Where i add these? var theBool: Bool var myTimer: NSTimer if i add these on class ViewController i get the error: class ViewController have not initializer. – Mirko Brombin Jan 26 '15 at 10:07
  • It's because if you add these to a class they need to be initialized at init time. `var theBool: Bool = false var myTimer = NSTimer()` – Starscream Jan 26 '15 at 10:36
  • Another option, rather than defining `funcToCallCalledWhenUIWebViewFinishesLoading()`, is to implement UIWebViewDelegate in the controller and to override the `webViewDidFinishLoad` function. You will also have to set the delegate of the webview to the controller for this to work. – Achintya Ashok Jun 26 '17 at 03:31
  • I was wondering why it only appears once: there is `self.myProgressView.isHidden = false`missing in `funcToCallWhenStartLoadingYourWebview` – FrugalResolution Aug 13 '20 at 13:46
4
    var theBool: Bool
    var myTimer: NSTimer
    var didFinishTimer: NSTimer


    required init(coder aDecoder: NSCoder) {
        self.theBool = false
        self.myTimer = NSTimer()
        self.didFinishTimer = NSTimer()

        super.init(coder: aDecoder)
    }
           func startAnimatingProgressBar() {
            self.theBool = false
            myProgressView.hidden = false
            myProgressView.alpha = 0
            UIView.animateWithDuration(0.2, animations: { () -> Void in
                self.myProgressView.alpha = 0.6
            })
            self.myProgressView.progress = 0.0

            //Tweek this number to alter the main speed of the progress bar
            var number = drand48() / 80;
//            println("startAnimatingProgressBar|\(number)")

            self.myTimer = NSTimer.scheduledTimerWithTimeInterval(number, target: self, selector: "timerCallback", userInfo: nil, repeats: true)
//            println("myTimer|\(myTimer)")
    }

    func finishAnimatingProgressBar() {
        self.theBool = true
    }

    func timerCallback() {
        if self.theBool {
            if self.myProgressView.progress >= 1 {
                UIView.animateWithDuration(0.2, animations: { () -> Void in
                    self.myProgressView.alpha = 0

//                    }, completion: { (success:Bool) -> Void in
//                        self.myProgressView.hidden = true
                })
                self.myTimer.invalidate()
            } else {
                //Loaded and zoom to finish
                var number = drand48() / 40
//                println("finished:\(number)")

                self.myProgressView.progress += Float(number)
            }

        } else {
            //Start slow
            if self.myProgressView.progress >= 0.00 && self.myProgressView.progress <= 0.10 {
                var number = drand48() / 8000;
//                println("Start:\(number)")

                self.myProgressView.progress += Float(number)
                //Middle speed up a bit
            } else if self.myProgressView.progress >= 0.10 && self.myProgressView.progress <= 0.42 {
                var smallerNumber = drand48() / 2000;
                self.myProgressView.progress += Float(smallerNumber)
//                println("Middle:\(smallerNumber)")

                //slow it down again
            } else if myProgressView.progress >= 0.42 && self.myProgressView.progress <= 0.80 {
                var superSmallNumber = drand48() / 8000;
                self.myProgressView.progress += Float(superSmallNumber)
//                println("slow it down:\(superSmallNumber)")

                //Stop it
            } else if myProgressView.progress == 0.80 {
                println("Stop:\(myProgressView.progress)")
                self.myProgressView.progress = 0.80
            }
        }
    }


    var webViewLoads = 0
    var webViewDidStart:Int = 0
    var webViewDidFinish:Int = 0

  func webViewDidStartLoad(webView: UIWebView) {


    webViewDidStart++
    webViewLoads++

    if webViewLoads <= 1 {
        startAnimatingProgressBar()
    }

    println("webViewDidStartNumber: = \(webViewDidStart)")
    println("webViewLoadsStart: = \(webViewLoads)")

//    println("webViewDidStartLoad")

    UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    updateToolbarItems()
  }

  func webViewDidFinishLoad(webView: UIWebView) {

        webViewLoads--
        webViewDidFinish++

        println("webViewDidFinishLoad \(webViewDidFinish)")

    if webViewLoads == 0 {
        finishAnimatingProgressBar()
//        println("webViewLoads \(webViewLoads)")
        return
    }
    getWebsiteInfo()
    UIApplication.sharedApplication().networkActivityIndicatorVisible = false

    updateToolbarItems()

  func webView(webView: UIWebView, didFailLoadWithError error: NSError) {
    theBool = true
    webViewLoads = 0
    UIApplication.sharedApplication().networkActivityIndicatorVisible = false

    println("didFailLoadWithError")

    updateToolbarItems()
  }

Took me ages to figure out how to determine when a website has fully loaded and animate the progress bar according... after some hardcore googling this is the best I could come up with. lots of people saying lots of stuff most of it didn't help me.... add, and improve please let me know if you figure out something better.

alemac852
  • 99
  • 1
  • 11
4

I am using this and looks good.

@IBOutlet var webView: UIWebView!
@IBOutlet var progressView: UIProgressView!
override func viewDidLoad() {
   super.viewDidLoad()
   let url = NSURL(string: "http://stackoverflow.com")
   let request = NSURLRequest(URL: url)
   webView.loadRequest(request)
   webView.delegate=self
}

func webViewDidStartLoad(_ webView: UIWebView) {

    self.progressView.setProgress(0.1, animated: false)
}


func webViewDidFinishLoad(_ webView: UIWebView) {

    self.progressView.setProgress(1.0, animated: true)
}

func webView(_ webView: UIWebView, didFailLoadWithError error: NSError?) {

    self.progressView.setProgress(1.0, animated: true)
}
nfinfu
  • 71
  • 1
  • 3
2

Swift 5

@IBOutlet weak var webView: WKWebView!
@IBOutlet weak var progressView: UIProgressView!

override func viewDidLoad() {
    super.viewDidLoad()

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" {
           print("estimatedProgress")
           progressView.progress = Float(webView.estimatedProgress)
       }
   }

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
  
    progressView.isHidden = true
}
Timchang Wuyep
  • 629
  • 7
  • 10
0

Why not use

loadRequest(_ request: URLRequest, progress: ((UInt, Int64, Int64) -> Swift.Void)?, success: ((HTTPURLResponse, String) -> String)?, failure: ((Error) -> Swift.Void)? = nil)

Ram Y
  • 1,944
  • 17
  • 23