美文网首页iOS开发-高级汇总
wkwebview开发常见问题(wkwebview加载本地沙盒文

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

作者: 顺情风 | 来源:发表于2019-01-04 09:33 被阅读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的方式:

    • 1、加载远程网页:
        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];
    
    • 2、加载项目工程网页:
        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__);
    }
    
    • 3从接口下载压缩包,解压缩再加载出来。(本地沙盒目录指这几个:Library/Preferences、Library/Caches、Documents
    //请求当前项目压缩包版本号。如果版本号比本地版本号大或者本地没有,请求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里面的。
    

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/gruaeftx.html