QZWebView
WebView的簡單封裝 - 適用于原生App與h5交互
GitHub:https://github.com/quzhongyeluo/QZWebView
現(xiàn)在的新項目基本上都是最低適配iOS 8了,畢竟現(xiàn)在中國的社交巨無霸QQ和微信都是最低適配iOS 8,除非...老板特別要求的要適配iOS 7...無奈.,兼容iOS 7真的要花很多時間刹泄。最近在重構(gòu)項目尽爆,現(xiàn)在簡單說一下WebView的封裝砸民!適用于原生App與h5交互
iOS 7使用UIWebView
iOS 8及以上系統(tǒng)使用WKWebView(好處不說了创南,坑也不說了,網(wǎng)上一大把)
使用方法:
導(dǎo)入QZWebView.m QZWebView.h
如果需要控制器還需導(dǎo)入QZWebViewViewController.m QZWebViewViewController.h
QZWebView *webView = [[QZWebView alloc] initWithFrame:self.view.bounds url:yoururl];
[self.view addSubview:webView];
或者
QZWebView *webView = [[QZWebView alloc] initWithFrame:self.view.bounds filePath:self.filePath];
[self.view addSubview:webView];
使用控制器例子:
QZWebViewViewController *vc = [[QZWebViewViewController alloc] initWithURL:@"https://www.baidu.com"];
[self.navigationController pushViewController:vc animated:true];
本地初始化就不寫例子了固以,涉及到公司的項目的信息
如果iOS 9及以上系統(tǒng)訪問失敗,請檢查是否開啟了http訪問的權(quán)限嘱巾。
自定義需要修改的地方:
1憨琳、本地html文件所在的文件位置
baseURL:本地html文件所在的文件夾
2、自定義的需要攔截的鏈接旬昭,進(jìn)行與h5交互
WKWebView改:
/*
在發(fā)送請求之前篙螟,決定是否跳轉(zhuǎn)
decisionHandler必須調(diào)用,來決定是否跳轉(zhuǎn)问拘,
WKNavigationActionPolicyCancel取消跳轉(zhuǎn)
WKNavigationActionPolicyAllow 允許跳轉(zhuǎn)
*/
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
// 如果 targetFrame 的 mainFrame 屬性為NO遍略,表明這個 WKNavigationAction 將會新開一個頁面。
if (!navigationAction.targetFrame.isMainFrame) {
[webView evaluateJavaScript:@"var a = document.getElementsByTagName('a');for(var i=0;i<a.length;i++){a[i].setAttribute('target','');}" completionHandler:nil];
}
// 對截獲的URL進(jìn)行自定義操作
// 獲取URL
NSString *urlString = [[navigationAction.request URL] absoluteString];
// 把URL轉(zhuǎn)成UTF8編碼
NSString *UTF8URL = [NSString stringWithString:[urlString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if ([UTF8URL rangeOfString:@"自定義"].length != 0) {
// 被攔截骤坐,不允許跳轉(zhuǎn)(進(jìn)行自定義操作)
decisionHandler(WKNavigationActionPolicyCancel);
}else{
// 允許跳轉(zhuǎn)
decisionHandler(WKNavigationActionPolicyAllow);
}
}
UIWebView改:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
// 獲取URL
NSString *urlString = [[request URL] absoluteString];
// 把URL轉(zhuǎn)成UTF8編碼
NSString *UTF8URL = [NSString stringWithString:[urlString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if ([UTF8URL rangeOfString:@"自定義"].length != 0) {
// 被攔截绪杏,不允許跳轉(zhuǎn)(進(jìn)行自定義操作)
return NO;
}else{
// 允許跳轉(zhuǎn)
return YES;
}
}
Github:https://github.com/quzhongyeluo/QZWebView
直接看代碼吧!代碼有詳細(xì)的注釋
QZWebView.h
//
// QZWebView.h
// QZWebView
//
// Created by 曲終葉落 on 2017/7/13.
// Copyright ? 2017年 曲終葉落. All rights reserved.
//
#import <UIKit/UIKit.h>
@protocol QZWebViewDelegate <NSObject>
@optional
/**
頁面開始加載時調(diào)用
*/
- (void)didStartProvisional;
/**
開始獲取到網(wǎng)頁內(nèi)容時返回
*/
- (void)didCommit;
/**
加載完成
*/
- (void)didFinish;
/**
加載失敗
*/
- (void)didFailProvisional;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
- (void)scrollViewWillEndDragging;
- (void)webViewTitle:(NSString *)title;
@end
/**
WebView封裝:
iOS7 : UIWebView
iOS8 : WKWebView
*/
@interface QZWebView : UIView
@property (nonatomic,weak) id<QZWebViewDelegate>deleget;
/**
通過網(wǎng)頁鏈接初始化
@param frame frame
@param url 網(wǎng)頁鏈接
@return webView
*/
- (instancetype)initWithFrame:(CGRect)frame url:(NSString *)url;
/**
通過本地鏈接初始化
@param frame frame
@param filePath 本地鏈接
@return webView
*/
- (instancetype)initWithFrame:(CGRect)frame filePath:(NSString *)filePath;
/**
加載JSON數(shù)據(jù)(需要在webView加載完成后使用)
@param json json數(shù)據(jù)
*/
- (void)loadingWithJSON:(NSString *)json;
@end
QZWebView.m
//
// QZWebView.m
// QZWebView
//
// Created by 曲終葉落 on 2017/7/13.
// Copyright ? 2017年 曲終葉落. All rights reserved.
//
#import "QZWebView.h"
#import <WebKit/WebKit.h>
/*-------------------- 系統(tǒng)版本 --------------------*/
/**
iOS 7
*/
#define TARGET_iOS7 [[UIDevice currentDevice].systemVersion doubleValue] < 8.0 && [[UIDevice currentDevice].systemVersion doubleValue] >= 7.0
/**
iOS 8
*/
#define TARGET_iOS8 [[UIDevice currentDevice].systemVersion doubleValue] < 9.0 && [[UIDevice currentDevice].systemVersion doubleValue] >= 8.0
/**
iOS 8及iOS 8以后的系統(tǒng)
*/
#define TARGET_iOS8Later [[UIDevice currentDevice].systemVersion doubleValue] >= 8.0
/**
iOS 9及iOS9以后的系統(tǒng)
*/
#define TARGET_iOS9Later [[UIDevice currentDevice].systemVersion doubleValue] >= 9.0
// 加載模式
typedef enum{
// 在線
LoadRequest,
// 本地HTML
LoadHTML
}QZWebViewLoadType;
@interface QZWebView () <UIWebViewDelegate,UIActionSheetDelegate,WKNavigationDelegate,UIScrollViewDelegate,WKScriptMessageHandler,WKUIDelegate>
@property (nonatomic,strong) UIWebView *uiWebView;
@property (nonatomic,strong) WKWebView *wkWebView;
/**
加載模式
*/
@property (nonatomic, assign) QZWebViewLoadType loadType;
/**
URL:可能是本地鏈接或油、在線鏈接
*/
@property (nonatomic, copy ) NSURL *url;
@end
@implementation QZWebView
#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame url:(NSString *)url{
if (self = [super initWithFrame:frame]) {
_url = [NSURL URLWithString:url];
_loadType = LoadRequest;
[self loadData];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame filePath:(NSString *)filePath{
if (self = [super initWithFrame:frame]) {
_url = [NSURL fileURLWithPath:filePath];
_loadType = LoadHTML;
[self loadData];
}
return self;
}
/**
開始加載在線數(shù)據(jù)或者加載本地html文件
*/
- (void)loadData{
switch (_loadType) {
case LoadRequest:{
// 在線鏈接
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url];
if (TARGET_iOS8Later) {
[self.wkWebView loadRequest:request];
}else{
[self.uiWebView loadRequest:request];
}
}
break;
default:{
// 加載本地文件 ()
// 本地文件所在的文件夾位置
// NSString *baseURL = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"web"];
NSString *baseURL = @"";
// 獲取本地html的信息
NSString *html = [NSString stringWithContentsOfURL:_url encoding:NSUTF8StringEncoding error:nil];
if (TARGET_iOS7) {
[self.uiWebView loadHTMLString:html baseURL:[NSURL fileURLWithPath:baseURL]];
}else if (TARGET_iOS9Later){
[self.wkWebView loadHTMLString:html baseURL:[NSURL fileURLWithPath:baseURL]];
}else{
/*
在iOS8系統(tǒng)中寞忿,WKWebView使用loadHTMLString會出現(xiàn)不加載CSS和JS的問題
應(yīng)使用loadRequest加載
另外WKWebView在iOS 8是不能加載NSBundle、NSHomeDirectory等位置的文件顶岸,只能加載NSTemporaryDirectory的文件腔彰,所以需要把本地文件復(fù)制到NSTemporaryDirectory里面
*/
// [self copyWebToTemp];
// NSString *webpath = [NSTemporaryDirectory() stringByAppendingString:@"web"];
NSString *webpath = @"";
NSString *path = [NSString stringWithFormat:@"%@/%@",webpath,_url.lastPathComponent];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]];
[self.wkWebView loadRequest:request];
}
}
break;
}
}
#pragma mark - 處理json數(shù)據(jù)
/**
加載JSON數(shù)據(jù)(需要在webView加載完成后使用)
@param json json數(shù)據(jù)
*/
- (void)loadingWithJSON:(NSString *)json{
if (TARGET_iOS8Later) {
[self.wkWebView evaluateJavaScript:json completionHandler:^(id item, NSError * _Nullable error) {
// Block中處理是否通過了或者執(zhí)行JS錯誤的代碼
NSLog(@"%@",item);
NSLog(@"%@",error);
}];
}else{
[self.uiWebView stringByEvaluatingJavaScriptFromString:json];
}
}
#pragma mark - WKWebView代理
// 頁面開始加載時調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigatio{
if ([self.deleget respondsToSelector:@selector(didStartProvisional)]) {
[self.deleget didStartProvisional];
}
}
// 開始獲取到網(wǎng)頁內(nèi)容時返回
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
if ([self.deleget respondsToSelector:@selector(didCommit)]) {
[self.deleget didCommit];
}
}
// 頁面加載完成
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
if ([self.deleget respondsToSelector:@selector(didFinish)]) {
[self.deleget didFinish];
}
}
// 頁面加載失敗
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
if ([self.deleget respondsToSelector:@selector(didFailProvisional)]) {
[self.deleget didFailProvisional];
}
}
// 接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用 (服務(wù)器端redirect),不一定調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
}
/*
在收到服務(wù)器的響應(yīng)頭辖佣,根據(jù)response相關(guān)信息霹抛,決定是否跳轉(zhuǎn)。
decisionHandler必須調(diào)用卷谈,來決定是否跳轉(zhuǎn)杯拐,
WKNavigationResponsePolicyCancel取消跳轉(zhuǎn)
WKNavigationResponsePolicyAllow 允許跳轉(zhuǎn)
*/
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
decisionHandler(WKNavigationResponsePolicyAllow);
}
/*
在發(fā)送請求之前,決定是否跳轉(zhuǎn)
decisionHandler必須調(diào)用,來決定是否跳轉(zhuǎn)端逼,
WKNavigationActionPolicyCancel取消跳轉(zhuǎn)
WKNavigationActionPolicyAllow 允許跳轉(zhuǎn)
*/
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
// 如果 targetFrame 的 mainFrame 屬性為NO朗兵,表明這個 WKNavigationAction 將會新開一個頁面。
if (!navigationAction.targetFrame.isMainFrame) {
[webView evaluateJavaScript:@"var a = document.getElementsByTagName('a');for(var i=0;i<a.length;i++){a[i].setAttribute('target','');}" completionHandler:nil];
}
// 對截獲的URL進(jìn)行自定義操作
// 獲取URL
NSString *urlString = [[navigationAction.request URL] absoluteString];
// 把URL轉(zhuǎn)成UTF8編碼
NSString *UTF8URL = [NSString stringWithString:[urlString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if ([UTF8URL rangeOfString:@"自定義"].length != 0) {
// 被攔截顶滩,不允許跳轉(zhuǎn)(進(jìn)行自定義操作)
decisionHandler(WKNavigationActionPolicyCancel);
}else{
// 允許跳轉(zhuǎn)
decisionHandler(WKNavigationActionPolicyAllow);
}
}
// web界面中有彈出警告框時調(diào)用
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
NSLog(@"%@",message);
}
-(void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"確認(rèn)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}])];
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alertController animated:YES completion:nil];
}
// 從web界面中接收到一個腳本時調(diào)用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
}
#pragma mark - UIWebView代理
// 開始加載
- (void)webViewDidStartLoad:(UIWebView *)webView{
if ([self.deleget respondsToSelector:@selector(didStartProvisional)]) {
[self.deleget didStartProvisional];
}
}
// 加載完成
- (void)webViewDidFinishLoad:(UIWebView *)webView{
if ([self.deleget respondsToSelector:@selector(didFinish)]) {
[self.deleget didFinish];
}
// 獲取title
if ([self.deleget respondsToSelector:@selector(webViewTitle:)]) {
NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
[self.deleget webViewTitle:title];
}
}
// 加載失敗
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
if ([self.deleget respondsToSelector:@selector(didFailProvisional)]) {
[self.deleget didFailProvisional];
}
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
// 獲取URL
NSString *urlString = [[request URL] absoluteString];
// 把URL轉(zhuǎn)成UTF8編碼
NSString *UTF8URL = [NSString stringWithString:[urlString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if ([UTF8URL rangeOfString:@"自定義"].length != 0) {
// 被攔截余掖,不允許跳轉(zhuǎn)(進(jìn)行自定義操作)
return NO;
}else{
// 允許跳轉(zhuǎn)
return YES;
}
}
#pragma mark - scrollView代理
/**
* 解決iOS9系統(tǒng)bug
*
* 在iOS9系統(tǒng)中使用WKWebView時,滾動的時候缺少慣性礁鲁,滾動幅度小盐欺,使用此方法可解決
*/
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if ([[UIDevice currentDevice].systemVersion doubleValue] >= 9.0 && [[UIDevice currentDevice].systemVersion doubleValue] < 10.0) {
scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
}
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
if ([self.deleget respondsToSelector:@selector(scrollViewDidScroll:)]) {
[self.deleget scrollViewDidScroll:scrollView];
}
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
if ([self.deleget respondsToSelector:@selector(scrollViewWillEndDragging)]) {
[self.deleget scrollViewWillEndDragging];
}
}
#pragma mark - dealloc
- (void)dealloc{
if (TARGET_iOS8Later) {
// 不把wkWebView.scrollView.delegate 置為空在模擬器上會出現(xiàn)奔潰現(xiàn)象
self.wkWebView.scrollView.delegate = nil;
[self.wkWebView removeObserver:self forKeyPath:@"title"];
}else{
self.uiWebView.scrollView.delegate = nil;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
NSLog(@"webView已被釋放");
}
#pragma mark - KVO
// 設(shè)置iOS 8及以上的標(biāo)題
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"title"])
{
if (object == self.wkWebView) {
// 此處可以獲取到標(biāo)題信息
NSLog(@"%@",self.wkWebView.title);
if ([self.deleget respondsToSelector:@selector(webViewTitle:)]) {
[self.deleget webViewTitle:self.wkWebView.title];
}
}else{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark - get and set
- (UIWebView *)uiWebView{
if (!_uiWebView) {
UIWebView *web = [[UIWebView alloc] initWithFrame:self.bounds];
web.delegate = self;
// YES 加載網(wǎng)頁中的電話號碼,單擊可以撥打
web.dataDetectorTypes = NO;
// 適應(yīng)屏幕
web.scalesPageToFit = YES;
web.backgroundColor = [UIColor whiteColor];
web.multipleTouchEnabled = true;
web.scrollView.delegate = self;
web.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
[self addSubview:web];
_uiWebView = web;
}
return _uiWebView;
}
- (WKWebView *)wkWebView{
if (!_wkWebView) {
WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:self.bounds];
wkWebView.UIDelegate = self;
wkWebView.navigationDelegate = self;
wkWebView.backgroundColor = [UIColor whiteColor];
/*! 解決iOS9.2以上黑邊問題 */
wkWebView.opaque = false;
wkWebView.multipleTouchEnabled = true;
wkWebView.scrollView.delegate = self;
wkWebView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
[wkWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
[self addSubview:wkWebView];
_wkWebView = wkWebView;
}
return _wkWebView;
}
@end
WebView封裝就完了仅醇,根據(jù)使用需求選擇初始化方法冗美。
為了方便使用順便封裝一個QZWebViewViewController:
QZWebViewViewController.h
//
// QZWebViewViewController.h
// QZWebView
//
// Created by 曲終葉落 on 2017/7/13.
// Copyright ? 2017年 曲終葉落. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface QZWebViewViewController : UIViewController
/**
通過網(wǎng)頁鏈接初始化
@param url 鏈接
@return return value description
*/
- (instancetype)initWithURL:(NSString *)url;
/**
通過本地文件初始化
@param filePath 本地文件位置
@return return value description
*/
- (instancetype)initWithFilePath:(NSString *)filePath;
@end
QZWebViewViewController.m
//
// QZWebViewViewController.m
// QZWebView
//
// Created by 曲終葉落 on 2017/7/13.
// Copyright ? 2017年 曲終葉落. All rights reserved.
//
#import "QZWebViewViewController.h"
#import "QZWebView.h"
@interface QZWebViewViewController () <QZWebViewDelegate>
@property (nonatomic, copy ) NSString *url;
@property (nonatomic, strong) QZWebView *webView;
@property (nonatomic, copy ) NSString *filePath;
@property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView;
@end
@implementation QZWebViewViewController
#pragma mark - 初始化
- (instancetype)initWithURL:(NSString *)url{
if (self = [super init]) {
self.url = url;
}
return self;
}
- (instancetype)initWithFilePath:(NSString *)filePath{
if (self = [super init]) {
self.filePath = filePath;
}
return self;
}
#pragma mark -
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self webView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - QZWebView代理
- (void)webViewTitle:(NSString *)title{
self.title = title;
}
/**
網(wǎng)頁開始加載
*/
- (void)didStartProvisional{
[self.activityIndicatorView startAnimating];
}
/**
加載完成
*/
- (void)didFinish{
[self.activityIndicatorView stopAnimating];
}
#pragma mark - get and set
- (QZWebView *)webView{
if (!_webView) {
if (self.url) {
_webView = [[QZWebView alloc] initWithFrame:self.view.bounds url:self.url];
[self.view addSubview:_webView];
}else{
_webView = [[QZWebView alloc] initWithFrame:self.view.bounds filePath:self.filePath];
[self.view addSubview:_webView];
}
_webView.deleget = self;
}
return _webView;
}
- (UIActivityIndicatorView *)activityIndicatorView{
if (!_activityIndicatorView) {
_activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:(UIActivityIndicatorViewStyleWhiteLarge)];
_activityIndicatorView.frame = CGRectMake(0, 0, 100, 100);
_activityIndicatorView.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
_activityIndicatorView.color = [UIColor grayColor];
[self.view addSubview:_activityIndicatorView];
}
return _activityIndicatorView;
}
@end