ios 带有进度条的 UIWebView
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21263358/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UIWebView with Progress Bar
提问by user3220034
Hi I'm new to programming and I'm trying to make my first app for iPhones on Xcode.
My app contains of a button which opens a UIWebView when pressed and loads up a homepage.
Now I also want to add a Progress View to the WebView like Safari also uses, which indicates the progress of loading the page. How can I do that?
My code so far for loading the URL in the UIWebView:
嗨,我是编程新手,我正在尝试在 Xcode 上为 iPhone 制作我的第一个应用程序。我的应用程序包含一个按钮,该按钮在按下时打开 UIWebView 并加载主页。现在我还想向 WebView 添加一个进度视图,就像 Safari 也使用的那样,它指示加载页面的进度。我怎样才能做到这一点?
到目前为止,我在 UIWebView 中加载 URL 的代码:
.h
。H
IBOutlet UIWebView *webView;
.m
.m
- (void)viewDidLoad
{
[webView loadRequest:[NSURLRequest requestWithURL: [NSURL URLWithString:@"http:/www.google.com/"]]];
Thanks for your help!
谢谢你的帮助!
回答by WolfLink
To have an accurate UIProgressView
, you need to have some task that:
要获得准确的UIProgressView
,您需要执行以下任务:
- You can get information from while it isn't complete
- Quantify its "completeness" as a percentage based on that information.
- 您可以在信息不完整时从中获取信息
- 根据该信息将其“完整性”量化为百分比。
Now when you are loading your UIWebView
, thats not possible. And Apple doesn't do it either. Apple often uses fake UIProgressView
s to give you something to look at while the page is loading. Mail also uses fake progress views. Go try it out for yourself. This is how Apple's fake progress views work:
现在当你加载你的UIWebView
,那是不可能的。而苹果也没有这样做。Apple 经常使用 fake UIProgressView
s 来让你在页面加载时查看一些东西。邮件还使用虚假的进度视图。自己去试试吧。这就是 Apple 的虚假进度视图的工作原理:
- The progress view starts moving at a slow, constant rate
- If the task finishes before the bar completes, it suddenly zips across the rest to 100% before disappearing
- If the task takes a long time, the progress view will stop at 95% and will stay there until the task is complete.
- 进度视图开始以缓慢、恒定的速度移动
- 如果任务在栏完成之前完成,它会在消失之前突然将其余部分压缩到 100%
- 如果任务需要很长时间,进度视图将在 95% 处停止并停留在那里直到任务完成。
To achieve this, you will have to animate the progressView manually. You could subclass it but that would probably be a bit advanced for you. The simplest way would be this:
为此,您必须手动为 progressView 设置动画。您可以将其子类化,但这对您来说可能有点高级。最简单的方法是这样的:
In myViewController.h
在 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
In myViewController.m
在 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
Then, where your task gets completed, set theBool = true;
and the progress view will take care of itself. Change the values in the if statement thing to control the speed of the animation.
然后,在您的任务完成的地方,设置theBool = true;
和进度视图将自行处理。更改 if 语句事物中的值以控制动画的速度。
回答by ZAFAR007
Swift 2.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
}
}
回答by Matej Mari?
If anyone wants to do it in swift 3, I spent a few days trying to figure it out and finally did.
如果有人想在 swift 3 中做到这一点,我花了几天的时间试图弄明白并最终做到了。
Here is the code :)
这是代码:)
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.
}
回答by danihvilla
I followed Wolflink's answer and found the same problem not having the progressView animated.
我按照 Wolflink 的回答发现了同样的问题,没有对 progressView 进行动画处理。
After reading NSTimer Class Reference:
阅读 NSTimer 类参考后:
In Discussionyou can read: "You must add the new timer to a run loop, using addTimer:forMode:. "
在讨论中,您可以阅读:“您必须使用 addTimer:forMode: 将新计时器添加到运行循环中。”
so I added
所以我补充说
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode];
after
后
myTimer = [NSTimer timerWithTimeInterval:0.01667 target:self selector:@selector(timerCallback) userInfo:nil repeats:YES];
myTimer = [NSTimer timerWithTimeInterval:0.01667 target:self selector:@selector(timerCallback) userInfo:nil repeats:YES];
It worked for me (As WolfLink recommended I did subclass UIProgressView )
它对我有用(正如 WolfLink 推荐的那样,我做了 UIProgressView 的子类)
回答by Tricertops
It's difficult (if even possible), since you would have to track all resources loaded by the site, but …
这很困难(如果可能的话),因为您必须跟踪站点加载的所有资源,但是……
I have one idea. It's more of a trickthan a real solution, but I think even Apple uses this in Messages app :)
我有一个想法。这与其说是真正的解决方案,不如说是一种技巧,但我认为即使是 Apple 也在 Messages 应用程序中使用它:)
- When you start loading the page, begin an animation to 90%of the progress (let's say with duration of 1.5 seconds, maybe be different for Wi-Fi, LTE, 3G, …).
- When page loads in meantime, quickly animate the progress to 100%. Done!
- If the page takes more time to load, the progress bar will stop at 90% and will wait there. Frustrating moment when the user watches slow spinning indicator in status bar! And then finally, the page finish loading and (as in bullet 2.) you play quick animation to 100%. Done!
- 当您开始加载页面时,开始播放90%的进度(假设持续时间为 1.5 秒,对于 Wi-Fi、LTE、3G 等可能有所不同)。
- 当页面同时加载时,快速将进度设置为 100%。完毕!
- 如果页面加载时间较长,进度条将在 90% 处停止并在那里等待。用户在状态栏中观看慢速旋转指示器时的令人沮丧的时刻!最后,页面完成加载并且(如第 2 项中所示)您将快速动画播放到 100%。完毕!
I think we all know, that this is how Messages app works, since I don't believe sending SMS can be tracked with such accurate progress :)
我想我们都知道,这就是 Messages 应用程序的工作方式,因为我不相信可以通过如此准确的进度跟踪发送 SMS :)
回答by Patrick T Nelson
Apple now supplies the WebKit framework, which includes the WKWebView class which allows you to query the estimated progress using the property 'estimatedProgress', so you no longer must 'fake' the progress like you do to show a progress bar on a UIWebView.
Apple 现在提供 WebKit 框架,其中包括 WKWebView 类,该类允许您使用属性“estimatedProgress”查询估计的进度,因此您不再需要像在 UIWebView 上显示进度条那样“伪造”进度。
回答by Bidhan Roy
I have tried the following pattern. It gave me a little bit better result. I am using WKWebview.
我尝试了以下模式。它给了我更好的结果。我正在使用 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
}
}
回答by NiklasLehnfeld
Some of these answers here are not completely correct, since they forgot to register the delegate for the webview. Unfortunately it is not enough to implement UIWebViewDelegate and override the two methods. You also have to set the delegate of the webview to self.
这里的一些答案并不完全正确,因为他们忘记为 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
}
}
This code refers to the answer @ZAFAR007
此代码参考答案@ZAFAR007
回答by Stuart Fisher
I would have added a comment, instead of adding an answer, but I don't yet have enough reputation to comment!
我会添加评论,而不是添加答案,但我还没有足够的声誉来发表评论!
Only a couple of days ago I asked almost exactly the same question. In that question I included the solution I came up with. My solution is more complicated than WolfLink's but it does track the real progress of loading the page, although it isn't 100% accurate. As WolfLink has said that's not possible. My question can be found here: Progress bar for UIWebView
就在几天前,我问了几乎完全相同的问题。在那个问题中,我包括了我想出的解决方案。我的解决方案比 WolfLink 的更复杂,但它确实跟踪加载页面的实际进度,尽管它不是 100% 准确。正如 WolfLink 所说,这是不可能的。我的问题可以在这里找到:UIWebView 的进度条
Note whichever way you do it you'll need to check the loading property of UIWebView to determine when the loading of the page is complete.
请注意,无论采用哪种方式,您都需要检查 UIWebView 的加载属性以确定页面加载何时完成。