Нужен контент в UIWebView для быстрого отображения
Часть моего приложения кэширует веб-страницы для автономного просмотра. Для этого я сохраняю HTML, извлеченный из сайта, и переписываю URL-адреса img, чтобы указать на файл в локальном хранилище. Когда я загружаю html в UIWebView, он загружает изображения, как ожидалось, и все в порядке. Таким образом, я также кэширую таблицы стилей.
Проблема заключается в том, что, когда я помещаю телефон в режим самолета, загрузка этого кэшированного html заставляет UIWebView отображать пустой экран и приостанавливать какое-то время перед отображением страницы. Я выяснил, что это вызвано не кэшированными URL-адресами, указанными в исходном HTML-документе, который пытается просмотреть веб-представление. Эти другие URL-адреса включают изображения в кэшированных таблицах стилей, содержимое в iframe и javascript, которые открывают соединение для извлечения других ресурсов. Пауза происходит, когда UIWebView пытается извлечь эти ресурсы, и веб-страница появляется только после истечения всех этих других изъятий.
Мои вопросы: как я могу заставить UIWebView отображать только то, что я кэшировал немедленно? Вот мои мысли:
- напишите еще больше кода для кэширования этих других ссылок. Это потенциально более тонкий код, чтобы поймать все крайние случаи и т.д., Особенно для того, чтобы проанализировать Javascript, чтобы увидеть, что он загружает после загрузки страницы.
- заставить UIWebView немедленно отключиться, чтобы пауза не была. Я не понял, как это сделать.
- каким-то образом получить то, что уже загружено для отображения, даже если внешние ссылки еще не завершили выборку
- разделите код всех скриптов, тегов ссылок и фреймов на "стереть" внешние ссылки. Я пробовал это, но для некоторых сайтов результирующая страница сильно перепуталась.
Может ли кто-нибудь помочь мне здесь? Я работаю над этим навсегда, и у меня заканчиваются идеи.
Ответы
Ответ 1
Изменить: просто указывая, что этому вопросу и его ответам 4 года. Я общаюсь с iOS 3.x ниже. Я могу легко представить, что все изменилось за последнее время - черт возьми, одни устройства быстрее, чем iPhone 3G. YMMV.:)
Я смотрел на это сам в прошлое, а на самом деле ничего не реализовал, просто в Google. Похоже, что это очень сложно/невозможно сделать на iPhone с помощью DontLoad. Даже в марте появились сообщения о том, что он работает в Simulator, но не на устройстве: http://discussions.apple.com/message.jspa?messageID=9245292
Я просто догадываюсь, но на основе даже других вопросов StackOverflow (например, этот). Я думаю, что кэширование на устройстве как-то более ограничено, чем как его Mac, так и Safari.
Другие на форумах Apple сообщают о проблемах с грубыми инженерами, когда спрашивают о UIWebView и NSURLCache, где в отчетах об ошибках инженеры говорят, что это должно сработать, но фактические разработчики говорят, что это не так. http://discussions.apple.com/thread.jspa?threadID=1588010 (29 июля - 20 августа)
Некоторые решения могут быть найдены здесь. Учитывая [UIImage imageWithData:...], Дэвид описывает некоторый код для "реализации асинхронного кэширующего загрузчика изображений для iPhone".
И теперь я обнаружил это письмо 10 августа в списке рассылки cocoa -dev:
10 августа 2009 года, в 13:38, Майк Манзано написал:
Кто-нибудь смог успешно получить систему загрузки URL-адресов на iPhone обратить внимание на пользовательские версии NSURLCache? Мне кажется, что это Быть игнорированным. Подробнее здесь:
[ссылка на эту страницу; удалены.]
(см. второй "ответ" на этот страница).
Просто нужна какая-то подсказка, если я что-то не так, или если мой код просто игнорируются.
Да, я работал, это просто требует создания собственного кеша экземпляр и общий кэш этого нового экземпляра.
Но это абсолютно сработало - один раз реализовано, мой "настроить представление таблицы образ ячейки к изображению из сети" работает красиво (и без нее прокрутка было больно в лучшем случае).
Гленн Андреас gandreas @xxxxxxxxxxxx http://www.gandreas.com/ злая забава! Безумный, Плохой и Опасный Знать
Вышеупомянутое электронное письмо можно найти на http://www.cocoabuilder.com/archive/message/cocoa/2009/8/10/242484 (без дальнейших ответов)
Дальнейшее доказательство того, что NSURLCache может работать, находится в Блог iCab: фильтрация URL-адресов для UIWebView на iPhone (18 августа 2009 г.)
Любой, кто здесь, вероятно, также должен искать пример приложения Apple URLCache, поскольку он может иметь отношение к их работе: https://developer.apple.com/iphone/library/samplecode/URLCache/index.html За исключением того, что просто посмотрел на нем сейчас говорится: "Это приложение не использует диск NSURLCache или кеш памяти, поэтому наша политика кэширования должна удовлетворять запросу, загружая данные из его источника". и использует cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
. Так что это менее актуально, чем я думал, и почему этот человек вызывал шумиху NSURLCache.
Итак, кажется, что NSURLCache не так сложно или невозможно, как я начал этот пост, сказав. Должен любить находить способ, который работает, исследуя причины, которые он не сработает. Я все еще не могу поверить, что есть только 25 результатов поиска Google для "iphone" и "NSURLRequestReturnCacheDataDontLoad"... Этот ответ имеет примерно каждый из них. И я рад, что написал это, чтобы потом поговорить об этом, -)
Ответ 2
Сгенерируйте a NSURLRequest
с помощью +requestWithURL:cachePolicy:timeoutInterval:
. Задайте политику кэша NSURLRequestReturnCacheDataDontLoad
. Загрузите запрос в веб-просмотр с помощью -loadRequest:
. Полный docs здесь.
Ответ 3
Спасибо за лидерство! Это меня отклеило. К сожалению, я, кажется, сталкиваюсь с кучей блокпостов.
Проблема в том, что если я использую NSURLRequestReturnCacheDataDontLoad, я думаю, что ресурсы, которые я загружаю, уже должны находиться в кеше.
Чтобы поместить их в кеш, я попробовал это:
NSURLRequest *request = [NSURLRequest
requestWithURL:cachedURL
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60.0] ;
NSURLResponse *responseToCache =
[[NSURLResponse alloc]
initWithURL:[request URL]
MIMEType:@"text/html"
expectedContentLength:[dataAtURL length]
textEncodingName:nil] ;
NSCachedURLResponse *cachedResponse =
[[NSCachedURLResponse alloc]
initWithResponse:responseToCache data:dataAtURL
userInfo:nil storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] ;
// Store it
[[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:request] ;
[responseToCache release] ;
[cachedResponse release] ;
NSLog( @"*** request %@, cache=%@", request ,[[NSURLCache sharedURLCache]
cachedResponseForRequest:request] ) ;
Я получил этот код из другого места в сети, но я думаю, что, хотя он работал на Mac, он не работает на iPhone, моей целевой платформе.
Кажется, он не вставляет элемент в кеш; NSLog печатает null для "cache =% @".
Затем я попытался перегрузить NSURLCache:
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
{
NSLog( @"FileFriendlyURLCache asked from request %lx of type %@ for data at URL: %@" ,
request , [request class] , [[request URL] absoluteString] ) ;
NSCachedURLResponse *result = nil ;
if( [[[request URL] absoluteString] hasPrefix:@"file://"] )
{
NSLog( @"\tFulfilling from cache" ) ;
NSError *error = nil ;
NSData *dataAtURL = [NSData dataWithContentsOfURL:[request URL]
options:0 error:&error] ;
if( error )
NSLog( @"FileFriendlyURLCache encountered an error while loading %@: %@" ,
[request URL] , error ) ;
NSURLResponse *reponse = [[NSURLResponse alloc]
initWithURL:[request URL]
MIMEType:@"text/html"
expectedContentLength:[dataAtURL length]
textEncodingName:nil] ;
result = [[NSCachedURLResponse alloc] initWithResponse:reponse data:dataAtURL
userInfo:nil storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] ;
#warning LEOPARD BUG MAKES IT SO I DONT AUTORELEASE result NSCachedURLResponse
[reponse release] ;
}
else
{
NSLog( @"\tFulfilling from web" ) ;
result = [super cachedResponseForRequest:request] ;
}
NSLog( @"Result = %@" , result ) ;
return result ;
}
В моем запросе я указываю политику кэша NSURLRequestReturnCacheDataDontLoad. Этот метод, кажется, называется просто прекрасным, но он имеет странное поведение на iPhone. Независимо от того, возвращаю ли я экземпляр NSCachedURLResponse, UIWebView все еще возвращает ошибку:
Error Domain=NSURLErrorDomain Code=-1008 UserInfo=0x45722a0 "resource unavailable"
Как будто UIWebView игнорирует тот факт, что он получает что-то отличное от нуля и вообще не работает. Затем я получил подозрение и задался вопросом, просто ли вся система загрузки URL просто игнорирует все, что происходит от -cachedResponseForRequest, поэтому я создал подкласс NSCachedURLResponse, который выглядит следующим образом:
@implementation DebugCachedURLResponse
- (NSData *)data
{
NSLog( @"**** DebugCachedURLResponse data accessed." ) ;
return [super data] ;
}
- (NSURLResponse *)response
{
NSLog( @"**** DebugCachedURLResponse response accessed." ) ;
return [super response] ;
}
- (NSURLCacheStoragePolicy)storagePolicy
{
NSLog( @"**** DebugCachedURLResponse storagePolicy accessed." ) ;
return [super storagePolicy] ;
}
- (NSDictionary *)userInfo
{
NSLog( @"**** DebugCachedURLResponse userInfo accessed." ) ;
return [super userInfo] ;
}
@end
Затем я модифицировал -cachedResponseForRequest, чтобы использовать этот класс:
result = [[DebugCachedURLResponse alloc] initWithResponse:reponse data:dataAtURL
userInfo:nil storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] ;
Я установил запрос на использование политики кэша NSURLRequestUseProtocolCachePolicy и запускал программу. Хотя я вижу, что моя версия -cachedResponseForRequest вызывается и возвращает DebugCachedURLResponses для всех URL-адресов файлов, ни одна из моей отладки не вызывается, сообщая мне, что мои экземпляры DebugCachedURLResponse полностью игнорируются!
Итак, опять же, я застрял. Я не думаю, что у вас есть другие идеи?
Большое спасибо за ваш первый ответ.
Ответ 4
Эй, ребята, я думаю, что нашел проблему. Похоже, что NSURLRequestReturnCacheDataElseLoad и NSURLRequestReturnCacheDataDontLoad разбиты на iPhone. Когда NSCachedURLResponse возвращается из кеша и содержит заголовки HTTP, указывающие, что срок действия контента истек (например, Expires, Cache-Control и т.д.), Кешированный ответ игнорируется и запрос выполняется в исходный источник.
Решение выглядит следующим образом:
- Внедрите свой собственный подкласс NSHTTPURLResponse, который позволяет вам изменить словарь allHeaderFields.
- Внедрите свой собственный NSURLCache, переопределите cachedResponseForRequest и верните новый NSCachedURLResponse, содержащий экземпляр вашего подкласса NSHTTPURLResponse с разделенными заголовками HTTP "expiry".