【问题标题】:UIWebView with Progress Bar带有进度条的 UIWebView
【发布时间】:2014-01-21 16:10:57
【问题描述】:

您好,我是编程新手,我正在尝试在 Xcode 上为 iPhone 制作我的第一个应用程序。 我的应用程序包含一个按钮,该按钮在按下时打开 UIWebView 并加载主页。 现在我还想像 Safari 一样向 WebView 添加一个进度视图,它指示加载页面的进度。我该怎么做?
到目前为止,我用于在 UIWebView 中加载 URL 的代码:

.h

IBOutlet UIWebView *webView;

.m

- (void)viewDidLoad
{
[webView loadRequest:[NSURLRequest requestWithURL: [NSURL URLWithString:@"http:/www.google.com/"]]];

感谢您的帮助!

【问题讨论】:

标签: ios uiwebview uiprogressview


【解决方案1】:

要获得准确的UIProgressView,您需要完成以下任务:

  • 您可以在信息不完整时从中获取信息
  • 根据该信息将其“完整性”量化为百分比。

现在,当您加载 UIWebView 时,这是不可能的。苹果也不这样做。 Apple 经常使用虚假的UIProgressViews 在页面加载时为您提供一些可以查看的内容。 Mail 还使用虚假的进度视图。自己去试试吧。这就是 Apple 的虚假进度视图的工作原理:

  • 进度视图开始以缓慢、恒定的速度移动
  • 如果任务在进度条完成之前完成,它会突然拉到 100%,然后消失
  • 如果任务需要很长时间,进度视图将在 95% 处停止并一直停留到任务完成。

要实现这一点,您必须手动为 progressView 设置动画。您可以将其子类化,但这对您来说可能有点先进。最简单的方法是:

在 myViewController.h 中

@interface myViewController : UIViewController {
     BOOL theBool;
     //IBOutlet means you can place the progressView in Interface Builder and connect it to your code
     IBOutlet UIProgressView* myProgressView;
     NSTimer *myTimer;
}
@end

在 myViewController.m 中

#import "myViewController.h"
@implementation myViewController
- (void)webViewDidStartLoad:(UIWebView *)webView{
     myProgressView.progress = 0;
     theBool = false;
     //0.01667 is roughly 1/60, so it will update at 60 FPS
     myTimer = [NSTimer scheduledTimerWithTimeInterval:0.01667 target:self selector:@selector(timerCallback) userInfo:nil repeats:YES];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView{
     theBool = true;
}
-(void)timerCallback {
    if (theBool) {
         if (myProgressView.progress >= 1) {
              myProgressView.hidden = true;
              [myTimer invalidate];
         }
         else {
              myProgressView.progress += 0.1;
         }
    }
    else {
         myProgressView.progress += 0.05;
         if (myProgressView.progress >= 0.95) {
              myProgressView.progress = 0.95;
         }
    }
}
@end

然后,在您的任务完成的地方,设置theBool = true;,进度视图将自行处理。更改 if 语句中的值以控制动画的速度。

【讨论】:

  • +1 在提交我的之前我没有正确阅读你的答案,这也暗示了假的进度条:)
  • 感谢您的帮助。但这对我不起作用。我究竟做错了什么?我将进度视图放入我的 Web 视图并将其与“IBOutlet”连接起来。现在我可以在运行模拟器时看到进度视图,但它不会动画。
  • 确保您的计时器正常工作。 timerCallback 被调用了吗?
  • @user3220034 danihvilla 注意到我犯了一个错误。使用scheduledTimerWithInterval: 而不是timerWithInterval:
  • 我知道您隐藏了进度条,但计时器应该仍在运行,对吧?我们不应该也停止计时器吗?
【解决方案2】:

斯威夫特 2.2

class ViewController: UIViewController,UIWebViewDelegate {

    @IBOutlet weak var webView: UIWebView!
    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
    @IBOutlet weak var myProgressView: UIProgressView!

    var myTimer = NSTimer()
    var theBool = Bool()

    override func viewDidLoad() {
        super.viewDidLoad()

        let url = NSURL(string: "https://www.youtube.com")
        let request = NSURLRequest(URL: url!)
        activityIndicator.hidesWhenStopped = true
        activityIndicator.startAnimating()
        webView.loadRequest(request)

    }
    func timerCallback(){
        if theBool {
            if myProgressView.progress >= 1 {
                myProgressView.hidden = true
                myTimer.invalidate()
            }else{
                myProgressView.progress += 0.1
            }
        }else{
            myProgressView.progress += 0.05
            if myProgressView.progress >= 0.95 {
                myProgressView.progress = 0.95
            }
        }
    }

    func webViewDidStartLoad(webView: UIWebView) {
        activityIndicator.startAnimating()
        myProgressView.progress = 0
        theBool = false
        myTimer =  NSTimer.scheduledTimerWithTimeInterval(0.01667,target: self,selector: #selector(ViewController.timerCallback),userInfo: nil,repeats: true)
    }
    func webViewDidFinishLoad(webView: UIWebView) {
        activityIndicator.stopAnimating()
        theBool = true
    }
}

【讨论】:

    【解决方案3】:

    如果有人想在 swift 3 中做到这一点,我花了几天时间试图弄清楚并最终做到了。

    这里是代码:)

    override func viewDidLoad() {
        super.viewDidLoad()
    
        let url = NSURL(string: "http://google.com")
        let request = NSURLRequest(url: url as! URL)
        webView.loadRequest(request as URLRequest)
        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: Error) {
    
        self.progressView.setProgress(1.0, animated: true)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    【讨论】:

      【解决方案4】:

      我按照 Wolflink 的回答发现了同样的问题,但没有使 progressView 动画化。

      看完NSTimer类参考:

      https://developer.apple.com/library/Mac/documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/Reference/NSTimer.html#//apple_ref/occ/clm/NSTimer/timerWithTimeInterval:target:selector:userInfo:repeats:

      讨论中,您可以阅读: "您必须使用 addTimer:forMode: 将新计时器添加到运行循环中。"

      所以我加了

      [[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode];

      之后

      myTimer = [NSTimer timerWithTimeInterval:0.01667 target:self selector:@selector(timerCallback) userInfo:nil repeats:YES];

      它对我有用(正如 WolfLink 建议的那样,我做了 UIProgressView 的子类)

      【讨论】:

      • 感谢您的关注!我的意思是使用[NSTimer scheduledtimerWithTimeInterval:0.01667 target:self selector:@selector(timerCallback) userInfo:nil repeats:YES];,它会自动将计时器添加到运行循环中。
      • 更干净,我会用那种方式。我已经看到您已经编辑了您的答案,因此@user3220034 可以将其标记为正确答案。至少这是我要找的。​​span>
      【解决方案5】:

      这很困难(如果可能的话),因为您必须跟踪网站加载的所有资源,但是……

      我有一个想法。这更像是一个技巧,而不是真正的解决方案,但我认为即使是 Apple 也在 Messages 应用程序中使用它:)

      1. 当您开始加载页面时,开始动画到进度的 90%(假设持续时间为 1.5 秒,对于 Wi-Fi、LTE、3G 等可能会有所不同)。
      2. 同时加载页面时,快速将进度设置为 100%。完成!
      3. 如果页面需要更多时间来加载,进度条将在 90% 处停止并在那里等待。当用户在状态栏中看到缓慢旋转的指示器时,令人沮丧的时刻!最后,页面完成加载并(如在项目符号 2 中所示)您将 快速动画播放到 100%。完成!

      我想我们都知道,这就是 Messages 应用程序的工作方式,因为我不相信发送 SMS 可以被跟踪到如此准确的进度:)

      【讨论】:

        【解决方案6】:

        Apple 现在提供 WebKit 框架,其中包括 WKWebView 类,该类允许您使用属性“estimatedProgress”查询估计进度,因此您不再需要像在屏幕上显示进度条那样“伪造”进度UIWebView。

        【讨论】:

        • 这是正确的答案。从 WebView 更改为 WKWebView。
        【解决方案7】:

        我会添加评论,而不是添加答案,但我还没有足够的声誉来评论!

        就在几天前,我问了几乎完全相同的问题。在那个问题中,我包括了我想出的解决方案。我的解决方案比 WolfLink 的更复杂,但它确实跟踪加载页面的实际进度,尽管它不是 100% 准确的。正如 WolfLink 所说,这是不可能的。我的问题可以在这里找到:Progress bar for UIWebView

        请注意,无论您采用哪种方式,都需要检查 UIWebView 的加载属性以确定页面加载何时完成。

        【讨论】:

        • 谢谢。我绝对没想到会这么复杂:p
        【解决方案8】:

        我尝试了以下模式。它给了我更好的结果。我正在使用 WKWebview。

        @IBOutlet weak var progressView: UIProgressView! // added in storyboard
        @IBOutlet weak var webView: WKWebView!
        
            override func viewDidLoad() {
                super.viewDidLoad()
                // Do any additional setup after loading the view.
                setupEstimatedProgressObserver()
            }
        
            // Observe the WebViewClient for estimatedProgress value
            private func setupEstimatedProgressObserver() {
                estimatedProgressObserver = webView.observe(\.estimatedProgress, options: [.new]) { [weak self] webView, _ in
                    self?.progressView.progress = Float(webView.estimatedProgress)
                }
            }
        
            //When WebView load finished in WKNavigationDelegate in didFinish method we will use the following approach
            func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
                self.progressView.progress =  1.0
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    // your code here
                     self.progressView.isHidden = true
                }
            }
        

        【讨论】:

          【解决方案9】:

          这里的一些答案并不完全正确,因为他们忘记为 webview 注册代理。不幸的是,仅实现 UIWebViewDelegate 并覆盖这两个方法是不够的。 您还必须将 webview 的委托设置为 self

          class ViewController: UIViewController,UIWebViewDelegate {
          
              @IBOutlet weak var webView: UIWebView!
              @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
              @IBOutlet weak var myProgressView: UIProgressView!
          
              var myTimer = NSTimer()
              var theBool = Bool()
          
              override func viewDidLoad() {
                  super.viewDidLoad()
          
                  let url = NSURL(string: "https://www.youtube.com")
                  let request = NSURLRequest(URL: url!)
                  activityIndicator.hidesWhenStopped = true
                  activityIndicator.startAnimating()
          
                  // !
                  webView.delegate = self // <--- important
                  // ! 
          
                  webView.loadRequest(request)
          
              }
          
              func timerCallback(){
                  if theBool {
                      if myProgressView.progress >= 1 {
                          myProgressView.hidden = true
                          myTimer.invalidate()
                      }else{
                          myProgressView.progress += 0.1
                      }
                  }else{
                      myProgressView.progress += 0.05
                      if myProgressView.progress >= 0.95 {
                          myProgressView.progress = 0.95
                      }
                  }
              }
          
              func webViewDidStartLoad(webView: UIWebView) {
                  activityIndicator.startAnimating()
                  myProgressView.progress = 0
                  theBool = false
                  myTimer =  NSTimer.scheduledTimerWithTimeInterval(0.01667,target: self,selector: #selector(ViewController.timerCallback),userInfo: nil,repeats: true)
              }
          
              func webViewDidFinishLoad(webView: UIWebView) {
                  activityIndicator.stopAnimating()
                  theBool = true
              } 
          }
          

          此代码参考答案@ZAFAR007

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-03-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多