需要UIWebView中的内容才能快速显示

时间:2009-08-07 18:54:35

标签: iphone cocoa-touch timeout uiwebview

我的部分应用会缓存网页以供离线观看。为此,我保存从站点获取的HTML并重写img urls以指向本地存储上的文件。当我将html加载到UIWebView中时,它会按预期加载图像,一切都很好。我也是以这种方式缓存样式表。

问题在于,当我将手机置于飞行模式时,加载此缓存的html会导致UIWebView显示空白屏幕并在显示页面之前暂停一段时间。我发现它是由Web视图试图获取的原始HTML文档引用的非缓存URL引起的。这些其他URL包括缓存样式表中的图像,iframe中的内容以及用于打开连接以获取其他资源的javascript。当UIWebView尝试获取这些资源时会发生暂停,并且只有在所有这些其他提取超时后才会显示该网页。

我的问题是,如何让UIWebView显示我立即缓存的内容?以下是我的想法:

  • 编写更多代码来缓存这些其他引用。这可能需要更多的代码来捕获所有边缘情况等,特别是必须解析Javascript以查看在加载页面后它加载的内容
  • 强制UIWebView立即超时,因此没有暂停。我还没弄明白怎么做。
  • 以某种方式获取已经加载的内容,即使外部引用尚未完成提取
  • 删除所有脚本,链接标记和iframe的代码以“擦除”外部引用。我已经尝试过这个,但对于某些网站来说,结果页面严重混乱

有人可以帮我吗?我一直在努力解决这个问题,但我的想法已经不多了。

4 个答案:

答案 0 :(得分:10)

编辑:只是指出这个问题及其答案已有4年了。我正在聊聊下面的iOS 3.x.我可以很容易地想象事情在此期间发生了变化 - 哎呀,单独的设备比iPhone 3G更快。因人而异。 :)

我一直在看这个过去,而不是实际上没有实现任何东西,只是谷歌搜索。总的来说,使用DontLoad在iPhone上执行此操作非常困难/不可能。即使在三月份,也有报道称它可以在模拟器中运行,但不能在设备上运行:http://discussions.apple.com/message.jspa?messageID=9245292

我只是在猜测,但基于其他StackOverflow问题(例如this one)我认为,设备上的缓存可能比Mac和Safari版本更加有限。

Apple论坛上的其他人在询问UIWebView和NSURLCache时报告了粗鲁的工程师的经验,在bug报告中工程师说它应该有效,但实际的开发人员说它没有。 http://discussions.apple.com/thread.jspa?threadID=1588010(7月29日至8月20日)

可能会找到一些解决方案here。考虑到[UIImage imageWithData:...],David描述了“用于iPhone的异步缓存图像下载器的实现”的一些代码。

现在我在cocoa-dev邮件列表上发现了这个8月10日的电子邮件:

  

2009年8月10日下午1:38,Mike Manzano写道:

     
    

有没有人能够成功     在iPhone上获取URL加载系统     要注意自定义版本     NSURLCache?在我看来,它是     被忽略了。更多信息:

         

[此页面的链接;除去。]

         

(见第二个“答案”     页)。

         

如果我是的话,只需要某种线索     做错了或者我的代码是     被忽略了

  
     

是的,我有它的工作 - 只是   需要制作自己的缓存   实例并明确设置   共享缓存到该新实例。

     

但它绝对有效 - 一次   实施,我的“设置表视图   细胞的图像从网络图像“   很好地工作(没有它滚动   最痛苦的。)

     

Glenn Andreas gandreas @ xxxxxxxxxxxx   http://www.gandreas.com/邪恶的乐趣!   疯狂,坏,并且知道危险

上述电子邮件可在http://www.cocoabuilder.com/archive/message/cocoa/2009/8/10/242484找到(无需进一步回复)

可以在iCab Blog: URL filtering for UIWebView on the iPhone(2009年8月18日)中找到NSURLCache可以工作的进一步证明

这里的任何人都可能也应该关注Apple的URLCache示例应用程序,因为它可能与他们的工作有关:https://developer.apple.com/iphone/library/samplecode/URLCache/index.html除了刚看到它之外,它说“此应用程序不使用NSURLCache磁盘或内存缓存,因此我们的缓存策略是通过从其源加载数据来满足请求。“并使用cachePolicy:NSURLRequestReloadIgnoringLocalCacheData。所以这不像我想的那么重要,为什么this person称NSURLCache是​​个笑话。

所以看起来NSURLCache并不像我开始写这篇文章那样棘手或不可能。喜欢找到一种有效的方法,同时研究它不起作用的原因。我仍然无法相信“iphone”和“NSURLRequestReturnCacheDataDontLoad”只有25个Google搜索结果...这个答案几乎包含了每一个。而且我很高兴我写了它以便稍后可以参考; - )

答案 1 :(得分:1)

使用NSURLRequest生成+requestWithURL:cachePolicy:timeoutInterval:。将缓存策略设置为NSURLRequestReturnCacheDataDontLoad。使用-loadRequest:将请求加载到webview中。完整文档here

答案 2 :(得分:1)

感谢领导!它让我松了一口气。不幸的是,我似乎陷入了一堆障碍。

问题是,如果我使用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为“cache =%@”打印null。

然后,我尝试重载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忽略了这样一个事实,即除了nil之外还有其他东西,无论如何都会失败。然后我怀疑并想知道整个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版本被调用并返回所有文件URL的DebugCachedURLResponses,但我没有调用任何调试,告诉我我的DebugCachedURLResponse实例被完全忽略了!

所以,再次,我被困住了。我不认为你还有其他想法吗?

非常感谢你的第一个回复。

答案 3 :(得分:1)

嘿伙计们,我想我发现了这个问题。它看起来像iPhone上的NSURLRequestReturnCacheDataElseLoad和NSURLRequestReturnCacheDataDontLoad。当从缓存返回NSCachedURLResponse并且它包含指示内容已过期的HTTP标头(例如Expires,Cache-Control等)时,将忽略缓存的响应并请求原始源。

解决方案如下:

  1. 实现您自己的NSHTTPURLResponse子类,允许您修改allHeaderFields字典。
  2. 实现您自己的NSURLCache,覆盖cachedResponseForRequest:并返回一个新的NSCachedURLResponse,其中包含NSHTTPURLResponse子类的实例,并删除了相关的“到期”HTTP标头。