昨天,一個朋友讓我幫他在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)