Soru Yerel görüntü dosyaları ile iOS WebView uzaktan html


Daha önce benzer sorular sorulmuştu, ama asla bir çözüm bulamadım.

İşte durumum - UIWebView'ım uzak bir html sayfası yüklüyor. Web sayfalarında kullanılan görüntüler yapım aşamasında bilinir. Sayfanın daha hızlı yüklenmesini sağlamak için, görüntü dosyalarını iOS uygulamasında paketlemek ve bunları çalışma zamanında değiştirmek istiyorum.

[Lütfen html'nin uzak olduğunu unutmayın. Yerelden hem html hem de resim dosyalarını yüklemek için her zaman yanıt alırım - bunu zaten yaptım)

En yakın tavsiyem, html sayfasında ve iOS uygulamasında myapp: //images/img.png gibi özel bir url şeması kullanmak, NSURLProtocol alt sınıfıyla myapp: // URL'yi kesip görüntüyü yerel görüntüsü. Teoride iyiydi, ama bunu gösteren tam bir kod örneği ile karşılaşmadım.

Java arka planım var. Bunu, bir Özel İçerik Sağlayıcısı kullanarak Android için kolayca yapabilirim. İOS / Objective-C için benzer bir çözüm bulunması gerektiğine eminim. Sahip olduğum kısa zaman diliminde kendimi çözmek için Objective-C'de yeterli tecrübem yok.

Herhangi bir yardım takdir edilecektir.


44
2018-04-06 19:59


Menşei




Cevaplar:


Tamam bir örnek nasıl alt sınıf NSURLProtocol ve bir görüntü (image1.png) zaten paketin içinde. Aşağıda alt sınıfların başlığı, uygulamanın yanı sıra bir viewController'da (tamamlanmamış kod) nasıl kullanılacağı ve yerel bir html dosyasında (uzaktan kumandayla kolayca değiştirilebilen) bir örnek verilmiştir. Özel protokolü aradım: myapp:// Altta html dosyasında gördüğünüz gibi.

Ve soru için teşekkürler! Bunu kendime uzun zamandır soruyordum, bunu anlamaya harcanan zaman her an değdi.

DÜZENLE: Birisi kodumu geçerli iOS sürümü altında çalıştırıyorsa zorluklar varsa, lütfen sjs'nin cevabına bakın. Soruyu cevapladığımda da çalışıyordu. Bazı yararlı eklemeleri işaret ediyor ve bazı sorunları düzeltti, bu yüzden ona da sahne ver.

Simülatörümde böyle görünüyor:

enter image description here

MyCustomURLProtocol.h

@interface MyCustomURLProtocol : NSURLProtocol
{
    NSURLRequest *request;
}

@property (nonatomic, retain) NSURLRequest *request;

@end

MyCustomURLProtocol.m

#import "MyCustomURLProtocol.h"

@implementation MyCustomURLProtocol

@synthesize request;

+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{
    if ([theRequest.URL.scheme caseInsensitiveCompare:@"myapp"] == NSOrderedSame) {
        return YES;
    }
    return NO;
}

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

- (void)startLoading
{
    NSLog(@"%@", request.URL);
    NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[request URL] 
                                                        MIMEType:@"image/png" 
                                           expectedContentLength:-1 
                                                textEncodingName:nil];

    NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"image1" ofType:@"png"];  
    NSData *data = [NSData dataWithContentsOfFile:imagePath];

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

- (void)stopLoading
{
    NSLog(@"something went wrong!");
}

@end

MyCustomProtocolViewController.h

@interface MyCustomProtocolViewController : UIViewController {
    UIWebView *webView;
}

@property (nonatomic, retain) UIWebView *webView;

@end

MyCustomProtocolViewController.m

...

@implementation MyCustomProtocolViewController

@synthesize webView;

- (void)awakeFromNib
{
    self.webView = [[[UIWebView alloc] initWithFrame:CGRectMake(20, 20, 280, 420)] autorelease];
    [self.view addSubview:webView];
}

- (void)viewDidLoad
{   
    // ----> IMPORTANT!!! :) <----
    [NSURLProtocol registerClass:[MyCustomURLProtocol class]];

    NSString * localHtmlFilePath = [[NSBundle mainBundle] pathForResource:@"file" ofType:@"html"];

    NSString * localHtmlFileURL = [NSString stringWithFormat:@"file://%@", localHtmlFilePath];

    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:localHtmlFileURL]]];

    NSString *html = [NSString stringWithContentsOfFile:localHtmlFilePath encoding:NSUTF8StringEncoding error:nil]; 

    [webView loadHTMLString:html baseURL:nil];
}

dosya.html

<html>
<body>
    <h1>we are loading a custom protocol</h1>
    <b>image?</b><br/>
    <img src="myapp://image1.png" />
<body>
</html>

84
2018-04-06 21:26



Fantastik! Tam olarak aradığım şey gibi görünüyor. Bunu deneyeceğim ve bilmene izin vereceğim. - CM Subram
Talep neden alt sınıfın bir özelliği olarak beyan edilir? NSURLProtocol zaten bir request özellik bu yüzden sadece kullanmalısınız self.request. Yukarıdaki kodda request her zaman nil. - Sami Samhuri
@sjs İyi nokta, ben bu özelliği tanıttığım zaman ne düşündüğümü söyleyemem, ancak, örnekte olduğu gibi o zamanlar iyi çalıştı, hiçbir zarar yoktu. Ve istek sıfır değil, bu NSURLProtocol kayıtlı bir alt sınıfıdır. Statik registerClass yönteminin belgelerine bakın. - Nick Weaver
@NickWeaver Belgeleri okudum. request asla atanmaz ve nil. Kodunu kullanıyorum ve işe yaratabilmek için mülkü kullanmam gerekti self.request. Bu kodu bugün deneyin, işe yaramıyor. İşe yaramamış olsa bile, kullanılmayan bir ivar kaldırılması gereken bir hırsızlıktır. Belgeleri okuduysanız, bunu da göreceksiniz - [NSURLProtocol stopLoading] bir hata durumu değildir. Giriş yapmamalısın "Bir şeyler ters gitti!" Bu başarılı bir istek döngüsünün düzenli bir parçası olduğunda. - Sami Samhuri
@Krutarth Patel Doğrudan değil. Belirli bir URL'yi tetikleyen html'nize bir düğme / bağlantı eklemeniz gerekir. Daha sonra bunu startLoading'de veya - webViewDidStartLoad: gibi bir UIWebViewDelegate yönteminde engellemeniz gerekecektir. - Nick Weaver


Nick Weaver'ın doğru fikri var ama cevabındaki kod çalışmıyor. Bazı adlandırma kurallarını da koparır, asla kendi sınıflarınızı NS önek ve tanımlayıcı isimlerinde URL gibi kısaltmaların aktive edilmesi kuralını izleyin. Bunu takip etmeyi kolaylaştırmak adına ismini yazacağım.

Değişiklikler ince ama önemli: Atanmadı request ivar ve bunun yerine sağlanan gerçek talebe bakın NSURLProtocol ve iyi çalışıyor.

NSURLProtocolCustom.h

@interface NSURLProtocolCustom : NSURLProtocol
@end

NSURLProtocolCustom.m

#import "NSURLProtocolCustom.h"

@implementation NSURLProtocolCustom

+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{
    if ([theRequest.URL.scheme caseInsensitiveCompare:@"myapp"] == NSOrderedSame) {
        return YES;
    }
    return NO;
}

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

- (void)startLoading
{
    NSLog(@"%@", self.request.URL);
    NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.request.URL 
                                                        MIMEType:@"image/png" 
                                           expectedContentLength:-1 
                                                textEncodingName:nil];

    NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"image1" ofType:@"png"];  
    NSData *data = [NSData dataWithContentsOfFile:imagePath];

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

- (void)stopLoading
{
    NSLog(@"request cancelled. stop loading the response, if possible");
}

@end

Nick'in koduyla ilgili problem, alt sınıfların NSURLProtocol isteği saklamak gerekmez. NSURLProtocol zaten istek var ve yöntemle erişebilirsiniz -[NSURLProtocol request]veya aynı adın mülkü. Beri request Orijinal kodundaki ivar hiçbir zaman atandı asla nil (ve atanmışsa bir yerde serbest bırakılmış olmalı). Bu kod işe yaramıyor ve çalışmıyor.

İkinci olarak, yanıtı oluşturmadan ve iletmeden önce dosya verilerini okumanızı öneriyorum [data length] -1 yerine beklenen içerik uzunluğu olarak.

Ve sonunda, -[NSURLProtocol stopLoading] mutlaka bir hata değildir, sadece mümkünse bir yanıt üzerinde çalışmayı durdurmanız gerektiği anlamına gelir. Kullanıcı bunu iptal etmiş olabilir.


39
2017-11-27 19:29



Bu önemli gelişme için teşekkürler! Dediğin gibi yaptım ve işe yaradı. - Vladimir Obrizan
Uygulamanızı Mobil Safari'de veya başka bir uygulamanın UIWebView'unda yüklenen bir web sayfası tarafından istenen bir resmi sağlamak için herhangi bir yol var mı? Bu yöntem işe yarayacak mı? - Danny
@Danny Ben öyle düşünmüyorum. Anladığım kadarıyla, http veya https isteklerini engelleyemezsiniz. - Sami Samhuri
Görünüşe göre bu çözüm ios8 ile çalışmayı bıraktı. CacheStoragePolicies iOS8 ile değişti mi? - Tim Christmann


Umarım sorununuzu doğru anlıyorum:

1) uzak bir web sayfasını yükleyin ... ve

2) bazı uzak varlıkları uygulama içindeki dosyalar ile değiştir / inşa et

Sağ?


Peki, yaptığım şey şu şekildedir (Mobil Safari'de 5 MB önbelleğe alma sınırlaması nedeniyle videoları kullanıyorum, ancak başka bir DOM içeriğinin eşit şekilde çalışması gerektiğini düşünüyorum):


• stil etiketleri içeren bir yerel (Xcode ile derlenecek) HTML sayfası oluşturmak, in-app / build içeriği yerine, gizli olarak ayarlamak, ör .:

<div style="display: none;">
<div id="video">
    <video width="614" controls webkit-playsinline>
            <source src="myvideo.mp4">
    </video>
</div>
</div> 


• Aynı dosyada bir içerik divı, ör.

<div id="content"></div>


• (burada jQuery'yi kullanarak) asıl içeriği uzak sunucudan yükleyin ve yerel (Xcode ithal edilen varlığınızı) hedef divunuza ekleyin.

<script src="jquery.js"></script>
<script>
    $(document).ready(function(){
        $("#content").load("http://www.yourserver.com/index-test.html", function(){
               $("#video").appendTo($(this).find("#destination"));           
        });

    });
</script>


• www dosyalarını (index.html / jquery.js / etc ... test için kök seviyelerini kullanın) projeye bırakın ve hedefe bağlanın


• uzak HTML dosyası (burada, sunucunuz.com/index-test.html adresinde bulunur)

<base href="http://www.yourserver.com/">


• yanı sıra bir hedef div, ör.

<div id="destination"></div>


• Son olarak Xcode projenizde yerel HTML'yi web görünümüne yükleyin.

self.myWebView = [[UIWebView alloc]init];

NSURL *baseURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[self.myWebView loadHTMLString:content baseURL:baseURL];

Benim için en iyi şekilde bir tedavi görür https://github.com/rnapier/RNCachingURLProtocolçevrimdışı önbellekleme için. Bu yardımcı olur umarım. F


2
2018-06-05 08:36





Hile, mevcut bir HTML'ye açık temel URL'yi sağlamaktır.

HTML'yi bir NSString'e yükleyin, UIWebView'leri kullanın loadHTMLString: baseURL: üssünüzün URL'si ile üs olarak. HTML'yi bir dizeye yüklemek için, [NSString stringWithContentsOfURL] öğesini kullanabilirsiniz, ancak bu senkron bir yöntemdir ve yavaş bağlantıda cihazı donduracaktır. HTML'yi yüklemek için bir async isteği kullanmak da mümkündür, ancak daha fazla dahil. Üzerinde okumak NSURLConnection.


1
2018-04-06 20:36





NSURLProtocol için iyi bir seçim UIWebViewama şu ana kadar WKWebView hala desteklemiyor. İçin WKWebView yerel dosya isteğini işlemek için yerel bir HTTP sunucusu oluşturabiliriz. GCDWebServer bunun için iyi:

self.webServer = [[GCDWebServer alloc] init];

[self.webServer addDefaultHandlerForMethod:@"GET"
                              requestClass:[GCDWebServerRequest class]
                              processBlock:
 ^GCDWebServerResponse *(GCDWebServerRequest *request)
{
    NSString *fp = request.URL.path;

    if([[NSFileManager defaultManager] fileExistsAtPath:fp]){
        NSData *dt = [NSData dataWithContentsOfFile:fp];

        NSString *ct = nil;
        NSString *ext = request.URL.pathExtension;

        BOOL (^IsExtInSide)(NSArray<NSString *> *) = ^(NSArray<NSString *> *pool){
            NSUInteger index = [pool indexOfObjectWithOptions:NSEnumerationConcurrent
                                                  passingTest:^BOOL(NSString *obj, NSUInteger idx, BOOL *stop) {
                                                      return [ext caseInsensitiveCompare:obj] == NSOrderedSame;
                                                  }];
            BOOL b = (index != NSNotFound);
            return b;
        };

        if(IsExtInSide(@[@"jpg", @"jpeg"])){
            ct = @"image/jpeg";
        }else if(IsExtInSide(@[@"png"])){
            ct = @"image/png";
        }
        //else if(...) // other exts

        return [GCDWebServerDataResponse responseWithData:dt contentType:ct];

    }else{
        return [GCDWebServerResponse responseWithStatusCode:404];
    }

}];

[self.webServer startWithPort:LocalFileServerPort bonjourName:nil];

Yerel dosyanın dosya yolunu belirtin, yerel sunucu önekini ekleyin:

NSString *fp = [[NSBundle mainBundle] pathForResource:@"picture" ofType:@"jpg" inDirectory:@"www"];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d%@", LocalFileServerPort, fp]];
NSString *str = url.absoluteString;
[self.webViewController executeJavascript:[NSString stringWithFormat:@"updateLocalImage('%@')", str]];

0
2018-03-22 04:22