带有本地图像文件的iOS WebView远程HTML

以前也有类似的问题,但我永远找不到解决方案。

这是我的情况 - 我的UIWebView加载一个远程HTML页面。 网页中使用的图像在构建时已知。 为了加快页面加载速度,我想将图像文件打包到iOS应用程序中并在运行时替换它们。

[请注意,该html是远程的。 我总是从本地获取加载html和图像文件的答案 - 我已经完成了]

我得到的最接近的建议是在html页面和iOS应用程序中使用自定义url方案,例如myapp://images/img.png,使用NSURLProtocol子类截取myapp:// URL并将图像替换为本地图片。 理论上听起来不错,但我还没有遇到一个完整的代码示例来说明这一点。

我有Java背景。 我可以使用自定义内容提供程序轻松完成Android操作。 我相信iOS / Objective-C必须存在类似的解决方案。 我没有足够的Objective-C经验在短时间内自己解决问题。

任何帮助将不胜感激。


确定这里是一个例子如何继承NSURLProtocol和提供的图像(image1.png),其已经在包。 下面是子类的头,实现以及如何在viewController(不完整代码)和本地html文件(可以很容易地与远程交换)中使用它的例子。 我已经调用了自定义协议: myapp://您可以在底部的html文件中看到。

并感谢您的问题! 我自己问了很长一段时间,弄清楚这一点的时间每秒都值得。

编辑:如果有人难以让我的代码在当前iOS版本下运行,请查看sjs的答案。 当我回答这个问题时,它正在工作。 他指出了一些有益的补充并纠正了一些问题,所以也给他一些道具。

这是它在我的模拟器中的外观:

在这里输入图像描述

MyCustomURLProtocol.h

@interface MyCustomURLProtocol : NSURLProtocol
{
    NSURLRequest *request;
}

@property (nonatomic, retain) NSURLRequest *request;

@end

MyCustomURLProtocol.m

#import "MyCustomURLProtocol.h"

@implementation MyCustomURLProtocol

@synthesize request;

+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{
    if ([theRequest.URL.scheme caseInsensitiveCompare:@"myapp"] == NSOrderedSame) {
        return YES;
    }
    return NO;
}

+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)theRequest
{
    return theRequest;
}

- (void)startLoading
{
    NSLog(@"%@", request.URL);
    NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[request URL] 
                                                        MIMEType:@"image/png" 
                                           expectedContentLength:-1 
                                                textEncodingName:nil];

    NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"image1" ofType:@"png"];  
    NSData *data = [NSData dataWithContentsOfFile:imagePath];

    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [[self client] URLProtocol:self didLoadData:data];
    [[self client] URLProtocolDidFinishLoading:self];
    [response release];
}

- (void)stopLoading
{
    NSLog(@"something went wrong!");
}

@end

MyCustomProtocolViewController.h

@interface MyCustomProtocolViewController : UIViewController {
    UIWebView *webView;
}

@property (nonatomic, retain) UIWebView *webView;

@end

MyCustomProtocolViewController.m

...

@implementation MyCustomProtocolViewController

@synthesize webView;

- (void)awakeFromNib
{
    self.webView = [[[UIWebView alloc] initWithFrame:CGRectMake(20, 20, 280, 420)] autorelease];
    [self.view addSubview:webView];
}

- (void)viewDidLoad
{   
    // ----> IMPORTANT!!! :) <----
    [NSURLProtocol registerClass:[MyCustomURLProtocol class]];

    NSString * localHtmlFilePath = [[NSBundle mainBundle] pathForResource:@"file" ofType:@"html"];

    NSString * localHtmlFileURL = [NSString stringWithFormat:@"file://%@", localHtmlFilePath];

    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:localHtmlFileURL]]];

    NSString *html = [NSString stringWithContentsOfFile:localHtmlFilePath encoding:NSUTF8StringEncoding error:nil]; 

    [webView loadHTMLString:html baseURL:nil];
}

file.html

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

尼克韦弗有正确的想法,但他的答案中的代码不起作用。 它也打破了一些命名约定,从不用NS前缀命名自己的类,并遵循大写首字母缩略词(如标识符名称中的URL)的约定。 为了使这个容易遵循,我会坚持他/他的命名。

这些更改很微妙但很重要:丢失未分配的request ivar,而是参考NSURLProtocol提供的实际请求,它工作正常。

NSURLProtocolCustom.h

@interface NSURLProtocolCustom : NSURLProtocol
@end

NSURLProtocolCustom.m

#import "NSURLProtocolCustom.h"

@implementation NSURLProtocolCustom

+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{
    if ([theRequest.URL.scheme caseInsensitiveCompare:@"myapp"] == NSOrderedSame) {
        return YES;
    }
    return NO;
}

+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)theRequest
{
    return theRequest;
}

- (void)startLoading
{
    NSLog(@"%@", self.request.URL);
    NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.request.URL 
                                                        MIMEType:@"image/png" 
                                           expectedContentLength:-1 
                                                textEncodingName:nil];

    NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"image1" ofType:@"png"];  
    NSData *data = [NSData dataWithContentsOfFile:imagePath];

    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [[self client] URLProtocol:self didLoadData:data];
    [[self client] URLProtocolDidFinishLoading:self];
    [response release];
}

- (void)stopLoading
{
    NSLog(@"request cancelled. stop loading the response, if possible");
}

@end

Nick代码的问题是NSURLProtocol子类不需要存储请求。 NSURLProtocol已经有了这个请求,你可以用方法-[NSURLProtocol request]或者同名的属性来访问。 由于原始代码中的ivar request从未被分配,所以它始终nil (并且如果它被分配了,它应该在某处被释放)。 该代码不能也不行。

其次,我建议在创建响应之前读取文件数据,并将[data length]作为预期的内容长度而不是-1。

最后, -[NSURLProtocol stopLoading]不一定是错误,它只是意味着如果可能的话,你应该停止响应。 用户可能已取消它。


我希望我能正确理解你的问题:

1)加载远程网页...和

2)用app / build内的文件替换某些远程资产

对?


那么,我正在做的事情如下(由于Mobile Safari缓存限制为5MB,我将其用于视频,但我认为任何其他DOM内容应该同样工作):


•创建一个本地(用Xcode编译)带有样式标签的HTML页面,用于替换应用内/构建内容,设置为隐藏,例如:

<div style="display: none;">
<div id="video">
    <video width="614" controls webkit-playsinline>
            <source src="myvideo.mp4">
    </video>
</div>
</div> 


•在同一个文件中提供一个内容div,例如

<div id="content"></div>


•(在此使用jQuery)从远程服务器加载实际内容,并将本地(Xcode导入的资产)附加到目标div,例如

<script src="jquery.js"></script>
<script>
    $(document).ready(function(){
        $("#content").load("http://www.yourserver.com/index-test.html", function(){
               $("#video").appendTo($(this).find("#destination"));           
        });

    });
</script>


•将www文件(index.html / jquery.js / etc ...使用根级别进行测试)放入项目并连接到目标


•远程HTML文件(位于yourserver.com/index-test.html)有一个

<base href="http://www.yourserver.com/">


•以及目标div,例如

<div id="destination"></div>


•最后在您的Xcode项目中,将本地HTML加载到Web视图中

self.myWebView = [[UIWebView alloc]init];

NSURL *baseURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[self.myWebView loadHTMLString:content baseURL:baseURL];

对我来说,最好结合https://github.com/rnapier/RNCachingURLProtocol进行脱机缓存。 希望这可以帮助。 F

链接地址: http://www.djcxy.com/p/34749.html

上一篇: iOS WebView remote html with local image files

下一篇: How to Get the Title of a HTML Page Displayed in UIWebView?