更多方法交流可以家魏鑫:lixiaowu1129,一起探討iOS相關(guān)技術(shù)!
一颜启、.h文件里
/*
(1)適用于HTML解析和網(wǎng)頁(yè)鏈接的解析,不適用與與JS的交互,因?yàn)榇矸椒刂破骼锊蛔撸侵貙?xiě)代理方法
(2)WKWebView的封裝所有屬性,包括進(jìn)度條、長(zhǎng)按保存圖片剔应、圖片自適應(yīng)手機(jī)、WKWebView的頭部控件语御、尾部控件
(3)使用時(shí)一定要先設(shè)置完頭部和尾部控件峻贮,在開(kāi)始加載網(wǎng)頁(yè)
*/
#import <WebKit/WebKit.h>
@interface HtmlWKWebView : WKWebView
//wkWebview網(wǎng)頁(yè)
@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不會(huì)走Init方法需要手動(dòng)調(diào)用創(chuàng)建UI
@property(nonatomic,strong)NSString * actionJS;
@end
二、.m文件里
#import "HtmlWKWebView.h"
//圖片?瀏覽器控制器
#import "JZAlbumViewController.h"
@interface HtmlWKWebView()<WKNavigationDelegate,UIGestureRecognizerDelegate>
//圖片鏈接數(shù)組
@property (strong, nonatomic)NSMutableArray * mArrayUrl;
//進(jìn)度條
@property (strong, nonatomic) UIProgressView *progressView;
@end
@implementation HtmlWKWebView
// 在initWithFrame:方法中添加子控件
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
//必須在初始化時(shí)創(chuàng)建否則會(huì)崩潰
[self createUI];
}
return self;
}
//和JS有交互的
-(void)setActionJS:(NSString *)actionJS{
_actionJS = actionJS;
//因?yàn)楹蚃S有交互的WKWebViewConfiguration不走inint方法应闯,所有手動(dòng)調(diào)用
[self createUI];
}
//注冊(cè)KVO開(kāi)啟進(jìn)度條
-(void)setWkWebview:(WKWebView *)wkWebview{
_wkWebview = wkWebview;
self.progressView.hidden = NO;
// NSLog(@"wkWebview:%@",self.wkWebview);
// KVO纤控,監(jiān)聽(tīng)webView屬性值得變化(estimatedProgress,title為特定的key)
[self.wkWebview addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
}
//得到html
-(void)setHtmlString:(NSString *)htmlString{
_htmlString = htmlString;
// reSizeImageWithHTML設(shè)置頭樣式
[self loadHTMLString:[self reSizeImageWithHTML:self.htmlString] baseURL:nil];
}
//創(chuàng)建UI
-(void)createUI{
//打開(kāi)垂直滾動(dòng)條
self.scrollView.showsVerticalScrollIndicator=YES;
//關(guān)閉水平滾動(dòng)條
self.scrollView.showsHorizontalScrollIndicator=NO;
//設(shè)置滾動(dòng)視圖的背景顏色
self.scrollView.backgroundColor = [UIColor groupTableViewBackgroundColor];
self.navigationDelegate = self;
//兩條屬性關(guān)閉黑影
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];
//開(kāi)始加載時(shí)不設(shè)置腳步坐標(biāo)
_footView.frame = CGRectZero;
_footView.backgroundColor = [UIColor whiteColor];
_footView.userInteractionEnabled = YES;
//先影藏
_footView.hidden = YES;
[self.scrollView addSubview:_footView];
//添加長(zhǎng)按手勢(shì)
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) {
//不創(chuàng)建點(diǎn)擊手勢(shì),不然點(diǎn)擊的時(shí)候又彈框有出現(xiàn)圖片瀏覽的
}else{
//添加點(diǎn)擊手勢(shì)(點(diǎn)擊查看大圖)
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]; // 設(shè)置進(jìn)度條的色彩
// self.progressView.progressTintColor = [UIColor blueColor];
// 設(shè)置初始的進(jìn)度碉纺,防止用戶(hù)進(jìn)來(lái)就懵逼了(微信大概也是一開(kāi)始設(shè)置的10%的默認(rèn)值)
self.progressView.hidden = YES;
[self.progressView setProgress:0.1 animated:YES];
[self addSubview:self.progressView];
}
//由于后臺(tái)返回的HTML代碼里……你懂的船万,所以只能添加樣式來(lái)適配屏幕,下面的方法可以調(diào)節(jié)文字和圖片大小來(lái)適配屏幕骨田。(刪者必死)
- (NSString *)reSizeImageWithHTML:(NSString *)html {
// 頂部留出空白(用來(lái)創(chuàng)建頭部視圖):<p style='padding-top:200px;'></p> 底部留出空白(用力啊創(chuàng)建尾部視圖):<p style='padding-bottom:200px;'></p>
//設(shè)置頭部耿导、尾部空白和設(shè)置html圖片自適應(yīng)手機(jī)
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];
}
//頁(yè)面加載完后獲取高度,設(shè)置腳,注意态贤,控制器里不能重寫(xiě)代理方法舱呻,否則這里會(huì)不執(zhí)行
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
// 不執(zhí)行前段界面彈出列表的JS代碼,關(guān)閉系統(tǒng)的長(zhǎng)按保存圖片
[self evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';" completionHandler:nil];
// WKWebView禁止放大縮小(捏合手勢(shì))
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];
//獲取網(wǎng)頁(yè)高度
[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);
//加載完設(shè)置好坐標(biāo)打開(kāi)
_footView.hidden = NO;
});
}];
//網(wǎng)頁(yè)加載完成后通過(guò)js獲取htlm中所有圖片url
[self getImageUrlByJS:self];
//清除WK緩存抵卫,否則H5界面跟新狮荔,這邊不會(huì)更新
[self remoViewCookies];
}
//清除WK緩存胎撇,否則H5界面跟新介粘,這邊不會(huì)更新
-(void)remoViewCookies{
if ([UIDevice currentDevice].systemVersion.floatValue>=9.0) {
// - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation 中就成功了 殖氏。
// 然而我們等到了iOS9!R霾伞雅采!沒(méi)錯(cuò)!WKWebView的緩存清除API出來(lái)了慨亲!代碼如下:這是刪除所有緩存和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];
// 獲取長(zhǎng)按位置對(duì)應(yīng)的圖片url的JS代碼
NSString *imgJS = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
// 執(zhí)行對(duì)應(yīng)的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(), ^{
//保存圖片到相冊(cè)
UIImageWriteToSavedPhotosAlbum(image, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), nil);
});
}
}];
}
}];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
//保存照片到本地相冊(cè)
- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
if(!error){
//延遲顯示婚瓜,否則會(huì)移除
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[MBProgressHUD showSuccess:@"保存成功"];
});
}else{
//延遲顯示,否則會(huì)移除
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[MBProgressHUD showSuccess:@"保存失敗"];
});
}
}
#pragma mark - KVO監(jiān)聽(tīng)進(jìn)度條
// 第三部:完成監(jiān)聽(tīng)方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([object isEqual:self] && [keyPath isEqualToString:@"estimatedProgress"]) { // 進(jìn)度條
CGFloat newprogress = [[change objectForKey:NSKeyValueChangeNewKey] doubleValue];
NSLog(@"網(wǎng)頁(yè)進(jìn)度:%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];
}
}
//暫時(shí)不用
//- (void)dealloc {
// NSLog(@"移除了KVO");
// // 最后一步:移除監(jiān)聽(tīng)的KVO
// [self.wkWebview removeObserver:self forKeyPath:@"estimatedProgress" context:nil];
//
//}
-(void)setRemoDeallocKvo:(NSString *)remoDeallocKvo{
_remoDeallocKvo = remoDeallocKvo;
NSLog(@"移除了KVO");
// 最后一步:移除監(jiān)聽(tīng)的KVO
[self.wkWebview removeObserver:self forKeyPath:@"estimatedProgress" context:nil];
}
#pragma mark ========= 點(diǎn)擊獲取所有圖片刑棵,并查看大圖 ========================
//通過(guò)js獲取htlm中圖片url
-(void)getImageUrlByJS:(WKWebView *)wkWebView{
//js方法遍歷圖片添加點(diǎn)擊事件返回圖片個(gè)數(shù)
//這里是js巴刻,主要目的實(shí)現(xiàn)對(duì)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獲取全部圖片,傳質(zhì)給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];
//添加到可變數(shù)組
[self.mArrayUrl addObjectsFromArray:array];
//移除最后一個(gè)元素(空白)
[self.mArrayUrl removeLastObject];
// NSLog(@"得到所有圖片url:%@",self.mArrayUrl);
}];
}
//點(diǎn)擊手勢(shì)
- (void)handleTapPress:(UITapGestureRecognizer *)sender{
CGPoint touchPoint = [sender locationInView:self];
// 獲取長(zhǎng)按位置對(duì)應(yīng)的圖片url的JS代碼
NSString *imgJS = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
// 執(zhí)行對(duì)應(yīng)的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才創(chuàng)建
if (self.mArrayUrl.count>0) {
NSInteger currentIndex = 0;
//得到索引
for (int i= 0; i< self.mArrayUrl.count; i++) {
if ([imgUrl isEqual:self.mArrayUrl[i]]) {
//當(dāng)前點(diǎn)擊了第幾張圖片
currentIndex = i;
}
}
//控制器跳轉(zhuǎn)
JZAlbumViewController *jzAlbumVC = [[JZAlbumViewController alloc]init];
//當(dāng)前點(diǎn)擊圖片的索引
jzAlbumVC.currentIndex = currentIndex;
//imgArr可以為url數(shù)組, 可以為urlString 數(shù)組, 可以為二進(jìn)制 UIImage 數(shù)組
jzAlbumVC.imgArr = self.mArrayUrl;
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:jzAlbumVC animated:NO completion:nil];
}else{
//如果加載完后拿不到所有圖片數(shù)組,就查看當(dāng)前點(diǎn)擊的圖片
//控制器跳轉(zhuǎn)
JZAlbumViewController *jzAlbumVC = [[JZAlbumViewController alloc]init];
//當(dāng)前點(diǎn)擊圖片的索引
jzAlbumVC.currentIndex = 0;
//imgArr可以為url數(shù)組, 可以為urlString 數(shù)組, 可以為二進(jìn)制 UIImage 數(shù)組
jzAlbumVC.imgArr = [NSMutableArray arrayWithArray:@[imgUrl]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:jzAlbumVC animated:NO completion:nil];
}
}
}];
}
//圖片數(shù)組
-(NSMutableArray *)mArrayUrl{
if (!_mArrayUrl) {
_mArrayUrl = [NSMutableArray array];
}
return _mArrayUrl;
}
#pragma mark======================WKWebView - alert不彈出(這是WKWebView相對(duì)于UIWebView的一個(gè)坑)===========================================
//WKWebView默認(rèn)不響應(yīng)js的alert()事件,如何可以開(kāi)啟alert權(quán)限呢?
//設(shè)置wkwebview.delegate = self;
//實(shí)現(xiàn)下面三個(gè)方法:
- (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:@"確認(rèn)" 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:@"確認(rèn)" 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打開(kāi)新界面 需要打開(kāi)新界面是,WKWebView的代理WKUIDelegate方法
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
// 會(huì)攔截到window.open()事件.只需要我們?cè)谠诜椒▋?nèi)進(jìn)行處理
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
@end