同様の質問が以前に聞かれましたが、解決策を見つけることができませんでした。
これが私の状況です-私のUIWebViewはリモートhtmlページをロードします。 Webページで使用される画像は、ビルド時に知られています。ページの読み込みを高速化するために、iOSアプリケーションに画像ファイルをパッケージ化し、実行時にそれらを置き換えたいと思います。
[htmlはリモートであることに注意してください。ローカルからhtmlファイルと画像ファイルの両方を読み込むと、常に答えが返ってきます。
私が得た最も近い推奨事項は、htmlページおよびiOSアプリケーションでmyapp://images/img.pngなどのカスタムURLスキームを使用し、NSURLProtocolサブクラスでmyapp:// URLをインターセプトし、画像をローカルに置き換えることでした。画像。理論的には良さそうに見えますが、これを示す完全なコード例はありません。
Javaバックグラウンドがあります。カスタムコンテンツプロバイダーを使用してAndroidに対してこれを簡単に行うことができます。 iOS/Objective-Cにも同様のソリューションが存在するはずです。 Objective-Cの経験が足りないので、短期間で自分で解決できません。
任意の助けをいただければ幸いです。
OKは、 NSURLProtocol をサブクラス化して、既にバンドルにあるイメージ(image1.png)を配信する方法の例です。以下は、サブクラスのヘッダー、実装、およびviewController(不完全なコード)およびローカルhtmlファイル(リモートファイルと簡単に交換可能)での使用方法の例です。カスタムプロトコルを呼び出しました:myapp://
下部のhtmlファイルで確認できます。
そして質問をありがとう!私はこれを非常に長い間求めていましたが、これを理解するのにかかった時間は毎秒の価値がありました。
EDIT:誰かが私のコードを現在の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]
または同じ名前のプロパティを使用してアクセスできます。元のコードのrequest
ivarは決して割り当てられないため、常にnil
になります(割り当てられた場合はどこかにリリースされているはずです)。そのコードは機能せず、機能しません。
第二に、応答を作成する前にファイルデータを読み取り、-1ではなく[data length]
を予想コンテンツ長として渡すことをお勧めします。
最後に、-[NSURLProtocol stopLoading]
は必ずしもエラーではなく、可能であれば応答の作業を停止することを意味します。ユーザーがキャンセルした可能性があります。
私はあなたの問題を正しく理解していることを望みます:
1)リモートWebページをロード...および
2)特定のリモートアセットをapp/build内のファイルに置き換えます
正しい?
さて、私がやっていることは次のとおりです(モバイル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を使用)リモートサーバーから実際のコンテンツをロードし、ターゲットdivにローカル(Xcodeインポートされたアセット)を追加します。
<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 /など...テストにルートレベルを使用)をドロップして接続しますターゲットへ
•リモート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
トリックは、明示的なベースURLを既存のHTMLに提供することです。
NSStringにHTMLをロードし、UIWebViewのloadHTMLString: baseURL:
バンドルへのURLをベースとして。 HTMLを文字列にロードするには、[NSString stringWithContentsOfURL]を使用できますが、これは同期メソッドであり、接続が遅いとデバイスがフリーズします。非同期リクエストを使用してHTMLをロードすることもできますが、より複雑です。 NSURLConnection
を読んでください。
NSURLProtocolはUIWebViewに適していますが、これまではWKWebViewはまだサポートしていません。 WKWebViewの場合、ローカルファイルリクエストを処理するローカルHTTPサーバーを構築できます。 GCDWebServer はこれに適しています:
self.webServer = [[GCDWebServer alloc] init];
[self.webServer addDefaultHandlerForMethod:@"GET"
requestClass:[GCDWebServerRequest class]
processBlock:
^GCDWebServerResponse *(GCDWebServerRequest *request)
{
NSString *fp = request.URL.path;
if([[NSFileManager defaultManager] fileExistsAtPath:fp]){
NSData *dt = [NSData dataWithContentsOfFile:fp];
NSString *ct = nil;
NSString *ext = request.URL.pathExtension;
BOOL (^IsExtInSide)(NSArray<NSString *> *) = ^(NSArray<NSString *> *pool){
NSUInteger index = [pool indexOfObjectWithOptions:NSEnumerationConcurrent
passingTest:^BOOL(NSString *obj, NSUInteger idx, BOOL *stop) {
return [ext caseInsensitiveCompare:obj] == NSOrderedSame;
}];
BOOL b = (index != NSNotFound);
return b;
};
if(IsExtInSide(@[@"jpg", @"jpeg"])){
ct = @"image/jpeg";
}else if(IsExtInSide(@[@"png"])){
ct = @"image/png";
}
//else if(...) // other exts
return [GCDWebServerDataResponse responseWithData:dt contentType:ct];
}else{
return [GCDWebServerResponse responseWithStatusCode:404];
}
}];
[self.webServer startWithPort:LocalFileServerPort bonjourName:nil];
ローカルファイルのファイルパスを指定する場合、ローカルサーバープレフィックスを追加します。
NSString *fp = [[NSBundle mainBundle] pathForResource:@"picture" ofType:@"jpg" inDirectory:@"www"];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d%@", LocalFileServerPort, fp]];
NSString *str = url.absoluteString;
[self.webViewController executeJavascript:[NSString stringWithFormat:@"updateLocalImage('%@')", str]];