如何从UIWebView(或WKWebView)访问资产目录中的图像资源

17

我们有一个本地的HTML文件需要在UIWebView中展示。我们希望在其中展示通过资源目录定义的图片。

我知道如果这个图片是放在主Bundle中的话,我们可以使用类似的方法。代码片段如下:

webView.loadHTMLString("<img src='target_image.png'/>", baseURL: NSBundle.mainBundle().bundleURL)

然而,如果png文件被打包在资源目录中,我不确定我可以为"target_image.png"指定什么。(此外,我们想要指定pdf以利用Xcode 6中的矢量图支持)

有没有人知道如何实现这一点?

4个回答

20

由于资源目录存储在捆绑包中作为单个文件,因此无法获取到单个资源的URL。

最简单的解决方案是不要在Web视图中使用资源目录中的图像。

如果必须将图像保留在资源目录中,则唯一的选择是将所需的资源“解压”为应用程序捆绑包文档目录中的不同文件,然后引用这些文件。


4

有一种方法可以从UIWebView中访问资源目录中的图像资源。您应该创建NSUrlProtocol子类,注册您的新协议,并手动响应WebView图像请求。

@interface AppDelegate : UIResponder <UIApplicationDelegate>
@end

@implementation AppDelegate
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        [NSURLProtocol registerClass:[ImageProtocol class]];    
        //... other code
        return YES;
    }
@end

@interface ImageProtocol : NSURLProtocol
@end

@implementation ImageProtocol

    + (BOOL)canInitWithRequest:(NSURLRequest *)request {
        NSString *imgName = (NSString *)[[request.URL.absoluteString componentsSeparatedByString:@"/"] lastObject];
        AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
        NSLog(@"imgName = %@",imgName);
        if ([UIImage imageNamed:imgName] != nil) {// or check for explicit name "target_image.png"
            return YES;
        }
        return NO;
    }

    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
        return request;
    }

    + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b{
        return [super requestIsCacheEquivalent:a toRequest:b];
    }

    - (void)startLoading {
        NSString *imgName = (NSString *)[[self.request.URL.absoluteString componentsSeparatedByString:@"/"] lastObject];
        UIImage *img = [UIImage imageNamed:imgName];
        NSString *mimeType = @"image/png";
        @try {
            NSData *imageData = nil;

            if ([imgName hasSuffix:@".png"]) {
                imageData = UIImagePNGRepresentation(img);
            } else if ([imgName hasSuffix:@".jpg"]) {
                imageData = UIImageJPEGRepresentation(img, 1);
                mimeType = @"image/jpg";
            }

            NSString *encoding = @"utf-8";
            NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.request.URL
                                                        MIMEType:mimeType
                                           expectedContentLength:imageData.length
                                                textEncodingName:encoding];

            [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
            [self.client URLProtocol:self didLoadData:imageData];
            [self.client URLProtocolDidFinishLoading:self];
        } @catch (NSException *exception) {
            NSError *err = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil];
            [self.client URLProtocol:self didFailWithError:err];
            NSLog(@"%@",exception.debugDescription);
        }
    }

    - (void)stopLoading {
    // do nothing
    }

    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
        [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    }

    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
    }

    - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        [self.client URLProtocolDidFinishLoading:self];
    }

    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
        [self.client URLProtocol:self didFailWithError:error];
    }

@end

实现自己的 NSURLProtocol 并从其中返回相应的资产目录中的 UIImage,我看到了。这是非常有趣的想法!从未想过。然而,明显的缺点是它会影响整个应用程序的 NSURLRequest,并且对于我的应用程序和可能大多数应用程序来说并不实用 :(话虽如此,感谢您提供新的视角! - barley
对我来说,在-stopLoading中崩溃了,报错为-[ImageProtocol task]: unrecognized selector sent to instance 0x7ad68ab0,但是简单地注释掉这一行就解决了。对于我的使用情况来说,这是一个很好的解决方案。谢谢! - Gereon
1
@Gereon 你说得对。在我的原始代码中,我使用 NSURLSession 加载了 requests,因此我使用了 task。已修正答案。 - Alexander

1
很容易做到。
func base64String(from image: UIImage?) -> String? {
    guard let image = image, let data = image.pngData() else { return nil }
    return data.base64EncodedString()
}


if let base64String = base64String(from: UIImage(named: "myAssetImage")) {

    let htmlString = """
    <html>
    <body>
        <img src="data:image/png;base64,\(base64String)"/>
    </body>
    </html>
    """

    webView.loadHTMLString(htmlString, baseURL: nil)
}

注意:SF Symbols也可以以这种方式进行整合。只需根据您的喜好,从所选的SF Symbol创建一个UIImage,例如:
let image = UIImage(systemName: "pencil")

然后使用与上述相同的技术处理该图像。 尽管默认大小可能与您习惯的缩放不匹配。
在这种情况下,您可以使用内联CSS来缩放图像:
    let scaledImageString = "<img src=\"data:image/png;base64,
          \(base64String!)\" style=\"width:20px; height:20px;\">"

0

一种方法是将图像转换为base64,然后将其输入HTML中。

if let base64 = UIImage(named: "myImage")?.pngData()?.base64EncodedString() {
    webView.loadHTMLString("<img src='data:application/png;base64,(base64)'/>", baseURL: nil)
}

1
感谢您提供答案。您是否可以编辑您的答案并包含代码解释?这将有助于未来的读者更好地理解正在发生的事情,特别是那些对该语言感到陌生并且难以理解概念的社区成员。 - Jeremy Caney
我有一个资产中的图片,我想将它转换成 "<a href=https://xyz.s3.us-east-2.amazonaws.com/photos-1600077780403-FIRSTQT8IMG1.jpg><img src ="https://xyz.s3.us-east-2.amazonaws.com/photos-1600077780403-FIRSTQT8IMG1.jpg "width ="200" height = "200"></a>。我该如何做到这一点? - Dilip Tiwari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接