WKWebView,wkwebview加载HTML字符串

廉价感情. 提交于 2020-12-23 07:32:18

wkwebview使用时需要导入(#import <WebKit/WebKit.h>)

WKWebView从iOS8才有,毫无疑问WKWebView将逐步取代笨重的UIWebView。通过简单的测试即可发现UIWebView占用过多内存,且内存峰值更是夸张。WKWebView网页加载速度也有提升,但是并不像内存那样提升那么多。下面列举一些其它的优势:

  • 更多的支持HTML5的特性
  • 官方宣称的高达60fps的滚动刷新率以及内置手势
  • Safari相同的JavaScript引擎
  • 将UIWebViewDelegate与UIWebView拆分成了14类与3个协议(官方文档说明)
  • 另外用的比较多的,增加加载进度属性:estimatedProgress

常用属性:

@property (nonatomic, readonly) BOOL canGoBack;
@property (nonatomic, readonly) BOOL canGoForward;
- (WKNavigation *)goBack;
- (WKNavigation *)goForward;
- (WKNavigation *)reload;
- (void)stopLoading;

/*  
  reloadFromOrigin会比较网络数据是否有变化,没有变化则使用缓存,否则从新请求。
  goToBackForwardListItem:比向前向后更强大,可以跳转到某个指定历史页面
*/
- (WKNavigation *)reloadFromOrigin;
- (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;
// 是否允许左右划手势导航,默认不允许
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;
// 加载进度 0 ~ 1
@property (nonatomic, readonly) double estimatedProgress;

@property (nullable, nonatomic, readonly, copy) NSString *title;
// 访问历史列表,可以通过前进后退按钮访问,或者通过goToBackForwardListItem函数跳到指定页面
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;

常用方法:

- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL


#pragma mark - WKNavigationDelegate
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{

}
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{

}
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{

}
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{

}
// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{

}
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{

    NSLog(@"%@",navigationResponse.response.URL.absoluteString);
    //允许跳转
    decisionHandler(WKNavigationResponsePolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationResponsePolicyCancel);
}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{

     NSLog(@"%@",navigationAction.request.URL.absoluteString);
    //允许跳转
    decisionHandler(WKNavigationActionPolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationActionPolicyCancel);
}



#pragma mark - WKUIDelegate
// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{
    return [[WKWebView alloc]init];
}
// 输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{
    completionHandler(@"http");
}
// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{
    completionHandler(YES);
}
// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    NSLog(@"%@",message);
    completionHandler();
}

 网页加载进度监听:

// 注册观察者
[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];

// 监听网页加载进度
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    [self.progressView setProgress:self.webView.estimatedProgress animated:YES];
    
    if (self.progressView.progress == 1) {
        self.progressView.hidden = YES;
    }

}

// 开始加载
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
    self.progressView.hidden = NO;
    
}

// 加载完成
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    self.progressView.hidden = YES;
    
}

// 加载失败
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
    self.progressView.hidden = YES;
    
}

js调动oc

首先需要添加WKScriptMessageHandler协议

// 为了避免循环引用,在viewWillAppear中添加js事件,在viewDidDisappear中移除
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
// 配置js环境
WKUserContentController *userCC = self.webView.configuration.userContentController;
// 添加js事件 
[userCC addScriptMessageHandler:self name:@"onClickName"];

}

// 处理js交互
// 前端必须使用window.webkit.messageHandlers.注册的方法名.postMessage({body:传输的数据}
/* 示例
window.webkit.messageHandlers.uploadRebate.postMessage({platName:data.platName,platId:data.platId,name:data.name,id:data.id})
前端给的方法名为uploadRebate,给的参数是一个字典
*/
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    NSLog(@"拦截的方法名%@ , 拦截的方法传过来的参数%@", message.name, message.body);
    
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];{
    
    // 移除js
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"onClickName"];
}

oc调用js

// 加载完成
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    
    //say()是JS方法名,completionHandler是异步回调block
    [self.webView evaluateJavaScript:@"say()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@",result);
    }];
}

清除缓存

// 清除缓存
+(void)deleteWebCache{
   
    if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
        /*
         在磁盘缓存上。
         WKWebsiteDataTypeDiskCache,
         html离线Web应用程序缓存。
         WKWebsiteDataTypeOfflineWebApplicationCache,
         内存缓存。
         WKWebsiteDataTypeMemoryCache,
         本地存储。
         WKWebsiteDataTypeLocalStorage,
         Cookies
         WKWebsiteDataTypeCookies,
         会话存储
         WKWebsiteDataTypeSessionStorage, IndexedDB数据库。
         WKWebsiteDataTypeIndexedDBDatabases,
         查询数据库。
         WKWebsiteDataTypeWebSQLDatabases
         */
        NSArray * types=@[WKWebsiteDataTypeCookies,WKWebsiteDataTypeLocalStorage];
        NSSet *websiteDataTypes= [NSSet setWithArray:types];
        NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
        [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
            
        }];
 
    } else {
        
        // 清楚Library目录下的 Cookies文件夹与WebKit文件夹里面的内容
        NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        
        NSString *cookiesFolderPath = [libraryPath stringByAppendingString:@"/Cookies"];
        NSError *errors;
        [[NSFileManager defaultManager] removeItemAtPath:cookiesFolderPath error:&errors];
        
        NSString *WebKit = [libraryPath stringByAppendingString:@"/WebKit"];
        NSError *error;
        [[NSFileManager defaultManager] removeItemAtPath:WebKit error:&error];
   
    }

}

页面导航逐级返回

if ([webV canGoBack]) {
            
  [webV goBack];
            
}else{
            
  [self.navigationController popViewControllerAnimated:YES];
}

监听网页链接的变化

有时候监听跳转的代理不走,不知道为什么,只能用kvo先解决一下了

[_webViews addObserver:self forKeyPath:@"URL" options:NSKeyValueObservingOptionNew context:nil];

-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context{

NSLog(@"url == %@",_webViews.URL.absoluteString);

}

小知识,wkwebview播放背景音乐

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];

config.allowsInlineMediaPlayback = YES;

config.mediaPlaybackRequiresUserAction = false;

wkWebView=[[WKWebView alloc] initWithFrame:rect configuration:config];

wkWebView.UIDelegate=self;

wkWebView.navigationDelegate=self;

 

wkwebview加载HTML字符串

- (void)setWKWeb{
    //js脚本 (脚本注入设置网页样式)
    NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
    //注入
    WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    WKUserContentController *wkUController = [[WKUserContentController alloc] init];
    [wkUController addUserScript:wkUScript];
    //配置对象
    WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
    wkWebConfig.userContentController = wkUController;
    //改变初始化方法 (这里一定要给个初始宽度,要不算的高度不对)
    WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, BKSCREEN_WIDTH - 30, 0) configuration:wkWebConfig];
    webView.scrollView.bounces = NO;
    _wkWeb = webView;
    _wkWeb.navigationDelegate = self;
    
}

//网页加载完成
//页面加载完后获取高度,设置脚,注意,控制器里不能重写代理方法,否则这里会不执行
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    
    //    document.body.scrollHeight(加载HTML源站用这个)   document.body.offsetHeight;(加载HTML字符串)
    [webView evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id Result, NSError * error) {
        NSString *heightStr = [NSString stringWithFormat:@"%@",Result];
        
        //必须加上一点
        CGFloat height = heightStr.floatValue+15.00;
        //网页加载完成
        NSLog(@"新闻加载完成网页高度:%f",height);
        self.wkWebHeigh = height;
        [self.tableV reloadData];
    }];
}

补全网页代码,设置图片适应屏幕宽度(遍历网页中所有的图片,设置$img[p].style.width = '100%%')

NSString *htmlString = [NSString stringWithFormat:@"<html> \n"
                                "<head> \n"
                                "<style type=\"text/css\"> \n"
                                "body {font-size:15px;}\n"
                                "</style> \n"
                                "</head> \n"
                                "<body>"
                                "<script type='text/javascript'>"
                                "window.onload = function(){\n"
                                "var $img = document.getElementsByTagName('img');\n"
                                "for(var p in  $img){\n"
                                " $img[p].style.width = '100%%';\n"
                                "$img[p].style.height ='auto'\n"
                                " let height = document.body.offsetHeight;\n"   "window.webkit.messageHandlers.imagLoaded.postMessage(height);\n"
                                "}\n"
                                "}"
                                "</script>%@"
                                "</body>"
                                "</html>", self.baseModel.desc];
        BKLog(@"HTML字符串:%@", htmlString);
        [self.wkWeb loadHTMLString:htmlString baseURL:nil];

计算高度方法二:(在 HTML DOM 中 Event 有个函数 onload 是用于一张页面或一幅图像完成加载时所执行的,我们需要监听所有的 img标签 或 body标签,然后在这个方法里发个消息给 WebKit 然后进行拦截)

" let height = document.body.offsetHeight;\n"   "window.webkit.messageHandlers.imagLoaded.postMessage(height);\n"

上面补全网页代码中这两行就是监听发送高度通知,然后我们只需要拦截imagLoaded这个js方法就可以拿到(document.body.offsetHeight)也就是网页的高度了

[self.wkWeb.configuration.userContentController addScriptMessageHandler:self name:@"imagLoaded"];


- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    NSLog(@"拦截的方法名%@ , 拦截的方法传过来的参数%@", message.name, message.body);
    if ([message.name isEqualToString:@"imagLoaded"]) {
        CGFloat height = [message.body floatValue];
        self.wkWebHeigh = height;
        [self.tableV reloadData];
    }
}

计算高度方法三:监听网页的加载状态(loading),比监听"scrollView.contentSize"属性调用的少

[self.wkWeb addObserver:self forKeyPath:@"loading" options:NSKeyValueObservingOptionNew context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    if ([keyPath isEqualToString:@"loading"]) {
        [self.wkWeb evaluateJavaScript:@"document.body.scrollHeight" completionHandler:^(id Result, NSError * error) {
            NSString *heightStr = [NSString stringWithFormat:@"%@",Result];
            
            //必须加上一点
            CGFloat height = heightStr.floatValue;
            if (height == 0) {
                return ;
            }
            //网页加载完成
            NSLog(@"新闻加载完成网页高度:%f",height);
            self.wkWebHeigh = height;
            [self.tableV reloadData];
        }];
        
    }
}

内存泄漏解决:

@interface WeakScriptMessageDelegate : NSObject

@property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;

- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;

@end
@implementation WeakScriptMessageDelegate

- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
    
    self = [super init];
    if (self) {
        _scriptDelegate = scriptDelegate;
    }
    return self;
}

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    
    [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
}

@end

注入方法改写:

[config.userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:scriptMessage];

dealloc中移除:

[self.config.userContentController removeScriptMessageHandlerForName:scriptMessage];

图片自适应屏幕宽度:

// 在网页加载完成的代理中添加如下js就行了 maxwidth可以自己设置

NSString *js = @"function imgAutoFit() { \

    var maxwidth = %f;\

    var imgs = document.getElementsByTagName('img'); \

    for (var i = 0; i < imgs.length; ++i) {\

    var img = imgs[i];   \

    if(img.width > maxwidth){ \

    img.width = maxwidth;   \

    }\

    } \

    }";

    js = [NSString stringWithFormat:js, self.meetingContentWebView.bounds.size.width];

    [webView evaluateJavaScript:js completionHandler:nil];

采坑:

1、IOS11一下,wkwebview加载网页只显示一半。。。。(tmd,IOS11一下系统渲染问题,wkwebview不太兼容)

滑动时不停的刷新布局就行了

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.tableV) {
        [self.wkWeb setNeedsLayout];
    }
   
}

 

 

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!