自己封装的HtmlWKWebView(OC)
一、.h文件里
/*
(1)适用于HTML解析和网页链接的解析,不适用与与JS的交互,因为代理方法控制器里不走,除非重写代理方法
(2)WKWebView的封装所有属性,包括进度条、长按保存图片、图片自适应手机、WKWebView的头部控件、尾部控件
(3)使用时一定要先设置完头部和尾部控件,在开始加载网页
*/
#import <WebKit/WebKit.h>
@interface HtmlWKWebView : WKWebView
//wkWebview网页
@property(nonatomic,strong)WKWebView * wkWebview;
//头部视图
@property(nonatomic,strong)UIView * headerView;
//尾部视图
@property(nonatomic,strong)UIView * footView;
//HTML
@property(nonatomic,strong)NSString * htmlString;
//移除KVO
@property(nonatomic,strong)NSString * remoDeallocKvo;
//与JS有交互的WKWebViewConfiguration不会走Init方法需要手动调用创建UI
@property(nonatomic,strong)NSString * actionJS;
@end
二、.m文件里
#import "HtmlWKWebView.h"
//图片�浏览器控制器
#import "JZAlbumViewController.h"
@interface HtmlWKWebView()<WKNavigationDelegate,UIGestureRecognizerDelegate>
//图片链接数组
@property (strong, nonatomic)NSMutableArray * mArrayUrl;
//进度条
@property (strong, nonatomic) UIProgressView *progressView;
@end
@implementation HtmlWKWebView
// 在initWithFrame:方法中添加子控件
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
//必须在初始化时创建否则会崩溃
[self createUI];
}
return self;
}
//和JS有交互的
-(void)setActionJS:(NSString *)actionJS{
_actionJS = actionJS;
//因为和JS有交互的WKWebViewConfiguration不走inint方法,所有手动调用
[self createUI];
}
//注册KVO开启进度条
-(void)setWkWebview:(WKWebView *)wkWebview{
_wkWebview = wkWebview;
self.progressView.hidden = NO;
// NSLog(@"wkWebview:%@",self.wkWebview);
// KVO,监听webView属性值得变化(estimatedProgress,title为特定的key)
[self.wkWebview addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
}
//得到html
-(void)setHtmlString:(NSString *)htmlString{
_htmlString = htmlString;
// reSizeImageWithHTML设置头样式
[self loadHTMLString:[self reSizeImageWithHTML:self.htmlString] baseURL:nil];
}
//创建UI
-(void)createUI{
//打开垂直滚动条
self.scrollView.showsVerticalScrollIndicator=YES;
//关闭水平滚动条
self.scrollView.showsHorizontalScrollIndicator=NO;
//设置滚动视图的背景颜色
self.scrollView.backgroundColor = [UIColor groupTableViewBackgroundColor];
self.navigationDelegate = self;
//两条属性关闭黑影
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
// 添加头部
_headerView = [[UIView alloc] init];
_headerView.backgroundColor = [UIColor whiteColor];
_headerView.userInteractionEnabled = YES;
[self.scrollView addSubview:_headerView];
//添加脚步
_footView = [[UIView alloc] init];
//开始加载时不设置脚步坐标
_footView.frame = CGRectZero;
_footView.backgroundColor = [UIColor whiteColor];
_footView.userInteractionEnabled = YES;
//先影藏
_footView.hidden = YES;
[self.scrollView addSubview:_footView];
//添加长按手势
self.userInteractionEnabled = YES;
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
longPress.allowableMovement = 20;
longPress.minimumPressDuration = 1.0f;
longPress.delegate = self;
[self addGestureRecognizer:longPress];
if (self.actionJS.length>0) {
//不创建点击手势,不然点击的时候又弹框有出现图片浏览的
}else{
//添加点击手势(点击查看大图)
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapPress:)];
tap.delegate = self;
[self addGestureRecognizer:tap];
}
// UIProgressView初始化
self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
self.progressView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 2);
self.progressView.trackTintColor = [UIColor clearColor]; // 设置进度条的色彩
// self.progressView.progressTintColor = [UIColor blueColor];
// 设置初始的进度,防止用户进来就懵逼了(微信大概也是一开始设置的10%的默认值)
self.progressView.hidden = YES;
[self.progressView setProgress:0.1 animated:YES];
[self addSubview:self.progressView];
}
//由于后台返回的HTML代码里……你懂的,所以只能添加样式来适配屏幕,下面的方法可以调节文字和图片大小来适配屏幕。(删者必死)
- (NSString *)reSizeImageWithHTML:(NSString *)html {
// 顶部留出空白(用来创建头部视图):<p style='padding-top:200px;'></p> 底部留出空白(用力啊创建尾部视图):<p style='padding-bottom:200px;'></p>
//设置头部、尾部空白和设置html图片自适应手机
return [NSString stringWithFormat:@"<p style='padding-top:%fpx;'></p><meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0'><meta name='apple-mobile-web-app-capable' content='yes'><meta name='apple-mobile-web-app-status-bar-style' content='black'><meta name='format-detection' content='telephone=no'><style type='text/css'>img{width:%fpx}</style>%@<p style='padding-bottom:%fpx;'></p>",self.headerView.frame.size.height, SCREEN_WIDTH - 15, html,self.footView.frame.size.height];
}
//页面加载完后获取高度,设置脚,注意,控制器里不能重写代理方法,否则这里会不执行
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
// 不执行前段界面弹出列表的JS代码,关闭系统的长按保存图片
[self evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';" completionHandler:nil];
// WKWebView禁止放大缩小(捏合手势)
NSString *injectionJSString = @"var script = document.createElement('meta');"
"script.name = 'viewport';"
"script.content=\"width=device-width, initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, user-scalable=no\";"
"document.getElementsByTagName('head')[0].appendChild(script);";
[webView evaluateJavaScript:injectionJSString completionHandler:nil];
//获取网页高度
[webView evaluateJavaScript:@"document.body.offsetHeight;" completionHandler:^(id Result, NSError * error) {
dispatch_async(dispatch_get_main_queue(), ^{
CGFloat documentHeight = [Result doubleValue]+15.00;
// NSLog(@"高度%f",documentHeight);
// [DWBToast showCenterWithText:[NSString stringWithFormat:@"%f",documentHeight]];
if (self.htmlString.length>0) {
//啥也不干
}else{
documentHeight = 0.0;
}
_footView.frame = CGRectMake(0, documentHeight-self.footView.frame.size.height, SCREEN_WIDTH, self.footView.frame.size.height);
//加载完设置好坐标打开
_footView.hidden = NO;
});
}];
//网页加载完成后通过js获取htlm中所有图片url
[self getImageUrlByJS:self];
//清除WK缓存,否则H5界面跟新,这边不会更新
[self remoViewCookies];
}
//清除WK缓存,否则H5界面跟新,这边不会更新
-(void)remoViewCookies{
if ([UIDevice currentDevice].systemVersion.floatValue>=9.0) {
// - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation 中就成功了 。
// 然而我们等到了iOS9!!!没错!WKWebView的缓存清除API出来了!代码如下:这是删除所有缓存和cookie的
NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
//// Date from
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
//// Execute
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
}];
}else{
//iOS8清除缓存
NSString * libraryPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
NSString * cookiesFolderPath = [libraryPath stringByAppendingString:@"/Cookies"];
[[NSFileManager defaultManager] removeItemAtPath:cookiesFolderPath error:nil];
}
}
- (void)handleLongPress:(UILongPressGestureRecognizer *)sender{
if (sender.state != UIGestureRecognizerStateBegan) {
return;
}
CGPoint touchPoint = [sender locationInView:self];
// 获取长按位置对应的图片url的JS代码
NSString *imgJS = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
// 执行对应的JS代码 获取url
[self evaluateJavaScript:imgJS completionHandler:^(id _Nullable imgUrl, NSError * _Nullable error) {
if (imgUrl) {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgUrl]];
UIImage *image = [UIImage imageWithData:data];
if (!image) {
NSLog(@"读取图片失败");
return;
}
//获取到图片image
//保存图片
[AlertViewTool AlertWXSheetToolWithTitle:nil otherItemArrays:@[@"保存图片"] ShowRedindex:-1 CancelTitle:@"取消" handler:^(NSInteger index) {
if (index==0) {
//保存
dispatch_async(dispatch_get_main_queue(), ^{
//保存图片到相册
UIImageWriteToSavedPhotosAlbum(image, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), nil);
});
}
}];
}
}];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
//保存照片到本地相册
- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
if(!error){
//延迟显示,否则会移除
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[MBProgressHUD showSuccess:@"保存成功"];
});
}else{
//延迟显示,否则会移除
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[MBProgressHUD showSuccess:@"保存失败"];
});
}
}
#pragma mark - KVO监听进度条
// 第三部:完成监听方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([object isEqual:self] && [keyPath isEqualToString:@"estimatedProgress"]) { // 进度条
CGFloat newprogress = [[change objectForKey:NSKeyValueChangeNewKey] doubleValue];
NSLog(@"网页进度:%f", newprogress);
if (newprogress == 1) { // 加载完成
// 首先加载到头
[self.progressView setProgress:newprogress animated:YES];
// 之后0.3秒延迟隐藏
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
weakSelf.progressView.hidden = YES;
[weakSelf.progressView setProgress:0 animated:NO];
});
} else { // 加载中
self.progressView.hidden = NO;
[self.progressView setProgress:newprogress animated:YES];
}
} else { // 其他
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
//暂时不用
//- (void)dealloc {
// NSLog(@"移除了KVO");
// // 最后一步:移除监听的KVO
// [self.wkWebview removeObserver:self forKeyPath:@"estimatedProgress" context:nil];
//
//}
-(void)setRemoDeallocKvo:(NSString *)remoDeallocKvo{
_remoDeallocKvo = remoDeallocKvo;
NSLog(@"移除了KVO");
// 最后一步:移除监听的KVO
[self.wkWebview removeObserver:self forKeyPath:@"estimatedProgress" context:nil];
}
#pragma mark ========= 点击获取所有图片,并查看大图 ========================
//通过js获取htlm中图片url
-(void)getImageUrlByJS:(WKWebView *)wkWebView{
//js方法遍历图片添加点击事件返回图片个数
//这里是js,主要目的实现对url的获取
static NSString * const jsGetImages =
@"function getImages(){\
var objs = document.getElementsByTagName(\"img\");\
var imgScr = '';\
for(var i=0;i<objs.length;i++){\
imgScr = imgScr + objs[i].src + '+';\
};\
return imgScr;\
};";
//用js获取全部图片,传质给js
[wkWebView evaluateJavaScript:jsGetImages completionHandler:nil];
//得到所有图片
NSString *jsString = @"getImages()";
[wkWebView evaluateJavaScript:jsString completionHandler:^(id Result, NSError * error) {
NSString *resurlt=[NSString stringWithFormat:@"%@",Result];
if([resurlt hasPrefix:@"+"]){
resurlt=[resurlt substringFromIndex:1];
}
NSArray * array=[resurlt componentsSeparatedByString:@"+"];
[self.mArrayUrl removeAllObjects];
//添加到可变数组
[self.mArrayUrl addObjectsFromArray:array];
//移除最后一个元素(空白)
[self.mArrayUrl removeLastObject];
// NSLog(@"得到所有图片url:%@",self.mArrayUrl);
}];
}
//点击手势
- (void)handleTapPress:(UITapGestureRecognizer *)sender{
CGPoint touchPoint = [sender locationInView:self];
// 获取长按位置对应的图片url的JS代码
NSString *imgJS = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
// 执行对应的JS代码 获取url
[self evaluateJavaScript:imgJS completionHandler:^(id _Nullable imgUrl, NSError * _Nullable error) {
if (imgUrl) {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgUrl]];
UIImage *image = [UIImage imageWithData:data];
if (!image) {
NSLog(@"读取图片失败");
return;
}
//获取到图片image
//图片大于0才创建
if (self.mArrayUrl.count>0) {
NSInteger currentIndex = 0;
//得到索引
for (int i= 0; i< self.mArrayUrl.count; i++) {
if ([imgUrl isEqual:self.mArrayUrl[i]]) {
//当前点击了第几张图片
currentIndex = i;
}
}
//控制器跳转
JZAlbumViewController *jzAlbumVC = [[JZAlbumViewController alloc]init];
//当前点击图片的索引
jzAlbumVC.currentIndex = currentIndex;
//imgArr可以为url数组, 可以为urlString 数组, 可以为二进制 UIImage 数组
jzAlbumVC.imgArr = self.mArrayUrl;
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:jzAlbumVC animated:NO completion:nil];
}else{
//如果加载完后拿不到所有图片数组,就查看当前点击的图片
//控制器跳转
JZAlbumViewController *jzAlbumVC = [[JZAlbumViewController alloc]init];
//当前点击图片的索引
jzAlbumVC.currentIndex = 0;
//imgArr可以为url数组, 可以为urlString 数组, 可以为二进制 UIImage 数组
jzAlbumVC.imgArr = [NSMutableArray arrayWithArray:@[imgUrl]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:jzAlbumVC animated:NO completion:nil];
}
}
}];
}
//图片数组
-(NSMutableArray *)mArrayUrl{
if (!_mArrayUrl) {
_mArrayUrl = [NSMutableArray array];
}
return _mArrayUrl;
}
#pragma mark======================WKWebView - alert不弹出(这是WKWebView相对于UIWebView的一个坑)===========================================
//WKWebView默认不响应js的alert()事件,如何可以开启alert权限呢?
//设置wkwebview.delegate = self;
//实现下面三个方法:
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}])];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
// DLOG(@"msg = %@ frmae = %@",message,frame);
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}])];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.text = defaultText;
}];
[alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(alertController.textFields[0].text?:@"");
}])];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}
//WKWebView打开新界面 需要打开新界面是,WKWebView的代理WKUIDelegate方法
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
// 会拦截到window.open()事件.只需要我们在在方法内进行处理
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
@end
网友评论