zoukankan      html  css  js  c++  java
  • iOS webView 远程html加载本地资源

       昨天,一个朋友让我帮他在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];

    //    NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"广告iOS" ofType:@"png"];

        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];

        // Do any additional setup after loading the view, typically from a nib.

    }

      到这里为止远程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];

    }

    好吧,表示完美解决。。。。cordova中可以干任何自己想弄的事情了

       (参考资料:http://stackoverflow.com/questions/5572258/ios-webview-remote-html-with-local-image-files)

  • 相关阅读:
    台州 OJ 3847 Mowing the Lawn 线性DP 单调队列
    洛谷 OJ P1417 烹调方案 01背包
    快速幂取模
    台州 OJ 2649 More is better 并查集
    UVa 1640
    UVa 11971
    UVa 10900
    UVa 11346
    UVa 10288
    UVa 1639
  • 原文地址:https://www.cnblogs.com/kingbo/p/4070612.html
Copyright © 2011-2022 走看看