iOS开发-高级汇总

wkwebview开发常见问题(wkwebview加载本地沙盒文

2019-01-04  本文已影响195人  顺情风

1、#pragma mark -https认证

//web项目里面,使用了https认证的问题
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential,card);
    }
}

2、隐藏状态栏:

    NSString *phoneVersion = [[UIDevice currentDevice] systemVersion];
    NSArray *versionarr = [phoneVersion componentsSeparatedByString:@"."];
    if ([[versionarr objectAtIndex:0] integerValue]<11) {
        self.edgesForExtendedLayout = UIRectEdgeNone;
    }else{
        self.myweb.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;//隐藏顶部状态栏,还要设置空间全屏
    }

3、禁止WKWebView的手势捏拉缩放

//推选这个方法;这个方法在11.3.1和11.2.6都有效
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
    // 禁止放大缩小
    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];
}

 //这个方法在11.3.1无效。11.2.6有效
    self.myweb.scrollView.delegate = self;
//禁止手指捏合和放大
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return nil;
}

4、我第一次合作是ionic写的界面。
WKPreferences preferences = [WKPreferences new];
preferences.minimumFontSize = 40.0;
后来另外一个web工程师用vue写的界面。同样的工程加载出来的界面,总是fontSize很大。
我更改 preferences.minimumFontSize = 15.0;解决了这个问题。minimumFontSize大致意思:web的一个px是多大。官网是
/
! @abstract The minimum font size in points.
@discussion The default value is 0.
*/
5、弹出系统提示框:

//web界面中有弹出警告框时调用
- (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();
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}
- (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:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }])];
    [self 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?:@"");
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}

6、三种加载html的方式:

    NSString *urlstr = @"https://junction.dev.havensphere.com/static/086test1/r003/phone/index.html";
    NSURL *url =[NSURL URLWithString:urlstr];
    NSURLRequest *request =[NSURLRequest requestWithURL:url];
    [self.myweb loadRequest:request];
    [self.view addSubview:self.myweb];
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    WKPreferences *preferences = [WKPreferences new];
    preferences.javaScriptCanOpenWindowsAutomatically = YES;
    preferences.minimumFontSize = 15.0;
    configuration.preferences = preferences;

    self.myweb = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, Screen_Width, Screen_Height) configuration:configuration];
    self.myweb.navigationDelegate = self;
    self.myweb.UIDelegate = self;
    self.myweb.scrollView.delegate = self;
    self.myweb.scrollView.contentSize= CGSizeMake(Screen_Width, Screen_Height);
    self.myweb.scrollView.bounces = false;

    NSString *bundleFile = [[NSBundle mainBundle] pathForResource:@"HTML" ofType:@"bundle"];
    NSString *path = [bundleFile stringByAppendingString:@"/dist/index.html"];
    NSURL *fileURL = [NSURL fileURLWithPath:path];
    NSURLRequest *request = [NSURLRequest requestWithURL:fileURL];
    [self.myweb loadRequest:request];
    [self.view addSubview:self.myweb];

    NSString *phoneVersion = [[UIDevice currentDevice] systemVersion];
    NSArray *versionarr = [phoneVersion componentsSeparatedByString:@"."];
    if ([[versionarr objectAtIndex:0] integerValue]<11) {
        self.edgesForExtendedLayout = UIRectEdgeNone;
    }else{
        self.myweb.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;//隐藏顶部状态栏,还要设置空间全屏
        self.mywebui.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;//隐藏顶部状态栏,还要设置空间全屏
    }    
//点击web界面跳转时候执行的方法
-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
    NSLog(@"createWebViewWithConfiguration");
    if (!navigationAction.targetFrame.isMainFrame) {
        [webView loadRequest:navigationAction.request];
    }
    return nil;
}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    decisionHandler(WKNavigationActionPolicyAllow);
    return;
}
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
    //    [MBProgressHUD showInView:self.view message:@"正在加载网页!!"];
    [self.activityIndiactorView  startAnimating];
    NSLog(@"didStartProvisionalNavigation");
}
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    decisionHandler(WKNavigationResponsePolicyAllow);
    NSLog(@"decidePolicyForNavigationResponse");
    self.responsecount++;
    if (self.responsecount==7) {//处理加载时间很长的时候的加载问题
        [self.activityIndiactorView  stopAnimating];
    }
    return;
}

// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
    self.responsecount = 0;
    NSLog(@"didCommitNavigation");
}
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    [self.activityIndiactorView  stopAnimating];
    NSLog(@"didFinishNavigation");
}
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
    [self.activityIndiactorView  stopAnimating];
    NSLog(@"didFailProvisionalNavigation");
}
// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
    NSLog(@"%s", __FUNCTION__);
}
//请求当前项目压缩包版本号。如果版本号比本地版本号大或者本地没有,请求zip压缩包;如果版本号和本地一样,直接加载本地web数据。
-(void)getversion{
    [MBProgressHUD showInView:self.view indicatorMessage:@"正在加载网页版本数据" duration:-1];
    // 1.获得请求管理者
    AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
    // 2.申明返回的结果是text/html类型
    mgr.responseSerializer = [ AFJSONResponseSerializer serializer];
    // 3.发送GET请求
    [mgr GET:@"http://101.132.91.12:8681/iot/app/info?id=1" parameters:nil progress::^(NSProgress * _Nonnull downloadProgress) {
        NSLog(@"%@",downloadProgress);
    }  success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSDictionary *dic = (NSDictionary *)responseObject;
        NSString *version = [[[dic objectForKey:@"content"]objectAtIndex:0]objectForKey:@"versioncode"];
        NSString *defaultsversion = [[NSUserDefaults standardUserDefaults] objectForKey:@"versioncode"];
        self.defaultsversion = version;
        if (defaultsversion==nil) {
            [MBProgressHUD showInView:self.view indicatorMessage:@"请求云端数据,请稍等" duration:-1];
            [self rquestZipArchivePath:@"http://101.132.91.12:8681/iot/file/app/iot.zip"];
            return ;
        }
        if ([version intValue]>[defaultsversion intValue]) {
            [MBProgressHUD showInView:self.view indicatorMessage:@"请求云端数据,请稍等" duration:-1];
            [self rquestZipArchivePath:@"http://101.132.91.12:8681/iot/file/app/iot.zip"];
            return ;
        }
        NSArray *documentArray =  NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
        NSString *path1 = [[documentArray lastObject] stringByAppendingPathComponent:@"Preferences"];
        if([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"%@/iot",path1]])
        {
            [self loadWebData];
        }else{
            [MBProgressHUD showInView:self.view indicatorMessage:@"请求云端数据,请稍等" duration:-1];
            [self rquestZipArchivePath:@"http://101.132.91.12:8681/iot/file/app/iot.zip"];
        }
        NSLog(@"responseObject:%@",version);
        return ;
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        [MBProgressHUD hideHUDForView:self.view];
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"温馨提示" message:@"本地网页版本不存在,请重启软件程序!" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK",nil];
        [alert show];

        NSLog(@"error:%@",error);
    }];
}
#pragma mark 请求zip地址
//请求到的压缩包数据,进行解压
-(void)rquestZipArchivePath:(NSString *)pathUrl{
    //远程地址
    NSURL *URL = [NSURL URLWithString:pathUrl];
    //默认配置
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    //请求
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    NSURLSessionDownloadTask * downloadTask= [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
        //- block的返回值, 要求返回一个URL, 返回的这个URL就是文件的位置的路径
        
        NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        
        //再次之前先删除本地文件夹里面相同的文件夹
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSArray *contents = [fileManager contentsOfDirectoryAtPath:cachesPath error:NULL];
        NSEnumerator *e = [contents objectEnumerator];
        NSString *filename;
        NSString *extension = @"zip";
        while ((filename = [e nextObject])) {
            if ([[filename pathExtension] isEqualToString:extension]) {
                [fileManager removeItemAtPath:[cachesPath stringByAppendingPathComponent:filename] error:NULL];
            }
        }
        NSString *path = [cachesPath stringByAppendingPathComponent:response.suggestedFilename];
        return [NSURL fileURLWithPath:path];
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        if (error==nil) {
            //设置下载完成操作
            // filePath就是你下载文件的位置,你可以解压,也可以直接拿来使用
            NSString *htmlFilePath = [filePath path];// 将NSURL转成NSString
            NSArray *documentArray =  NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
            NSString *path = [[documentArray lastObject] stringByAppendingPathComponent:@"Preferences/"];
            NSFileManager *fileManager = [NSFileManager defaultManager];
            [fileManager removeItemAtPath:[NSString stringWithFormat:@"%@/iot",path] error:nil];
            [self releaseZipFilesWithUnzipFileAtPath:htmlFilePath Destination:path];
            return ;
        }
        [MBProgressHUD hideHUDForView:self.view];
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"温馨提示" message:@"网页数据下载失败,请重启软件程序!" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK",nil];
        [alert show];
    }];
    [downloadTask resume];
}
#pragma mark 解压
//把压缩包数据解压到本地后,要想加载出来,必须把document数据复制到Preferences文件夹
- (void)releaseZipFilesWithUnzipFileAtPath:(NSString *)zipPath Destination:(NSString *)unzipPath{
    NSError *error;
    if ([SSZipArchive unzipFileAtPath:zipPath toDestination:unzipPath overwrite:YES password:nil error:&error delegate:self]) {
        
        NSString *path = [unzipPath stringByAppendingString:@"/iot"];
        NSFileManager *fileManager1 = [NSFileManager defaultManager];
        NSString *tmpPath2 = NSTemporaryDirectory();
        if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"%@iot",tmpPath2]]){
            [fileManager1 removeItemAtPath:[NSString stringWithFormat:@"%@iot",tmpPath2] error:nil];
        }
        [fileManager1 copyItemAtPath:[NSString stringWithFormat:@"%@/iot",path] toPath:[NSString stringWithFormat:@"%@iot",tmpPath2] error:nil];
        [[NSUserDefaults standardUserDefaults] setObject:self.defaultsversion forKey:@"versioncode"];
        [[NSUserDefaults standardUserDefaults] synchronize];
        [self loadWebData];
//        NSLog(@"解压缩成功!");
    }
    else{
        [MBProgressHUD hideHUDForView:self.view];
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"温馨提示" message:@"解压数据失败,请重启软件程序!" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK",nil];
        [alert show];
    }
}
//加载本地沙盒目录下的index.html(本地沙盒目录指这几个:Library/Preferences、Library/Caches、Documents)
-(void)loadWebData{
    NSArray *documentArray =  NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *path1 = [[documentArray lastObject] stringByAppendingPathComponent:@"Preferences"];
    NSFileManager *fileManager1 = [NSFileManager defaultManager];
    if([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"%@/iot",path1]])
    {
    //第一步:找到文件所在目录
        NSArray *LibraryArray =  NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
        NSString *CachesPath = [[LibraryArray lastObject] stringByAppendingPathComponent:@"Caches"];
        NSString *indexPath = [CachesPath stringByAppendingPathComponent:@"/dist/index.html"];
        //第二步:创建加载URL和访问权限URL(加载URL:访问的初始文件一般是index;访问权限URL:所有html、js、资源文件所在的文件夹一般是项目文件名字)。注意如果没有访问权限URL,html没办法加载相关的js和资源文件。
        //创建加载URL方法一:
    //    indexPath=[[NSURL fileURLWithPath:indexPath]absoluteString];
    //    NSURL *loadurl =[NSURL URLWithString:indexPath];//URLWithString后面的path1必须前面有file:///,[[NSURL fileURLWithPath:path1]absoluteString]这个处理可以得到file:///
        //创建加载URL方法二:
        NSURL *loadurl =[NSURL fileURLWithPath:indexPath];//fileURLWithPath后面跟的是文件目录不需要file:///
        //创建访问权限URL
        NSString *accessURLStr = [[[LibraryArray lastObject] stringByAppendingPathComponent:@"Caches"] stringByAppendingPathComponent:@"/dist"];
        NSURL *accessURL = [NSURL fileURLWithPath:accessURLStr];
        //第三步:进行加载
        [self.myweb loadFileURL:loadurl allowingReadAccessToURL:accessURL];
        [MBProgressHUD showInView:self.view successMessage:@"网页加载成功!"];
    }else{
        [MBProgressHUD hideHUDForView:self.view];
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"温馨提示" message:@"本地网页配置文件不存在,请重启软件程序!" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK",nil];
        [alert show];
    }
}

注意:
1、解压缩功能要导入SSZipArchive库(包含minizip和aes),导入后出现了20多个错误,解决办法:
选择所有.c文件,将属性的 identity and type 改为Objective-C Source
2、下载功能需要导入AFNetworking库。
3、加载本地沙盒功能主要针对文件夹里面除了html还有js和其它资源文件,如果只有一个html文件,那accessURL就是loadURL。这里沙盒目录主要指:Library/Preferences、Library/Caches、Documents(建议使用Library/Caches)。

7、js和原生交互:
参考文章:iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge

1、引入工作:WebViewJavascriptBridge文件夹
@property WKWebViewJavascriptBridge *webViewBridge;
2、创建WebViewJavascriptBridge实例。
    _webViewBridge = [WKWebViewJavascriptBridge bridgeForWebView:self.webView];
// 如果控制器里需要监听WKWebView 的`navigationDelegate`方法,就需要添加下面这行。
[_webViewBridge setWebViewDelegate:self];
3、iOS语法:web调用iOS的方法:
    [_webViewBridge registerHandler:@"locationClick" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSDictionary *tempDic = data;
        NSLog(@"%@",tempDic);
        // 获取位置信息
        NSString *location = @"广东省深圳市南山区学府路XXXX号";
        // 将结果返回给js        
        responseCallback(location);//responseCallback(nil);
    }];
注意:
(1)、js可以传数字给iOS,iOS通过[data isKindOfClass:[NSNumber class]]判断是不是数字类型
(2)、iOS返回可以是数字类型如@1。
(3)、无返回的情况
js方法:
 WebViewJavascriptBridge.callHandler('colorClick',params);
iOS方法:
 [_webViewBridge registerHandler:@"colorClick" handler:^(id data, WVJBResponseCallback responseCallback) {
 // data 的类型与 JS中传的参数有关
 NSDictionary *tempDic = data;
 
 CGFloat r = [[tempDic objectForKey:@"r"] floatValue];
 CGFloat g = [[tempDic objectForKey:@"g"] floatValue];
 CGFloat b = [[tempDic objectForKey:@"b"] floatValue];
 CGFloat a = [[tempDic objectForKey:@"a"] floatValue];
 
 weakSelf.webView.backgroundColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a];
 }];
(4)、传入数字类型时候,如果用数字类型作为key去找[[NSUserDefaults standardUserDefaults] objectForKey:key],会导致崩溃。
4、iOS语法:iOS调用web方法:
* (1)、传字符串给js,所以如果是json或者array都需要转换成字符串传递。
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    NSData *jsonData= [NSJSONSerialization dataWithJSONObject:dic options:0 error:nil];
    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    NSString *jsStr = [NSString stringWithFormat:@"backMQTTData('%@')",jsonString];
    [self.myweb evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {}];//wkwebview执行方法
* (2)、传数字给js。(传bool类型时候数字为0或者1就可以)
        NSString *jsStr = [NSString stringWithFormat:@"iOSCallJS(%@)",@0];
        [self.myweb evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {}];//wkwebview执行方法
                alert(typeof(istrue));//number
                alert(istrue);
5、js语法:
 if (window.AndroidWebView) {

  }else if(window.WebViewJavascriptBridge){
         WebViewJavascriptBridge.callHandler(funName,iosKey,function(response) {
            if(methodType==APPCONFIG.METHOD_GET){
                callback(response);
            }
         });
    }
注意:调用安卓方法时候是顺序执行的,而调用iOS的时候顺序执行完成后,在回调到callHandler里面的。
上一篇下一篇

猜你喜欢

热点阅读