• 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)

  • 相关阅读:
    Docker系统知识整理(从安装到熟练操作)
    Dockerfile 文件介绍
    Cmake命令之add_subdirectory介绍
    Cmake实战指南
    cmake的四个命令:add_compile_options、add_definitions、target_compile_definitions、build_command
    cmake:选择编译器及设置编译器选项
    Task异常
    单元测试误区
    网络的核心概念
    java~使用枚举来实现接口的多态
  • 原文地址:https://www.cnblogs.com/kingbo/p/4070612.html
Copyright © 2020-2023  润新知