How to access image resource in asset catalog from UIWebView (or WKWebView)
Asked Answered
A

4

18

We have a local html to show in UIWebView. And there, we want to show images that are defined in Asset Catalog.

I know that we can do similar thing if we had the image flat in the main bundle. The code snippet would be like this.

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

However, I am not sure what I can specify for "target_image.png" if the png file is packaged in asset catalog. (Furthermore, we want to specify pdf to take advantage of vector image support in Xcode 6)

Does anybody have any idea how to achieve this?

Advocate answered 14/11, 2014 at 0:4 Comment(0)
M
21

Since the asset catalog is stored in the bundle as a single file, there's no way to get an URL to a singular asset.

The simplest solution is to NOT use the asset catalog for images you'll show in a web view.

If keeping the images in the asset catalog is essential, your only option is to "unpack" the required asset(s) into distinct files to the documents directory of the application bundle, and then reference those files.

Misery answered 4/2, 2015 at 13:53 Comment(0)
B
4

There is a way to access image resource in asset catalog from UIWebView. You should create NSUrlProtocol subclass, register your new protocol and manually respond for WebView images request.

@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
Bastogne answered 24/8, 2016 at 4:31 Comment(3)
Implement your own NSURLProtocol and return corresponding UIImage in the asset catalog from there, I see. That's very interesting idea! Never came to me. However, the obvious drawback is that it affects NSURLRequest application wide, and not very practical for my app and probably most of the apps :( Having said that, thanks for a new perspective!Advocate
For me, this crashes in -stopLoading with -[ImageProtocol task]: unrecognized selector sent to instance 0x7ad68ab0, but simply commenting the line fixes it. Great solution for my use case. Thanks!Autoerotic
@Autoerotic You are right. In my original code, I loaded requests with NSURLSession so I used task for that. Fixed the answer.Bastogne
J
1

Pretty easy to do.

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)
}

Note: SF Symbols can be incorporated this way too. Just create a UIImage from the SF Symbol of choice as you prefer, such as with:

let image = UIImage(systemName: "pencil")

Then use the same technique with that image as above. Although the default size won't match the scaling you're probably used to.

In that case, you can use inline CSS to scale the image:

    let scaledImageString = "<img src=\"data:image/png;base64,
          \(base64String!)\" style=\"width:20px; height:20px;\">"
Jugum answered 5/9, 2023 at 18:8 Comment(0)
R
0

One way to do it is to convert the image into base64 and enter that into the HTML.

if let base64 = UIImage(named: "myImage")?.pngData()?.base64EncodedString() {
    webView.loadHTMLString("<img src='data:application/png;base64,(base64)'/>", baseURL: nil)
}
Rite answered 11/12, 2020 at 20:17 Comment(2)
Thank you for contributing an answer. Would you kindly edit your answer to to include an explanation of your code? That will help future readers better understand what is going on, and especially those members of the community who are new to the language and struggling to understand the concepts.Deherrera
I have an image in asset, i want to convert it "<a href=xyz.s3.us-east-2.amazonaws.com/… src=\"xyz.s3.us-east-2.amazonaws.com/…\" width= \"200\" height= \"200\"></a> . How can i do thatAntigorite

© 2022 - 2024 — McMap. All rights reserved.