Ios UIWebView/NSURLProtocol-脱机时不可访问的外部资产的占位符

Ios UIWebView/NSURLProtocol-脱机时不可访问的外部资产的占位符,ios,nsurlprotocol,Ios,Nsurlprotocol,我的应用程序是在iPhone/iPad上阅读书籍的电子阅读器。用户可以选择下载我们在设备上保存为.epub文件(压缩内容)的书籍-这可以在脱机时查看书籍。页面内容是html格式的 一些资产,如视频,不在此.epub内容中。当用户导航到这样的页面时,我必须检测不在.ePub中的外部资源,并显示一个简单的占位符,其中包含文本“此内容在脱机时不可用” 这些视频可以嵌入iFrame html标记或对象标记中 现在我们有一个继承自NSURLProtocol抽象类的类,我正在使用它来处理这个需求。我已经实现

我的应用程序是在iPhone/iPad上阅读书籍的电子阅读器。用户可以选择下载我们在设备上保存为.epub文件(压缩内容)的书籍-这可以在脱机时查看书籍。页面内容是html格式的

一些资产,如视频,不在此.epub内容中。当用户导航到这样的页面时,我必须检测不在.ePub中的外部资源,并显示一个简单的占位符,其中包含文本“此内容在脱机时不可用”

这些视频可以嵌入iFrame html标记或对象标记中

现在我们有一个继承自NSURLProtocol抽象类的类,我正在使用它来处理这个需求。我已经实现了这3种方法:

+ (BOOL) canInitWithRequest:(NSURLRequest *)request

- (void)startLoading

- (void) sendResponse:(NSData *)data mimetype:(NSString *)mimetype url:(NSURL *)url
我现在的问题是,我有一个简单的.png,它显示“脱机时此内容不可用”,但它很难看,因为有时iFrame的面积比图像小。我如何缩放它以适应?还是有更好的方法?它不一定是一个图像,只要我可以显示这条信息,内容是不可用的。我的当前代码采用了惊人的加载方法:

- (void) startLoading
{
    …
    NSString *bundlePath = [NSString stringWithFormat:@"%@/%@",[[NSBundle mainBundle] bundlePath],@"PxeReaderResources.bundle"];
    NSString *pathToPlaceholder = [NSString stringWithFormat:@"%@/%@",bundlePath,@"OfflinePlaceholder.png"]; //my placeholder image
    NSLog(@"Path to placeholder is: %@", pathToPlaceholder);

    data = [[NSFileManager defaultManager] contentsAtPath:pathToPlaceholder];

    … 

    [self sendResponse:data mimetype:@"application/octet-stream" url:self.request.URL];
}

- (void) sendResponse:(NSData *)data mimetype:(NSString *)mimetype url:(NSURL *)url
{
    NSDictionary *headers = @{@"Content-Type" : mimetype, @"Access-Control-Allow-Origin" : @"*", @"Cache-control" : @"no-cache"};
    NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headers];

    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [self.client URLProtocol:self didLoadData:data];
    [self.client URLProtocolDidFinishLoading:self];
}

谢谢您的建议。

您可以使用(注入)JavaScript遍历DOM查找这些object/iframe标记,并用漂亮的“Not available offline”消息替换它们,而不是试图拦截资源负载。您可能会在加载页面后执行此操作,或者您可以尝试在将HTML传递到WebView之前对其进行解析和修改。

我可以发送一个简单的自定义HTML div标记来代替外部资源。这里是代码-希望这将帮助别人。 注意:所有这些代码都在从NSURLProtocol继承的自定义类中

#define EXTERNAL_REQUEST_KEY @"ExternalRequest"

+ (BOOL) canInitWithRequest:(NSURLRequest *)request
{
     NSString* filePath = [[PxePlayer sharedInstance] getBaseURL]; //gets base path to the epub zipped folder

    if ([Reachability isReachable])
    {
        return filePath != nil && [@"file" caseInsensitiveCompare:request.URL.scheme] == NSOrderedSame;
    }
    else
    {
        if ([request.URL.scheme isEqualToString:@"https"] || [request.URL.scheme isEqualToString:@"http"]) //these schemes won't work if offline
        {
            [NSURLProtocol setProperty:@YES forKey:EXTERNAL_REQUEST_KEY inRequest:(NSMutableURLRequest*)request]; //marking those request so that we will only provide placeholder for these requests
        }
        return filePath != nil && ([@"file" caseInsensitiveCompare:request.URL.scheme] == NSOrderedSame ||
                               [@"https" caseInsensitiveCompare:request.URL.scheme] == NSOrderedSame ||
                               [@"http" caseInsensitiveCompare:request.URL.scheme] == NSOrderedSame);
    }
}

- (void) startLoading
{
    ...

    if ([NSURLProtocol propertyForKey:EXTERNAL_REQUEST_KEY inRequest:self.request])
    {   
        custMimeType = @"text/html";

        data = [self prepareDataForPlaceholderForExternalAsset];
    }

    ...

    if (data)
    {
        //Now ready to send the placeholder as custom response
        [self sendResponse:data mimetype:custMimeType url:self.request.URL];
    }
}

//method to create a simple div tag as NSData
- (NSData*) prepareDataForPlaceholderForExternalAsset
{
    NSString *placeholder =
    @"<div style=\"background-color:#efefef; border:1px solid #999999; text-align:center; width:100%; height:100%\"> \
        <font font-family=\"Helvetica Neue\" font-weight=\"Medium\" size=\"5\" color=\"#b3b3b3\"> \
          <div style=\"display:inline-block; margin-top:5%\"> \
            This content is unavailable when offline or printing \
          </div> \
        </font> \
      </div>";

    return [placeholder dataUsingEncoding:NSUTF8StringEncoding];
}

- (void) sendResponse:(NSData *)data mimetype:(NSString *)mimetype url:(NSURL *)url
{
    NSDictionary *headers = @{@"Content-Type" : mimetype, @"Access-Control-Allow-Origin" : @"*", @"Cache-control" : @"no-cache"};
    NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headers];

    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [self.client URLProtocol:self didLoadData:data];
    [self.client URLProtocolDidFinishLoading:self];
}
#定义外部请求键@“外部请求”
+(BOOL)canInitWithRequest:(NSURLRequest*)request
{
NSString*filePath=[[PxePlayer sharedInstance]getBaseURL];//获取epub压缩文件夹的基本路径
if([可达性是可访问的])
{
返回filePath!=nil&&[@“file”caseInsensitiveCompare:request.URL.scheme]==sensorderedname;
}
其他的
{
if([request.URL.scheme-isEqualToString:@“https”]| |[request.URL.scheme-isEqualToString:@“http”])//如果脱机,这些方案将无法工作
{
[NSURLProtocol setProperty:@YES forKey:EXTERNAL_REQUEST_KEY in REQUEST:(NSMutableURLRequest*)REQUEST];//标记这些请求,以便我们仅为这些请求提供占位符
}
返回文件路径!=nil&([@“file”caseInsensitiveCompare:request.URL.scheme]==sensorderedname||
[@“https”区分大小写比较:request.URL.scheme]==sensorderedName||
[@“http”caseInsensitiveCompare:request.URL.scheme]==sensorderedName);
}
}
-(空)惊人的
{
...
if([NSURLProtocol propertyWorkey:EXTERNAL\u REQUEST\u KEY in REQUEST:self.REQUEST])
{   
custMimeType=@“text/html”;
数据=[self-PreparedataForPlaceholder ForExternalAsset];
}
...
如果(数据)
{
//现在准备好将占位符作为自定义响应发送
[self-sendResponse:data mimetype:custMimeType url:self.request.url];
}
}
//方法将简单的div标记创建为NSData
-(NSData*)为占位符ForExternalAsset准备数据
{
NSString*占位符=
@" \
\
\
脱机或打印时,此内容不可用\
\
\
";
返回[placeholder dataUsingEncoding:NSUTF8StringEncoding];
}
-(void)sendResponse:(NSData*)数据mimetype:(NSString*)mimetype url:(NSURL*)url
{
NSDictionary*头文件=@{@“内容类型”:mimetype,@“访问控制允许源文件”:@“*”,@“缓存控制”:@“无缓存”};
NSHTTPURLResponse*response=[[NSHTTPURLResponse alloc]initWithURL:url状态码:200 HTTPVersion:@“HTTP/1.1”headerFields:headers];
[self.client URLProtocol:self-didReceiverResponse:response-cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[self.client-URLProtocol:self-didLoadData:data];
[self.client URLProtocoldFinishLoading:self];
}

谢谢你,汤姆。我最初想到了这种方法,但后来我意识到解析html可能有点太多了,因为我甚至不知道这些URL可能包含的所有标记。一旦我意识到我可以为给定的url请求发回自定义html片段,NSURLProtocal路由就成了一个非常简单的解决方案。