自己封裝的WKWebView(OC)

自己封裝的HtmlWKWebView(OC)

一、.h文件里
/*
  (1)適用于HTML解析和網(wǎng)頁(yè)鏈接的解析,不適用與與JS的交互,因?yàn)榇矸椒刂破骼锊蛔撸侵貙懘矸椒?  (2)WKWebView的封裝所有屬性,包括進(jìn)度條、長(zhǎng)按保存圖片、圖片自適應(yīng)手機(jī)、WKWebView的頭部控件、尾部控件
 (3)使用時(shí)一定要先設(shè)置完頭部和尾部控件,在開始加載網(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開啟進(jìn)度條
-(void)setWkWebview:(WKWebView *)wkWebview{
    _wkWebview = wkWebview;
    self.progressView.hidden = NO;
//    NSLog(@"wkWebview:%@",self.wkWebview);
    // KVO,監(jiān)聽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{
      //打開垂直滾動(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];
    //開始加載時(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)度,防止用戶進(jìn)來(lá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è)置腳,注意,控制器里不能重寫代理方法,否則這里會(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)打開
            _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?。?!沒(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)聽進(jìn)度條
// 第三部:完成監(jiān)聽方法
- (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)聽的KVO
//    [self.wkWebview removeObserver:self forKeyPath:@"estimatedProgress" context:nil];
//
//}

-(void)setRemoDeallocKvo:(NSString *)remoDeallocKvo{
    _remoDeallocKvo = remoDeallocKvo;
    NSLog(@"移除了KVO");
    // 最后一步:移除監(jiān)聽的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()事件,如何可以開啟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打開新界面 需要打開新界面是,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


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容