美文网首页
iOS HTML加载本地资源

iOS HTML加载本地资源

作者: EnjoyWT | 来源:发表于2018-05-15 14:52 被阅读68次
    0.0 需求:如标题.

    这个其实是cordova远程加载html.各种插件js放在本地的一个需求.

    问题分析:
        1. 社区开源的意思是啥(我也不懂)
        2. read error , search Google , ask help
    
    以下非原创.

    昨天,一个朋友让我帮他在iOS上弄这样一件事情:
    webView 调用远程URL,并且让远程的web 通过自定义标签能实现内嵌本地的图片、js 或音频等。
    比如:在服务器端 的html文件中 这样写到

    <html>
        <body>
            <h1>we are loading a custom protocl</h1>
            <b>image?</b><br/>
            <img src="myapp://image1.png" />
        <body>
    </html>
    

    那么当这个页面被iOS 中webView 显示的时,当渲染到 myapp://image1.png 的自定义标签的时候能将本地的图片资源 替换进去。这样的好处就是在网络上无需传输图片,性能比较高。

    我朋友的项目是基于cordova 框架,一开始我还不是很理解他为什么说要远程web 调用本地资源,在我的脑海里面就是:“这个框架js 不都是本地的吗????”,然后他告诉我是他在cordova 框架中导航到 自己的web 服务器。 我听了之后就只能用“呵呵” 表示了,好吧...也就不管了。

    那么我就想到其实cordova框架就是基于webView 的一个事件拦截和封装的。 其实它是对NSURLProtocol 的自定义累进行注册,那么所有的webview 对http请求都会被他拦截到;
    这里我们可以做很多事情;
    接下来我们自己做自己的 NSURLProtocol 类吧

    #import <Foundation/Foundation.h>
    #import <CoreFoundation/CoreFoundation.h>
    #import <MobileCoreServices/MobileCoreServices.h>
    
    @interface NSURLProtocolCustom : NSURLProtocol  //在项目中添加自定义NSURLProtocolCustom 并且继承NSURLProtocol
    {}
    //实现中重现如下几个方法
    
    @implementation NSURLProtocolCustom
    
    //重写方法 1
     +(BOOL)canInitWithRequest:(NSURLRequest *)request
    {
        NSLog(@"canInitWithRequest");
       // 这里是html 渲染时候入口,来处理自定义标签 如 "myapp",若return YES 则会执行接下来的 -startLoading方法 
    
        if ([request.URL.scheme caseInsensitiveCompare:@"myapp"] == NSOrderedSame||
    
            [request.URL.scheme caseInsensitiveCompare:@"app"] == NSOrderedSame) {
               return YES;
        }
      return NO;
    }
    //重写方法
    +(NSURLRequest*)canonicalRequestForRequest:(NSURLRequest *)request
    {
        NSLog(@"canInitWithRequest");
        return request;
    }
    
    //重写方法
    -(void)startLoading
    {
      //处理自定义标签 ,并实现内嵌本地资源
        NSLog(@"startLoading");
        NSLog(@"%@", super.request.URL);
        NSString *url=super.request.URL.resourceSpecifier;// 得到//image1.png"
    
        //去掉 //前缀()
        url=[url substringFromIndex:2];//image1.png
    
        //若是app 协议 需要添加www (这里是我们自己业务上的吹)
        if ([super.request.URL.scheme caseInsensitiveCompare:@"app"]) {
            url=[[NSString alloc] initWithFormat:@"www/%@",url];
        }
    //  NSString *path=  [[NSBundle mainBundle] pathForResource:@"www/image1.png" ofType:nil];
    
          NSString *path=  [[NSBundle mainBundle] pathForResource:url ofType:nil];//这里是获取本地资源路径 如 :png,js 等
    
        if (!path) {
            return;
        }
    
        //根据路径获取MIMEType   (以下函数方法需要添加.h文件的引用,)
    
           // Get the UTI from the file's extension:
        CFStringRef pathExtension = (__bridge_retained CFStringRef)[path pathExtension];
        CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, NULL);
    
        CFRelease(pathExtension);
    
        // The UTI can be converted to a mime type:
        NSString *mimeType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType);
    
        if (type != NULL)
            CFRelease(type);
    
      // 这里需要用到MIMEType
        NSURLResponse *response = [[NSURLResponse alloc] initWithURL:super.request.URL    MIMEType:mimeType expectedContentLength:-1 textEncodingName:nil];
        NSData *data = [NSData dataWithContentsOfFile:path];//加载本地资源
    
        //硬编码 开始嵌入本地资源到web中
        [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [[self client] URLProtocol:self didLoadData:data];
        [[self client] URLProtocolDidFinishLoading:self];
    }
    -(void)stopLoading
    {
        NSLog(@"something went wrong!");
    }
     @end
    

    类已经实现好了 那么怎样调用呢???
    其他代码都已经省略了,核心如下:

     -(void)viewDidLoad {
        [super viewDidLoad];
         // 这里可以看出 只要注册一次就够了。。。我们可以将它写在delegate 入口就可以实现所有的请求拦截
         [NSURLProtocol registerClass:[NSURLProtocolCustom class]];
        //测试: 这里webView 我是直接从interface build 中引用过来的所以没有自定义实例化。
    
        self.myWebView.backgroundColor = [UIColor  redColor];
        self.myWebView.scalesPageToFit =YES;
        self.myWebView.delegate =self;
        NSURL *url =[[NSURL alloc] initWithString:@"http://192.168.199.197/soqik/test.html"];//地址可以是远程地址也可以是本地的html 方法
    
      NSURLRequest *request =  [[NSURLRequest alloc] initWithURL:url];
       [self.myWebView loadRequest:request];
    }
    

    到这里为止远程web调用本地的js 或者图片资源已经完成了,接下来就是怎样在cordova 中进行改造。。。。原本以为在cordova中这样弄进去就可以了,但是发现这样是不行的,原因很简单:它们已经对 这个封装过,所以必须改造它们的对象。经过一定时间的研究 最终发现改造需要到-->CDVURLProtocol.h类中实现

    那么这里需要注意的是:若资源找不到则需要调用Cordova封装的方法
    //错误处理,而不是直接返回nil 不进行任何处理,这样会导致js 无法正常加载、运行

    -(void)startLoading{
      ....//省略
    
    if (!path) {
    
    [self sendResponseWithResponseCode:401 data:nil mimeType:nil];//重要
    return;
    }
    ...//省略
    //否则
    NSData *data = [NSData dataWithContentsOfFile:path];
    
    [self sendResponseWithResponseCode:200 data:data mimeType:mimeType];
    }
    

    0. 值得学习的博主
    1. stackoverflow 链接 关键词 iOS WebView remote html with local image files
    2. 安卓版的相关知识

    1. 容易忽略点.
    1. 上面需要加载的本地链接.是遵守协议规范格式的
    2. Cordova项目 直接引入cordova.js就可以. cordova_plugin.js 会自定加载.但是如果1.你不注意是不会正确加载的(当加载不出来的时候去看cordova的源码吧.会有收获的)
    3. 心细一点,什么错误都不会有的
    4. 不修改cordova的源码,注册自定义的协议类, 也可以实现相同的效果我注册是放在appdelegate中的
    5. 只是为了提醒第4点, 不修改cordova的源码暂时没有发现有什么问题.有的话我会更新说明
    

    相关文章

      网友评论

          本文标题:iOS HTML加载本地资源

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