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

CM *_*ram 46 html iphone image uiwebview nsurlprotocol

之前已经提出过类似的问题,但我找不到解决方案.

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

[请注意,html是远程的.我总是得到从本地加载html和图像文件的答案 - 我已经这样做了]

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

我有Java背景.我可以使用自定义内容提供商轻松地为Android做到这一点.我相信iOS/Objective-C必须存在类似的解决方案.我没有足够的Objective-C经验可以在很短的时间内自行解决.

任何帮助将不胜感激.

Nic*_*ver 85

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

谢谢你的提问!我在很长一段时间里都在问这个问题,花时间计算这个问题的时间非常值得.

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

这是它在我的模拟器中的样子:

在此输入图像描述

MyCustomURLProtocol.h

@interface MyCustomURLProtocol : NSURLProtocol
{
    NSURLRequest *request;
}

@property (nonatomic, retain) NSURLRequest *request;

@end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

MyCustomProtocolViewController.h

@interface MyCustomProtocolViewController : UIViewController {
    UIWebView *webView;
}

@property (nonatomic, retain) UIWebView *webView;

@end
Run Code Online (Sandbox Code Playgroud)

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];
}
Run Code Online (Sandbox Code Playgroud)

file.html

<html>
<body>
    <h1>we are loading a custom protocol</h1>
    <b>image?</b><br/>
    <img src="myapp://image1.png" />
<body>
</html>
Run Code Online (Sandbox Code Playgroud)


Sam*_*uri 39

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

这些变化很微妙但很重要:丢失未分配的requestivar,而是参考提供的实际请求,NSURLProtocol并且工作正常.

NSURLProtocolCustom.h

@interface NSURLProtocolCustom : NSURLProtocol
@end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

Nick的代码的问题是子类NSURLProtocol不需要存储请求.NSURLProtocol已经有请求,您可以使用-[NSURLProtocol request]相同名称的方法或属性访问.由于request他的原始代码中的ivar从未被分配,因此它总是nil(如果它被分配,它应该已经在某处释放).该代码不能也不起作用.

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

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