在项目中我们经常会在多个界面使用到WebView,所以本文对WebView进行封装,实现一个通用的WebView控制器。
实现效果
简单的Webview
实现逻辑
使用系统提供的WKWebView控件,外界通过传递网址,或者字符串进行显示。
1)创建WebView控制器SuperWebController,继承自BaseTitleController。
class SuperWebController: BaseTitleController {}
2)定义SuperWebController接收的两个参数uri和content。
class SuperWebController: BaseTitleController { var uri:String? var content:String?}
3)重写initViews(),设置SuperWebController内容布局为RelativeLayout,并添加标题栏右边的关闭按钮 和 webView控件,webView控件使用懒加载创建。
class SuperWebController: BaseTitleController { var uri:String? var content:String? override func initViews() { super.initViews() initRelativeLayoutSafeArea() //设置右侧按钮 addRightImageButton(R.image.close()!.withTintColor()) container.addSubview(webView) } /// 获取配置 static func defaultConfiguration() -> WKWebViewConfiguration { let r = WKWebViewConfiguration() if #available(iOS 10.0, *) { r.mediaTypesRequiringUserActionForPlayback = .all } else if #available(iOS 9.0, *){ r.requiresUserActionForMediaPlayback = false }else{ r.mediaPlaybackRequiresUserAction = false } return r } lazy var webView: WKWebView = { let r = WKWebView(frame: CGRect.zero, configuration: SuperWebController.defaultConfiguration()) r.tg_width.equal(.fill) r.tg_height.equal(.fill) return r }()}
4)获取外界传入的数据,并设置到webview上。
class SuperWebController: BaseTitleController { static let CONTENT_WRAPPER_START = "<!DOCTYPE html><html><head><title></title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"><style type=\"text/css\"> body{font-family: Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Arial,sans-serif;word-wrap: break-word;word-break: normal;} h2{text-align: center;} img {max-width: 100%;} pre{word-wrap: break-word!important;overflow: auto;}</style></head><body>" static let CONTENT_WRAPPER_END = "</body></html>" static let WEBVIEW_BASE_URL = "http://ixuea.com" override func initDatum() { super.initDatum() if SuperStringUtil.isNotBlank(uri) { //显示网址内容 //创建一个Request let request = URLRequest(url: URL(string: uri)!) //请求 webView.load(request) } else { //显示字符串 //由于服务端,返回的字符串,不是一个完整的HTML字符串 //同时本地可能希望添加一些字体设置,所以要前后拼接为一个 //完整的HTML字符串 var buffer = String(SuperWebController.CONTENT_WRAPPER_START) //添加内容 buffer.append(content!) buffer.append(SuperWebController.CONTENT_WRAPPER_END) //加载字符串 webView.loadHTMLString(buffer, baseURL: URL(string: SuperWebController.WEBVIEW_BASE_URL)) } }}
5)拦截返回按钮事件和添加关闭页面事件
class SuperWebController: BaseTitleController { /// 拦截点击返回按钮 override func leftClick(_ sender: QMUIButton) { if webView.canGoBack { //如果浏览器能返回上一页,就直接返回上一页 webView.goBack() return } super.leftClick(sender) } override func rightClick(_ sender: QMUIButton) { finish() } /// 关闭界面 func finish() { navigationController?.popViewController(animated: true) }}
6)扩展一个start方法,提供给外界启动webview。
extension SuperWebController{ /// 启动方法 static func start(_ controller:UINavigationController,title:String?=nil,uri:String?=nil,content:String?=nil) { let target = SuperWebController() target.title = title target.uri=uri target.content = content controller.pushViewController(target, animated: true) }}
7)使用webview
SuperWebController.start(navigationController!,title: data.title,uri: data.uri)
至此,简单的webview就实现了,下面继续封装 动态网页标题和进度功能。
动态标题和进度Webview
实现逻辑
如果外界没有传递标题,就显示网页标题;仿微信内部的浏览器顶部的进度条,这里要实现类似功能。进度条控件使用UIProgressView,然后监听webView进度,并设置到进度条。
1)给SuperWebController添加UIProgressView,UIProgressView控件使用懒加载创建。
class SuperWebController : BaseTitleController{ override func initViews() { super.initViews() initRelativeLayoutSafeArea() //设置右侧按钮 addRightImageButton(R.image.close()!.withTintColor()) container.addSubview(webView) container.addSubview(progressView) } lazy var progressView: UIProgressView = { let r = UIProgressView() r.tg_width.equal(.fill) r.tg_height.equal(1) //设置进度条的颜色 r.progressTintColor = .colorPrimary return r }()}
2)添加标题和进度监听
class SuperWebController : BaseTitleController{ override func initListeners() { super.initListeners() if SuperStringUtil.isBlank(title){ //监听网页标题 webView.addObserver(self, forKeyPath: "title", options: .new,context: nil) } //监听加载进度 webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil) }}
3)重写KVO监听回调,动态更新标题和进度条。
class SuperWebController : BaseTitleController{ /// KVO监听回调 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if let _ = object as? WKWebView{ if keyPath == "title"{ //标题 title = webView.title }else if keyPath == "estimatedProgress"{ //进度 //0~1 let progress = change?[.newKey] as? Float ?? 0 progressView.progress = progress if progress < 1 { progressView.show() //完全不透明 progressView.alpha = 1 }else{ UIView.animate(withDuration: 0.35, delay: 0.15) { self.progressView.alpha = 0 } completion: { finished in if finished { self.progressView.hide() self.progressView.progress=0 self.progressView.alpha = 1 } } } } } } }
至此,动态网页标题和进度功能的webview就实现了。