当前位置: > > > Swift - 使用UIWebView和UIToolbar制作一个浏览器

Swift - 使用UIWebView和UIToolbar制作一个浏览器

(本文代码已升级至Swfit3)

使用网页控件(UIWebView)与工具栏控件(UIToolbar),我们可以自制一个小型的浏览器,其功能如下:
1,输入网址,点击“Go”按钮加载网页
2,加载过程中有进度条,同时可以点击停止按钮取消加载
3,有页面刷新按钮
4,有前进后退按钮

效果图如下:


代码如下:
import UIKit

class ViewController: UIViewController, UIWebViewDelegate, UITextFieldDelegate {
    
    @IBOutlet var btngo:UIButton!
    @IBOutlet var webview:UIWebView!
    @IBOutlet var txturl:UITextField!
    var loadIndicator:UIActivityIndicatorView!
    
    //进度条计时器
    var ptimer:Timer!
    //进度条控件
    var progBar:UIProgressView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.webview.delegate = self
        
        let frame = CGRect(x: 100.0, y: 100.0, width: 32.0, height: 32.0)
        loadIndicator = UIActivityIndicatorView(frame: frame)
        loadIndicator.activityIndicatorViewStyle = .gray
        self.view.addSubview(loadIndicator);
        
        txturl.delegate = self
        
        //构建浏览器工具条
        setupBrowserToolbar()
        
    }
    
    func setupBrowserToolbar()
    {
        // 创建一个浏览器工具条,并设置它的大小和位置
        let browserToolbar =  UIToolbar(frame:CGRect(x: 0, y: 20, width: 320, height: 44))
        
        // 将工具条添加到当前应用的界面中
        self.view.addSubview(browserToolbar)
        
        //创建图片工具条,但是不是直接使用文件名,而是用 Data 方式初始化 UIImage
        let path = Bundle.main.path(forResource: "back", ofType:"png")
        
        let urlStr = URL(fileURLWithPath: path!);
        let data = try? Data(contentsOf: urlStr);
        let btnback =  UIBarButtonItem(image:UIImage(data: data!), style:.plain, target:self,
                                       action:#selector(ViewController.backClicked(_:)));
        //第一个分隔按钮
        let btngap1 =  UIBarButtonItem(barButtonSystemItem:.flexibleSpace,
                                       target:nil, action:nil)
        
        // 创建前进按钮 UIBarButtonItem
        let btnforward = UIBarButtonItem(image:UIImage(named:"forward.png"),
                                         style:.plain, target:self,
                                         action:#selector(ViewController.forwardClicked(_:)))
        
        // 第二个分隔按钮,创建一个可伸缩的UIBarButtonItem
        let btngap2 =  UIBarButtonItem(barButtonSystemItem:.flexibleSpace,
                                       target:nil, action:nil)
        
        // 创建重新加载按钮 UIBarButtonItem
        let btnreload = UIBarButtonItem(image:UIImage(named:"reload.png"),
                                        style:.plain, target:self,
                                        action:#selector(ViewController.reloadClicked(_:)))
        
        //第三个分隔按钮
        let btngap3 =  UIBarButtonItem(barButtonSystemItem:.flexibleSpace,
                                       target:nil, action:nil)
        
        //创建加载停止按钮
        let btnstop = UIBarButtonItem(image:UIImage(named:"stop"),
                                      style:.plain, target:self,
                                      action:#selector(ViewController.stopClicked(_:)))
        
        //第四个分隔按钮
        let btngap4 =  UIBarButtonItem(barButtonSystemItem:.flexibleSpace,
                                       target:nil, action:nil)
        
        //创建进度工具条
        progBar = UIProgressView(progressViewStyle:UIProgressViewStyle.bar)
        
        // 设置UIProgressView的大小
        progBar.frame = CGRect(x: 0 , y: 0 , width: 80, height: 20)
        
        // 设置该进度条的初始进度为0
        progBar.progress = 0
        
        // 创建使用 UIView 的自定义的 UIBarButtonItem
        let btnprog =  UIBarButtonItem(customView:progBar)
        
        // 为工具条设置工具按钮
        browserToolbar.setItems( [btnback,btngap1,btnforward,btngap2,btnreload,
                                  btngap3,btnstop,btngap4, btnprog], animated:true)
        
        //创建计时器对象
        ptimer = Timer.scheduledTimer(timeInterval: 0.2, target:self,
                                      selector: #selector(ViewController.loadProgress),
                                      userInfo:nil,repeats:true)
        ptimer.invalidate()
    }
    
    func textFieldShouldReturn(_ textField:UITextField) -> Bool
    {
        txturl.resignFirstResponder()
        print("url Changed!")
        let url = txturl.text;
        loadUrl(url!)
        return true
    }
    /*
     在 UIWebView 加载指定 URL
     */
    func loadUrl(_ url:String)
    {
        let urlobj = URL(string:url)
        let request = URLRequest(url:urlobj!)
        webview.loadRequest(request);
    }
    
    func stopClicked(_ sender:UIBarButtonItem)
    {
        webview.stopLoading()
    }
    
    func reloadClicked(_ sender:UIBarButtonItem)
    {
        webview.reload()
    }
    
    func backClicked(_ sender:UIBarButtonItem)
    {
        webview.goBack()
    }
    
    func forwardClicked(_ sender:UIBarButtonItem)
    {
        webview.goForward()
    }
    
    @IBAction func goClicked(_ sender:UIButton)
    {
        //收起输入面板
        txturl.resignFirstResponder()
        let url = txturl.text;
        loadUrl(url!)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func webViewDidStartLoad(_ webView:UIWebView)
    {
        progBar.setProgress(0, animated:false);
        ptimer.fire();
        loadIndicator.startAnimating();
    }
    func webViewDidFinishLoad(_ webView:UIWebView)
    {
        loadIndicator.stopAnimating();
        progBar.setProgress(1, animated:true);
        ptimer.invalidate();
    }
    func loadProgress()
    {
        // 如果进度满了,停止计时器
        if(progBar.progress >= 1.0)
        {
            // 停用计时器
            ptimer.invalidate();
        }
        else
        {
            // 改变进度条的进度值
            progBar.setProgress(progBar.progress + 0.02, animated:true);
        }
    }
    
    func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
        //999错误过滤(防止页面正在加载时候,点击stop按钮,提示NSURLErrorDomain error=-999)
        if error._code == NSURLErrorCancelled {
            return
        }

        let alertController = UIAlertController(title: "出错!",
                                                message: error.localizedDescription,
                                                preferredStyle: .alert)
        let okAction = UIAlertAction(title: "确定", style: .cancel, handler: nil)
        alertController.addAction(okAction)
        self.present(alertController, animated: true, completion: nil)
    }
}

源代码下载:hangge_550.zip
评论11
  • 11楼
    2017-03-12 21:33
    苹果不青

    航哥,我按照你的写了一遍,确实能用了,但是当一个页面正在加载时候,我点击stop按钮,会提示NSURLErrorDomain error=-999,我是用百度的网址测试的,请问这是为什么呢

    站长回复

    多谢提醒,我把代码又更新了下,现在不会报错了。

  • 10楼
    2016-11-19 08:13
    ww

    大神 uiwebview 怎么加载证书不受信任的https网站啊 swift的

    站长回复

    Info.plist里添加个安全配置试试看呢?具体参考我之前写的文章:Swift - 网络请求报App Transport Security has blocked a cleartext错

  • 9楼
    2016-11-10 21:36
    discreet

    回复不可见???

    站长回复

    为了避免垃圾信息和广告,回复要我审核后就可见了。

  • 8楼
    2016-10-28 10:42
    ww

    不能添加评论

    站长回复

    可以评论的啊。

  • 7楼
    2016-10-05 19:07
    000000

    站长,能不能网址前不加http://和www

    站长回复

    这个可以在我们程序中自行判断处理,如果输入的网址前面没有http,我们调用的时候自动在前面给加上。

  • 6楼
    2016-03-11 11:49
    铁皮

    要是能把写每一部分代码的步骤也写上那就真是太完美了

    站长回复

    谢谢你的建议,如果样例比较复杂的话我会分步讲解的。代码量不多情况下,个人不太喜欢拆开,这样会显得很碎片化。

  • 5楼
    2016-03-05 19:49
    苦茶。

    航哥,这几天一直在折腾webview,想实现一些需求,网上找的资料也不尽如意,还是想给你留言,希望能得到你的帮助。
    1、webview加载网页是,想在导航栏上显示网页的标题,一般都在webViewDidFinishLoad代理方法中加上语句:self.title = webView.stringByEvaluatingJavaScriptFromString("document.title") 但是,今天在测试一个网页的时候,刚一打开,在web view的代理方法didFailLoadWithError就报错,错误代码-999,而且webViewDidFinishLoad代理方法居然不会执行了!那此时我该如何去获取网页标题呢???我尝试性在这几个代理方法中打印webView.stringByEvaluatingJavaScriptFromString("document.title"),但是一直是空,但是我又在界面上加上一个测试按钮,触发事件也是打印这个值,却能打印出标题!真的好困惑!我的测试网址:http://politics.gmw.cn/2016-03/05/content_19181474.htm

    2、我在你的那篇文章《Swift - 修改导航栏“返回”按钮文字,图标》下留了言,自定义按钮后右滑pop会消失。我看你已经更新解决了该问题:self.navigationController?.interactivePopGestureRecognizer!.delegate = self 这样做是能解决问题没错,但是我想做微信的加载webview的效果:在微信里打开个网页,点击网页里的链接,通过手势可以返回到上一个网页,当在第一个网页的时候,又可以手势pop当前的控制器。如果在打开的网页上再多次点击链接,默认情况下右滑直接pop回原VC,而不会一层一层地pop回上一级打开的网页,这种效果感觉挺难的没有一点思路,航哥能给点思路吗?谢谢!

    站长回复

    1,这个网址我也测试了下,确实会报-999错误,然后也不触发webViewDidFinishLoad方法。我觉得是由于这个网址请求的时候做了它自身跳转(网站为了适配移动端)。这个我目前也不知道有什么好的办法解决。
    2,要实现微信打开网页,能一层层地返回的效果。我觉得它应该不是只用一个webview,然后链接点击就在webview内部跳转。而是通过shouldStartLoadWithRequest 方法捕获链接请求,然后再push一个新的webview进来。

  • 4楼
    2016-01-26 09:12
    hard-working1

    楼主你写的博客我一直坚持再看, 请问下你在哪看的这些知识点

    站长回复

    目前基本都是根据大家的需求(比如给我留言)来写相关的文章。如果我很熟悉的话就直接写,这种比较快。但有时也有我不太了解的地方,就使用百度,google来查阅相关资料后边调代码边写文章,这种就比较费事了,一般要花两到三天才能写一篇。

  • 3楼
    2015-10-16 17:35
    amx5l

    181行的UIAlertView();有警告说是ios9 deprecated 推荐使用一个新的类,新手也不太懂

    站长回复

    推荐使用UIAlertController。我原来也写过相关文章介绍用法。在网站右上角搜索“UIAlertController”

  • 2楼
    2015-10-16 17:25
    amx5l

    swift前段时间刚刚改了,那啥println报错,直接写print()就行了吧,换行输出

    站长回复

    现已修改,多谢提醒。

  • 1楼
    2015-09-20 23:26
    冷漠

    楼主在吗?

    站长回复

    在。每天我都会看留言和评论的。