iOS WebView удаленный html с локальными файлами изображений
подобные вопросы задавались раньше, но я никогда не мог найти решение.
вот моя ситуация-мой UIWebView загружает удаленную html-страницу. Изображения, используемые на веб-страницах, известны во время сборки. Чтобы ускорить загрузку страницы, я хочу упаковать файлы изображений в приложение iOS и заменить их во время выполнения.
[обратите внимание, что html удален. Я всегда получаю ответы на загрузку файлов html и изображений из локального-я это сделал уже]
ближайшая рекомендация, которую я получил, - использовать пользовательскую схему url, такую как myapp://images/img.png на html-странице и в приложении iOS перехватите URL-адрес myapp:// с подклассом NSURLProtocol и замените изображение локальным изображением. Звучит хорошо в теории, но я не встречал полного примера кода, демонстрирующего это.
У меня есть Java-фон. Я мог бы сделать это легко для Android, используя пользовательский контент-провайдера. Я уверен, что подобное решение должен существовать для iOS / Objective-C. У меня недостаточно опыта в Objective-C, чтобы решить его самостоятельно за короткий промежуток времени.
любая помощь будет оценили.
5 ответов
Ok вот пример того, как подкласс NSURLProtocol и поставить картинку (рис1.формат PNG), который уже находится в комплекте. Ниже приведен заголовок подклассов, реализация, а также пример использования его в viewController(неполный код) и локальном html-файле(который можно легко обменять на удаленный). Я вызвал пользовательский протокол:myapp://
как вы можете видеть в html-файле внизу.
и спасибо за вопрос! Я я задавал себе этот вопрос довольно долго, время, которое потребовалось, чтобы понять это, стоило каждой секунды.
EDIT: Если у кого-то есть трудности с запуском моего кода под текущей версией iOS, пожалуйста, посмотрите на ответ от sjs. Когда я ответил на вопрос, это сработало. Он указывает на некоторые полезные дополнения и исправил некоторые проблемы, поэтому дайте ему реквизит.
вот как это выглядит в моем симулятор:
MyCustomURLProtocol.h
@interface MyCustomURLProtocol : NSURLProtocol
{
NSURLRequest *request;
}
@property (nonatomic, retain) NSURLRequest *request;
@end
MyCustomURLProtocol.м
#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.м
...
@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];
}
.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.м
#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
проблема с кодом Ника заключается в том, что подклассы NSURLProtocol
не нужно хранить запросу. NSURLProtocol
уже есть запрос и вы можете получить доступ с помощью метода -[NSURLProtocol request]
или свойство с тем же именем. С request
Ивар в своем исходном коде никогда не присваивается, он всегда nil
(и если он был назначен, он должен были выпущены где-то). Этот код не может и не работает.
во-вторых, я рекомендую прочитать данные файла перед созданием ответа и передачей [data length]
как ожидаемая длина содержимого вместо -1.
и наконец, -[NSURLProtocol stopLoading]
не обязательно ошибка, это просто означает, что вы должны прекратить работу над ответом, если это возможно. Возможно, пользователь отменил его.
надеюсь, я правильно понимаю вашу проблему:
1) Загрузите удаленную веб-страницу ... и
2) замените некоторые удаленные активы файлами в приложении / build
верно?
Ну, то, что я делаю, выглядит следующим образом (я использую его для видео из-за ограничения кэширования 5 Мб на мобильном Safari, но я думаю, что любой другой контент DOM должен работать одинаково):
• создать локальная (для компиляции с помощью Xcode) HTML-страница с тегами стилей, для замены содержимого в приложении/сборке, установите значение hidden, например:
<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 (индекс.html / jquery.js / etc ... используйте корневые уровни для тестирования) в проект и подключитесь к target
• удаленный HTML-файл (здесь находится по адресу yourserver.com/index-test.html) имея
<base href="http://www.yourserver.com/">
• а также пункт назначения div, например,
<div id="destination"></div>
• и, наконец, в вашем проекте Xcode загрузите локальный HTML в веб-представление
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.
загрузить HTML в NSString, используйте 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]];